diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index 2543ad72b..62a1c09ba 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -1036,7 +1036,7 @@ bool Application::activateWorkbench(const char* name) } 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) { wc.restoreCursor(); 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; #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); - const QChar *msg = qmsg.unicode(); #ifdef FC_DEBUG switch (type) { @@ -1290,26 +1289,26 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt case QtInfoMsg: #endif case QtDebugMsg: - Base::Console().Message("%s\n", msg); + Base::Console().Message("%s\n", msg.toUtf8().constData()); break; case QtWarningMsg: - Base::Console().Warning("%s\n", msg); + Base::Console().Warning("%s\n", msg.toUtf8().constData()); break; case QtCriticalMsg: - Base::Console().Error("%s\n", msg); + Base::Console().Error("%s\n", msg.toUtf8().constData()); break; case QtFatalMsg: - Base::Console().Error("%s\n", msg); + Base::Console().Error("%s\n", msg.toUtf8().constData()); abort(); // deliberately core dump } #ifdef FC_OS_WIN32 if (old_qtmsg_handler) - (*old_qtmsg_handler)(type, context, qmsg); + (*old_qtmsg_handler)(type, context, msg); #endif #else // do not stress user with Qt internals but write to log file if enabled Q_UNUSED(type); - Base::Console().Log("%s\n", msg); + Base::Console().Log("%s\n", msg.toUtf8().constData()); #endif } #else diff --git a/src/Gui/OnlineDocumentation.h b/src/Gui/OnlineDocumentation.h index b4ec736e7..3a7cf5744 100644 --- a/src/Gui/OnlineDocumentation.h +++ b/src/Gui/OnlineDocumentation.h @@ -69,7 +69,7 @@ public: void pause(); void resume(); -private slots: +private Q_SLOTS: void readClient(); void discardClient(); diff --git a/src/Gui/QSint/actionpanel/taskheader_p.cpp b/src/Gui/QSint/actionpanel/taskheader_p.cpp index 8ea426a25..42f18b9dc 100644 --- a/src/Gui/QSint/actionpanel/taskheader_p.cpp +++ b/src/Gui/QSint/actionpanel/taskheader_p.cpp @@ -195,7 +195,7 @@ void TaskHeader::leaveEvent ( QEvent * /*event*/ ) void TaskHeader::fold() { if (myExpandable) { - emit activated(); + Q_EMIT activated(); // 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. #if 0 @@ -254,7 +254,7 @@ void TaskHeader::changeIcons() void TaskHeader::mouseReleaseEvent ( QMouseEvent * event ) { if (event->button() == Qt::LeftButton) { - emit activated(); + Q_EMIT activated(); } } diff --git a/src/Gui/Quarter/ContextMenu.h b/src/Gui/Quarter/ContextMenu.h index ac44f98dd..a9965001e 100644 --- a/src/Gui/Quarter/ContextMenu.h +++ b/src/Gui/Quarter/ContextMenu.h @@ -50,7 +50,7 @@ public: QMenu * getMenu(void) const; -public slots: +public Q_SLOTS: void changeRenderMode(QAction * action); void changeStereoMode(QAction * action); void changeTransparencyType(QAction * action); diff --git a/src/Gui/Quarter/SensorManager.h b/src/Gui/Quarter/SensorManager.h index 4b3fb7d0d..6344cf93e 100644 --- a/src/Gui/Quarter/SensorManager.h +++ b/src/Gui/Quarter/SensorManager.h @@ -48,7 +48,7 @@ public: SensorManager(void); ~SensorManager(); -public slots: +public Q_SLOTS: void idleTimeout(void); void delayTimeout(void); void timerQueueTimeout(void); diff --git a/src/Gui/Quarter/SignalThread.cpp b/src/Gui/Quarter/SignalThread.cpp index 2b7254d3c..6d18f8636 100644 --- a/src/Gui/Quarter/SignalThread.cpp +++ b/src/Gui/Quarter/SignalThread.cpp @@ -70,7 +70,7 @@ SignalThread::run(void) // just wait, and trigger every time we receive a signal this->waitcond.wait(&this->mutex); if (!this->isstopped) { - emit triggerSignal(); + Q_EMIT triggerSignal(); } } } diff --git a/src/Gui/iisTaskPanel/src/iisiconlabel.cpp b/src/Gui/iisTaskPanel/src/iisiconlabel.cpp index f014cbf3c..f967eacc3 100644 --- a/src/Gui/iisTaskPanel/src/iisiconlabel.cpp +++ b/src/Gui/iisTaskPanel/src/iisiconlabel.cpp @@ -9,193 +9,193 @@ #include "iistaskpanelscheme.h" iisIconLabel::iisIconLabel(const QIcon &icon, const QString &title, QWidget *parent) - : QWidget(parent), - myPixmap(icon), - myText(title), - mySchemePointer(0), - m_over(false), - m_pressed(false), - m_changeCursorOver(true), - m_underlineOver(true) + : QWidget(parent), + myPixmap(icon), + myText(title), + mySchemePointer(0), + m_over(false), + m_pressed(false), + m_changeCursorOver(true), + m_underlineOver(true) { - setFocusPolicy(Qt::StrongFocus); - setCursor(Qt::PointingHandCursor); + setFocusPolicy(Qt::StrongFocus); + setCursor(Qt::PointingHandCursor); - myFont.setWeight(0); - myPen.setStyle(Qt::NoPen); + myFont.setWeight(0); + myPen.setStyle(Qt::NoPen); - myColor = myColorOver = myColorDisabled = QColor(); + myColor = myColorOver = myColorDisabled = QColor(); } iisIconLabel::~iisIconLabel() { - //if (m_changeCursorOver) - // QApplication::restoreOverrideCursor(); + //if (m_changeCursorOver) + // QApplication::restoreOverrideCursor(); } void iisIconLabel::setSchemePointer(iisIconLabelScheme **pointer) { - mySchemePointer = pointer; - update(); + mySchemePointer = pointer; + update(); } void iisIconLabel::setColors(const QColor &color, const QColor &colorOver, const QColor &colorOff) { - myColor = color; - myColorOver = colorOver; - myColorDisabled = colorOff; - update(); + myColor = color; + myColorOver = colorOver; + myColorDisabled = colorOff; + update(); } void iisIconLabel::setFont(const QFont &font) { - myFont = font; - update(); + myFont = font; + update(); } void iisIconLabel::setFocusPen(const QPen &pen) { - myPen = pen; - update(); + myPen = pen; + update(); } QSize iisIconLabel::sizeHint() const { - return minimumSize(); + return minimumSize(); } QSize iisIconLabel::minimumSizeHint() const { - int s = (mySchemePointer && *mySchemePointer) ? (*mySchemePointer)->iconSize : 16; - QPixmap px = myPixmap.pixmap(s,s, - isEnabled() ? QIcon::Normal : QIcon::Disabled); + int s = (mySchemePointer && *mySchemePointer) ? (*mySchemePointer)->iconSize : 16; + QPixmap px = myPixmap.pixmap(s,s, + isEnabled() ? QIcon::Normal : QIcon::Disabled); - int h = 4+px.height(); - int w = 8 + px.width(); - if (!myText.isEmpty()) { - QFontMetrics fm(myFont); - w += fm.width(myText); - h = qMax(h, 4+fm.height()); - } + int h = 4+px.height(); + int w = 8 + px.width(); + if (!myText.isEmpty()) { + QFontMetrics fm(myFont); + w += fm.width(myText); + h = qMax(h, 4+fm.height()); + } - return QSize(w+2,h+2); + return QSize(w+2,h+2); } -void iisIconLabel::paintEvent ( QPaintEvent * event ) +void iisIconLabel::paintEvent ( QPaintEvent * event ) { - Q_UNUSED(event); - QPainter p(this); + Q_UNUSED(event); + QPainter p(this); - QRect textRect(rect().adjusted(0,0,-1,0)); + QRect textRect(rect().adjusted(0,0,-1,0)); - int x = 2; + int x = 2; - if (!myPixmap.isNull()) { - int s = (mySchemePointer && *mySchemePointer) ? (*mySchemePointer)->iconSize : 16; - QPixmap px = myPixmap.pixmap(s,s, - isEnabled() ? QIcon::Normal : QIcon::Disabled); - p.drawPixmap(x,0,px); - x += px.width() + 4; - } + if (!myPixmap.isNull()) { + int s = (mySchemePointer && *mySchemePointer) ? (*mySchemePointer)->iconSize : 16; + QPixmap px = myPixmap.pixmap(s,s, + isEnabled() ? QIcon::Normal : QIcon::Disabled); + p.drawPixmap(x,0,px); + x += px.width() + 4; + } - if (!myText.isEmpty()) { - QColor text = myColor, textOver = myColorOver, textOff = myColorDisabled; - QFont fnt = myFont; - QPen focusPen = myPen; - bool underline = m_underlineOver/*, cursover = m_changeCursorOver*/; - if (mySchemePointer && *mySchemePointer) { - if (!text.isValid()) text = (*mySchemePointer)->text; - if (!textOver.isValid()) textOver = (*mySchemePointer)->textOver; - if (!textOff.isValid()) textOff = (*mySchemePointer)->textOff; - if (!fnt.weight()) fnt = (*mySchemePointer)->font; - if (focusPen.style() == Qt::NoPen) focusPen = (*mySchemePointer)->focusPen; - underline = (*mySchemePointer)->underlineOver; - //cursover = (*mySchemePointer)->cursorOver; - } + if (!myText.isEmpty()) { + QColor text = myColor, textOver = myColorOver, textOff = myColorDisabled; + QFont fnt = myFont; + QPen focusPen = myPen; + bool underline = m_underlineOver/*, cursover = m_changeCursorOver*/; + if (mySchemePointer && *mySchemePointer) { + if (!text.isValid()) text = (*mySchemePointer)->text; + if (!textOver.isValid()) textOver = (*mySchemePointer)->textOver; + if (!textOff.isValid()) textOff = (*mySchemePointer)->textOff; + if (!fnt.weight()) fnt = (*mySchemePointer)->font; + if (focusPen.style() == Qt::NoPen) focusPen = (*mySchemePointer)->focusPen; + underline = (*mySchemePointer)->underlineOver; + //cursover = (*mySchemePointer)->cursorOver; + } - p.setPen(isEnabled() ? m_over ? textOver : text : textOff); + p.setPen(isEnabled() ? m_over ? textOver : text : textOff); - if (isEnabled() && underline && m_over) - fnt.setUnderline(true); - p.setFont(fnt); + if (isEnabled() && underline && m_over) + fnt.setUnderline(true); + p.setFont(fnt); - textRect.setLeft(x); - QRect boundingRect; + textRect.setLeft(x); + QRect boundingRect; - QFontMetrics fm(fnt); + QFontMetrics fm(fnt); #if QT_VERSION >= 0x040203 - QString txt(fm.elidedText(myText, Qt::ElideRight, textRect.width())); + QString txt(fm.elidedText(myText, Qt::ElideRight, textRect.width())); #else - QString txt = myText; + QString txt = myText; #endif - p.drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, txt, &boundingRect); + p.drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, txt, &boundingRect); - if (hasFocus()) { - p.setPen(focusPen); - p.drawRect(boundingRect.adjusted(-2,-1,0,0)); - } - } + if (hasFocus()) { + p.setPen(focusPen); + p.drawRect(boundingRect.adjusted(-2,-1,0,0)); + } + } } void iisIconLabel::enterEvent ( QEvent * /*event*/ ) { - m_over = true; + m_over = true; - //if (m_changeCursorOver) - // QApplication::setOverrideCursor(Qt::PointingHandCursor); + //if (m_changeCursorOver) + // QApplication::setOverrideCursor(Qt::PointingHandCursor); - update(); + update(); } void iisIconLabel::leaveEvent ( QEvent * /*event*/ ) { - m_over = false; - update(); + m_over = false; + update(); - //if (m_changeCursorOver) - // QApplication::restoreOverrideCursor(); + //if (m_changeCursorOver) + // QApplication::restoreOverrideCursor(); } void iisIconLabel::mousePressEvent ( QMouseEvent * event ) { - if (event->button() == Qt::LeftButton) { - m_pressed = true; - emit pressed(); - } else - if (event->button() == Qt::RightButton) - emit contextMenu(); + if (event->button() == Qt::LeftButton) { + m_pressed = true; + Q_EMIT pressed(); + } else + if (event->button() == Qt::RightButton) + Q_EMIT contextMenu(); - update(); + update(); } void iisIconLabel::mouseReleaseEvent ( QMouseEvent * event ) { - if (event->button() == Qt::LeftButton) { - m_pressed = false; - emit released(); + if (event->button() == Qt::LeftButton) { + m_pressed = false; + Q_EMIT released(); - if (rect().contains( event->pos() )) { - emit clicked(); - emit activated(); - } - } + if (rect().contains( event->pos() )) { + Q_EMIT clicked(); + Q_EMIT activated(); + } + } - update(); + update(); } void iisIconLabel::keyPressEvent ( QKeyEvent * event ) { - switch (event->key()) { - case Qt::Key_Space: - case Qt::Key_Return: - emit activated(); - break; + switch (event->key()) { + case Qt::Key_Space: + case Qt::Key_Return: + Q_EMIT activated(); + break; - default:; - } + default:; + } - QWidget::keyPressEvent(event); + QWidget::keyPressEvent(event); } diff --git a/src/Gui/iisTaskPanel/src/iistaskheader.cpp b/src/Gui/iisTaskPanel/src/iistaskheader.cpp index 12e75cc20..4d1cd4243 100644 --- a/src/Gui/iisTaskPanel/src/iistaskheader.cpp +++ b/src/Gui/iisTaskPanel/src/iistaskheader.cpp @@ -16,38 +16,38 @@ #include "iisiconlabel.h" iisTaskHeader::iisTaskHeader(const QIcon &icon, const QString &title, bool expandable, QWidget *parent) - : QFrame(parent), - myExpandable(expandable), - m_over(false), - m_buttonOver(false), - m_fold(true), - m_opacity(0.1), - myButton(0) + : QFrame(parent), + myExpandable(expandable), + m_over(false), + m_buttonOver(false), + m_fold(true), + m_opacity(0.1), + myButton(0) { - myTitle = new iisIconLabel(icon, title, this); - myTitle->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + myTitle = new iisIconLabel(icon, title, this); + myTitle->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); - connect(myTitle, SIGNAL(activated()), this, SLOT(fold())); + connect(myTitle, SIGNAL(activated()), this, SLOT(fold())); - QHBoxLayout *hbl = new QHBoxLayout(); - hbl->setMargin(2); - setLayout(hbl); + QHBoxLayout *hbl = new QHBoxLayout(); + hbl->setMargin(2); + setLayout(hbl); - hbl->addWidget(myTitle); + hbl->addWidget(myTitle); - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); - setScheme(iisTaskPanelScheme::defaultScheme()); - myTitle->setSchemePointer(&myLabelScheme); + setScheme(iisTaskPanelScheme::defaultScheme()); + myTitle->setSchemePointer(&myLabelScheme); - if (myExpandable) { - myButton = new QLabel(this); - hbl->addWidget(myButton); - myButton->installEventFilter(this); - myButton->setFixedWidth(myScheme->headerButtonSize.width()); - changeIcons(); - } + if (myExpandable) { + myButton = new QLabel(this); + hbl->addWidget(myButton); + myButton->installEventFilter(this); + myButton->setFixedWidth(myScheme->headerButtonSize.width()); + changeIcons(); + } } iisTaskHeader::~iisTaskHeader() @@ -57,145 +57,145 @@ iisTaskHeader::~iisTaskHeader() bool iisTaskHeader::eventFilter(QObject *obj, QEvent *event) { - switch (event->type()) { - case QEvent::MouseButtonPress: - fold(); - return true; + switch (event->type()) { + case QEvent::MouseButtonPress: + fold(); + return true; - case QEvent::Enter: - m_buttonOver = true; - changeIcons(); - return true; + case QEvent::Enter: + m_buttonOver = true; + changeIcons(); + return true; - case QEvent::Leave: - m_buttonOver = false; - changeIcons(); - return true; + case QEvent::Leave: + m_buttonOver = false; + changeIcons(); + return true; - default:; - } + default:; + } - return QFrame::eventFilter(obj, event); + return QFrame::eventFilter(obj, event); } void iisTaskHeader::setScheme(iisTaskPanelScheme *scheme) { - if (scheme) { - myScheme = scheme; - myLabelScheme = &(scheme->headerLabelScheme); + if (scheme) { + myScheme = scheme; + myLabelScheme = &(scheme->headerLabelScheme); - if (myExpandable) { - setCursor(myLabelScheme->cursorOver ? Qt::PointingHandCursor : cursor()); - changeIcons(); - } + if (myExpandable) { + setCursor(myLabelScheme->cursorOver ? Qt::PointingHandCursor : cursor()); + changeIcons(); + } - setFixedHeight(scheme->headerSize); + setFixedHeight(scheme->headerSize); - update(); - } + update(); + } } -void iisTaskHeader::paintEvent ( QPaintEvent * event ) +void iisTaskHeader::paintEvent ( QPaintEvent * event ) { - Q_UNUSED(event); - QPainter p(this); + Q_UNUSED(event); + QPainter p(this); #if QT_VERSION >= 0x040203 - if (myScheme->headerAnimation) - p.setOpacity(m_opacity+0.7); + if (myScheme->headerAnimation) + p.setOpacity(m_opacity+0.7); #endif - p.setPen(myScheme->headerBorder); - p.setBrush(myScheme->headerBackground); - if (myScheme->headerBorder.style() == Qt::NoPen) - p.drawRect(rect()); - else - p.drawRect(rect().adjusted(0,0,-1,-1)); + p.setPen(myScheme->headerBorder); + p.setBrush(myScheme->headerBackground); + if (myScheme->headerBorder.style() == Qt::NoPen) + p.drawRect(rect()); + else + p.drawRect(rect().adjusted(0,0,-1,-1)); } void iisTaskHeader::animate() { - if (!myScheme->headerAnimation) - return; + if (!myScheme->headerAnimation) + return; - if (!isEnabled()) { - m_opacity = 0.1; - update(); - return; - } + if (!isEnabled()) { + m_opacity = 0.1; + update(); + return; + } - if (m_over) { - if (m_opacity >= 0.3) { - m_opacity = 0.3; - return; - } - m_opacity += 0.05; - } else { - if (m_opacity <= 0.1) { - m_opacity = 0.1; - return; - } - m_opacity = qMax(0.1, m_opacity-0.05); - } + if (m_over) { + if (m_opacity >= 0.3) { + m_opacity = 0.3; + return; + } + m_opacity += 0.05; + } else { + if (m_opacity <= 0.1) { + m_opacity = 0.1; + return; + } + m_opacity = qMax(0.1, m_opacity-0.05); + } - QTimer::singleShot(100, this, SLOT(animate())); - update(); + QTimer::singleShot(100, this, SLOT(animate())); + update(); } void iisTaskHeader::enterEvent ( QEvent * /*event*/ ) { - m_over = true; + m_over = true; - if (isEnabled()) - QTimer::singleShot(100, this, SLOT(animate())); + if (isEnabled()) + QTimer::singleShot(100, this, SLOT(animate())); - update(); + update(); } void iisTaskHeader::leaveEvent ( QEvent * /*event*/ ) { - m_over = false; - - if (isEnabled()) - QTimer::singleShot(100, this, SLOT(animate())); + m_over = false; - update(); + if (isEnabled()) + QTimer::singleShot(100, this, SLOT(animate())); + + update(); } void iisTaskHeader::fold() { - if (myExpandable) { - emit activated(); + if (myExpandable) { + Q_EMIT activated(); - m_fold = !m_fold; - changeIcons(); - } + m_fold = !m_fold; + changeIcons(); + } } void iisTaskHeader::changeIcons() { - if (!myButton) - return; + if (!myButton) + return; - if (m_buttonOver) - { - if (m_fold) - myButton->setPixmap(myScheme->headerButtonFoldOver.pixmap(myScheme->headerButtonSize)); - else - myButton->setPixmap(myScheme->headerButtonUnfoldOver.pixmap(myScheme->headerButtonSize)); - } else - { - if (m_fold) - myButton->setPixmap(myScheme->headerButtonFold.pixmap(myScheme->headerButtonSize)); - else - myButton->setPixmap(myScheme->headerButtonUnfold.pixmap(myScheme->headerButtonSize)); - } + if (m_buttonOver) + { + if (m_fold) + myButton->setPixmap(myScheme->headerButtonFoldOver.pixmap(myScheme->headerButtonSize)); + else + myButton->setPixmap(myScheme->headerButtonUnfoldOver.pixmap(myScheme->headerButtonSize)); + } else + { + if (m_fold) + myButton->setPixmap(myScheme->headerButtonFold.pixmap(myScheme->headerButtonSize)); + else + myButton->setPixmap(myScheme->headerButtonUnfold.pixmap(myScheme->headerButtonSize)); + } } void iisTaskHeader::mouseReleaseEvent ( QMouseEvent * event ) { - if (event->button() == Qt::LeftButton) { - emit activated(); - } + if (event->button() == Qt::LeftButton) { + Q_EMIT activated(); + } } diff --git a/src/Mod/Draft/Resources/ui/preferences-draft.ui b/src/Mod/Draft/Resources/ui/preferences-draft.ui index 5eda20018..25a3af1df 100755 --- a/src/Mod/Draft/Resources/ui/preferences-draft.ui +++ b/src/Mod/Draft/Resources/ui/preferences-draft.ui @@ -222,46 +222,6 @@ - - - - - - Dimensions precision level - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - 8 - - - 2 - - - dimPrecision - - - Mod/Draft - - - - - diff --git a/src/Mod/Draft/Resources/ui/preferences-drafttexts.ui b/src/Mod/Draft/Resources/ui/preferences-drafttexts.ui index 4c8629c7e..213e2cc6a 100644 --- a/src/Mod/Draft/Resources/ui/preferences-drafttexts.ui +++ b/src/Mod/Draft/Resources/ui/preferences-drafttexts.ui @@ -7,7 +7,7 @@ 0 0 522 - 462 + 473 @@ -153,6 +153,46 @@ such as "Arial:Bold" + + + + + + Number of decimals + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 8 + + + 2 + + + dimPrecision + + + Mod/Draft + + + + + @@ -364,7 +404,7 @@ such as "Arial:Bold" - + 300 @@ -405,6 +445,21 @@ such as "Arial:Bold" qPixmapFromMimeSource + + Gui::FileChooser + QWidget +
Gui/FileDialog.h
+
+ + Gui::PrefFileChooser + Gui::FileChooser +
Gui/PrefWidgets.h
+
+ + Gui::PrefSpinBox + QSpinBox +
Gui/PrefWidgets.h
+
Gui::PrefCheckBox QCheckBox @@ -425,16 +480,6 @@ such as "Arial:Bold" QDoubleSpinBox
Gui/PrefWidgets.h
- - Gui::FileChooser - QWidget -
Gui/FileDialog.h
-
- - Gui::PrefFileChooser - Gui::FileChooser -
Gui/PrefWidgets.h
-
diff --git a/src/Mod/Draft/importDXF.py b/src/Mod/Draft/importDXF.py index 9d2b864c1..9b5a32ff7 100644 --- a/src/Mod/Draft/importDXF.py +++ b/src/Mod/Draft/importDXF.py @@ -776,6 +776,8 @@ non-parametric curve""" warn('polygon fallback on %s' %spline) return drawSplineIterpolation(controlpoints,closed=closed,\ forceShape=forceShape,alwaysDiscretize=True) + if fitpoints and not(controlpoints): + return drawSplineIterpolation(fitpoints,closed=closed,forceShape=forceShape) try: bspline=Part.BSplineCurve() bspline.buildFromPolesMultsKnots(poles=controlpoints,mults=multvector,\ diff --git a/src/Mod/Fem/App/CMakeLists.txt b/src/Mod/Fem/App/CMakeLists.txt index 9502efc15..551d956c2 100755 --- a/src/Mod/Fem/App/CMakeLists.txt +++ b/src/Mod/Fem/App/CMakeLists.txt @@ -71,6 +71,7 @@ SET(FemScripts_SRCS _CommandMaterial.py _CommandMeshGmshFromShape.py _CommandMeshNetgenFromShape.py + _CommandMeshGroup.py _CommandMeshRegion.py _CommandPrintMeshInfo.py _CommandPurgeResults.py @@ -83,6 +84,7 @@ SET(FemScripts_SRCS _FemConstraintSelfWeight.py _FemMaterialMechanicalNonlinear.py _FemMeshGmsh.py + _FemMeshGroup.py _FemMeshRegion.py _FemShellThickness.py _FemSolverCalculix.py @@ -90,6 +92,7 @@ SET(FemScripts_SRCS _FemMaterial.py _TaskPanelFemBeamSection.py _TaskPanelFemMeshGmsh.py + _TaskPanelFemMeshGroup.py _TaskPanelFemMeshRegion.py _TaskPanelFemShellThickness.py _TaskPanelFemSolverCalculix.py @@ -99,6 +102,7 @@ SET(FemScripts_SRCS _ViewProviderFemConstraintSelfWeight.py _ViewProviderFemMaterialMechanicalNonlinear.py _ViewProviderFemMeshGmsh.py + _ViewProviderFemMeshGroup.py _ViewProviderFemMeshRegion.py _ViewProviderFemShellThickness.py _ViewProviderFemSolverCalculix.py @@ -122,6 +126,7 @@ SET(FemScripts_SRCS FemMaterialMechanicalNonlinear.py FemMesh2Mesh.py FemMeshGmsh.py + FemMeshGroup.py FemMeshRegion.py FemMeshTools.py FemShellThickness.py @@ -136,6 +141,7 @@ SET(FemScripts_SRCS z88DispReader.py TaskPanelFemBeamSection.ui TaskPanelFemMeshGmsh.ui + TaskPanelFemMeshGroup.ui TaskPanelFemMeshRegion.ui TaskPanelFemShellThickness.ui TaskPanelFemSolverCalculix.ui diff --git a/src/Mod/Fem/App/FemResultObject.cpp b/src/Mod/Fem/App/FemResultObject.cpp index 20915c414..5a793f786 100644 --- a/src/Mod/Fem/App/FemResultObject.cpp +++ b/src/Mod/Fem/App/FemResultObject.cpp @@ -42,6 +42,8 @@ FemResultObject::FemResultObject() 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(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(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"); @@ -59,6 +61,8 @@ FemResultObject::FemResultObject() Stats.setStatus(App::Property::ReadOnly, true); DisplacementVectors.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); PrincipalMax.setStatus(App::Property::ReadOnly, true); PrincipalMed.setStatus(App::Property::ReadOnly, true); diff --git a/src/Mod/Fem/App/FemResultObject.h b/src/Mod/Fem/App/FemResultObject.h index a6202024f..1d765385e 100644 --- a/src/Mod/Fem/App/FemResultObject.h +++ b/src/Mod/Fem/App/FemResultObject.h @@ -49,8 +49,12 @@ public: App::PropertyFloatList Stats; /// Displacement vectors of analysis App::PropertyVectorList DisplacementVectors; - /// Lengths of displacement vestors of analysis + /// Lengths of displacement vectors of analysis App::PropertyFloatList DisplacementLengths; + /// Stress vectors of analysis + App::PropertyVectorList StressVectors; + /// Strain vectors of analysis + App::PropertyVectorList StrainVectors; /// Von Mises Stress values of analysis App::PropertyFloatList StressValues; /// First principal Stress values of analysis diff --git a/src/Mod/Fem/App/FemVTKTools.cpp b/src/Mod/Fem/App/FemVTKTools.cpp index c767beb8c..c5f8d337b 100644 --- a/src/Mod/Fem/App/FemVTKTools.cpp +++ b/src/Mod/Fem/App/FemVTKTools.cpp @@ -108,7 +108,7 @@ template void writeVTKFile(const char* filename, vtkSmartPointer< writer->SetInputData(dataset); writer->Write(); } - + void FemVTKTools::importVTKMesh(vtkSmartPointer dataset, FemMesh* mesh) { const vtkIdType nPoints = dataset->GetNumberOfPoints(); @@ -184,7 +184,7 @@ FemMesh* FemVTKTools::readVTKMesh(const char* filename, FemMesh* mesh) Base::TimeInfo Start; Base::Console().Log("Start: read FemMesh from VTK unstructuredGrid ======================\n"); Base::FileInfo f(filename); - + if(f.hasExtension("vtu")) { vtkSmartPointer dataset = readVTKFile(filename); @@ -236,7 +236,7 @@ void exportFemMeshFaces(vtkSmartPointer grid, const SMDS_Fa quad->GetPointIds()->SetId(1, aFace->GetNode(1)->GetID()-1); quad->GetPointIds()->SetId(2, aFace->GetNode(2)->GetID()-1); quad->GetPointIds()->SetId(3, aFace->GetNode(3)->GetID()-1); - + quadArray->InsertNextCell(quad); } //quadratic triangle @@ -263,7 +263,7 @@ void exportFemMeshFaces(vtkSmartPointer grid, const SMDS_Fa quad->GetPointIds()->SetId(5, aFace->GetNode(5)->GetID()-1); quad->GetPointIds()->SetId(6, aFace->GetNode(6)->GetID()-1); quad->GetPointIds()->SetId(7, aFace->GetNode(7)->GetID()-1); - + quadQuadArray->InsertNextCell(quad); } } @@ -275,7 +275,7 @@ void exportFemMeshFaces(vtkSmartPointer grid, const SMDS_Fa if(quadTriangleArray->GetNumberOfCells()>0) grid->SetCells(VTK_QUADRATIC_TRIANGLE, quadTriangleArray); - + if(quadQuadArray->GetNumberOfCells()>0) grid->SetCells(VTK_QUADRATIC_QUAD, quadQuadArray); @@ -291,7 +291,7 @@ void exportFemMeshCells(vtkSmartPointer grid, const SMDS_Vo // quadratic elemnts with 13 and 15 nodes are not added yet vtkSmartPointer quadTetraArray = vtkSmartPointer::New(); vtkSmartPointer quadHexaArray = vtkSmartPointer::New(); - + for (;aVolIter->more();) { const SMDS_MeshVolume* aVol = aVolIter->next(); @@ -314,7 +314,7 @@ void exportFemMeshCells(vtkSmartPointer grid, const SMDS_Vo cell->GetPointIds()->SetId(2, aVol->GetNode(2)->GetID()-1); cell->GetPointIds()->SetId(3, aVol->GetNode(3)->GetID()-1); cell->GetPointIds()->SetId(4, aVol->GetNode(4)->GetID()-1); - + pyramidArray->InsertNextCell(cell); } if(aVol->NbNodes() == 6) { @@ -325,7 +325,7 @@ void exportFemMeshCells(vtkSmartPointer grid, const SMDS_Vo cell->GetPointIds()->SetId(3, aVol->GetNode(3)->GetID()-1); cell->GetPointIds()->SetId(4, aVol->GetNode(4)->GetID()-1); cell->GetPointIds()->SetId(5, aVol->GetNode(5)->GetID()-1); - + wedgeArray->InsertNextCell(cell); } if(aVol->NbNodes() == 8) { @@ -338,7 +338,7 @@ void exportFemMeshCells(vtkSmartPointer grid, const SMDS_Vo cell->GetPointIds()->SetId(5, aVol->GetNode(5)->GetID()-1); cell->GetPointIds()->SetId(6, aVol->GetNode(6)->GetID()-1); cell->GetPointIds()->SetId(7, aVol->GetNode(7)->GetID()-1); - + hexaArray->InsertNextCell(cell); } //quadratic tetrahedra @@ -371,10 +371,10 @@ void exportFemMeshCells(vtkSmartPointer grid, const SMDS_Vo if(hexaArray->GetNumberOfCells()>0) grid->SetCells(VTK_HEXAHEDRON, hexaArray); - + if(quadTetraArray->GetNumberOfCells()>0) grid->SetCells(VTK_QUADRATIC_TETRA, quadTetraArray); - + if(quadHexaArray->GetNumberOfCells()>0) grid->SetCells(VTK_QUADRATIC_HEXAHEDRON, quadHexaArray); @@ -382,11 +382,11 @@ void exportFemMeshCells(vtkSmartPointer grid, const SMDS_Vo void FemVTKTools::exportVTKMesh(const FemMesh* mesh, vtkSmartPointer grid) { - + SMESH_Mesh* smesh = const_cast(mesh->getSMesh()); SMESHDS_Mesh* meshDS = smesh->GetMeshDS(); const SMDS_MeshInfo& info = meshDS->GetMeshInfo(); - + //start with the nodes vtkSmartPointer points = vtkSmartPointer::New(); SMDS_NodeIteratorPtr aNodeIter = meshDS->nodesIterator(); @@ -409,11 +409,11 @@ void FemVTKTools::exportVTKMesh(const FemMesh* mesh, vtkSmartPointer grid = vtkSmartPointer::New(); exportVTKMesh(mesh, grid); //vtkSmartPointer dataset = vtkDataSet::SafeDownCast(grid); @@ -426,7 +426,7 @@ void FemVTKTools::writeVTKMesh(const char* filename, const FemMesh* mesh) else{ Base::Console().Error("file name extension is not supported to write VTK\n"); } - + Base::Console().Log(" %f: Done \n",Base::TimeInfo::diffTimeF(Start, Base::TimeInfo())); } @@ -487,7 +487,7 @@ App::DocumentObject* FemVTKTools::readFluidicResult(const char* filename, App::D Base::TimeInfo Start; Base::Console().Log("Start: read FemResult with FemMesh from VTK file ======================\n"); Base::FileInfo f(filename); - + vtkSmartPointer ds; if(f.hasExtension("vtu")) { @@ -501,7 +501,7 @@ App::DocumentObject* FemVTKTools::readFluidicResult(const char* filename, App::D { Base::Console().Error("file name extension is not supported\n"); } - + App::Document* pcDoc = App::GetApplication().getActiveDocument(); if(!pcDoc) { @@ -532,12 +532,12 @@ App::DocumentObject* FemVTKTools::readFluidicResult(const char* filename, App::D static_cast(mesh->getPropertyByName("FemMesh"))->setValue(*fmesh); static_cast(result->getPropertyByName("Mesh"))->setValue(mesh); // PropertyLink is the property type to store DocumentObject pointer - + importFluidicResult(dataset, result); pcDoc->recompute(); - + Base::Console().Log(" %f: Done \n", Base::TimeInfo::diffTimeF(Start, Base::TimeInfo())); - + return result; } @@ -561,12 +561,12 @@ void FemVTKTools::writeResult(const char* filename, const App::DocumentObject* r Base::TimeInfo Start; Base::Console().Log("Start: write FemResult or CfdResult to VTK unstructuredGrid dataset =======\n"); Base::FileInfo f(filename); - + vtkSmartPointer grid = vtkSmartPointer::New(); App::DocumentObject* mesh = static_cast(res->getPropertyByName("Mesh"))->getValue(); const FemMesh& fmesh = static_cast(mesh->getPropertyByName("FemMesh"))->getValue(); FemVTKTools::exportVTKMesh(&fmesh, grid); - + if(res->getPropertyByName("Velocity")){ FemVTKTools::exportFluidicResult(res, grid); } @@ -576,7 +576,7 @@ void FemVTKTools::writeResult(const char* filename, const App::DocumentObject* r else{ return; } - + //vtkSmartPointer dataset = vtkDataSet::SafeDownCast(grid); if(f.hasExtension("vtu")){ writeVTKFile(filename, grid); @@ -587,7 +587,7 @@ void FemVTKTools::writeResult(const char* filename, const App::DocumentObject* r else{ Base::Console().Error("file name extension is not supported to write VTK\n"); } - + Base::Console().Log(" %f: Done \n",Base::TimeInfo::diffTimeF(Start, Base::TimeInfo())); } @@ -604,7 +604,7 @@ void FemVTKTools::importFluidicResult(vtkSmartPointer dataset, App:: vars["TurbulenceEnergy"] = "k"; vars["TurbulenceDissipationRate"] = "epsilon"; vars["TurbulenceSpecificDissipation"] = "omega"; - + const int max_var_index = 11; std::vector stats(3*max_var_index, 0.0); @@ -620,10 +620,10 @@ void FemVTKTools::importFluidicResult(vtkSmartPointer dataset, App:: varids["TurbulenceDissipationRate"] = 8; //varids["TurbulenceThermalDiffusivity"] = 9; //varids["TurbulenceSpecificDissipation"] = 10; - + double ts = 0.0; // t=0.0 for static simulation static_cast(res->getPropertyByName("Time"))->setValue(ts); - + vtkSmartPointer pd = dataset->GetPointData(); const vtkIdType nPoints = dataset->GetNumberOfPoints(); if(pd->GetNumberOfArrays() == 0) { @@ -631,7 +631,7 @@ void FemVTKTools::importFluidicResult(vtkSmartPointer dataset, App:: // if pointData is empty, data may be in cellDate, cellData -> pointData interpolation is possible in VTK return; } - + std::vector nodeIds(nPoints); vtkSmartPointer vel = pd->GetArray(vars["Velocity"]); if(nPoints && vel && vel->GetNumberOfComponents() == 3) { @@ -692,7 +692,7 @@ void FemVTKTools::importFluidicResult(vtkSmartPointer dataset, App:: stats[index*3] = vmin; stats[index*3 + 2] = vmax; stats[index*3 + 1] = vmean/nPoints; - + Base::Console().Message("field \"%s\" has been loaded \n", kv.first); } } @@ -760,6 +760,7 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar const FemResultObject* res = static_cast(obj); if(!res->StressValues.getValues().empty()) { const std::vector& vec = res->StressValues.getValues(); + if (vec.size()>1) { vtkSmartPointer data = vtkSmartPointer::New(); data->SetNumberOfValues(vec.size()); data->SetName("Von Mises stress"); @@ -768,10 +769,11 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar data->SetValue(i, vec[i]); grid->GetPointData()->AddArray(data); - } + }} - if(!res->StressValues.getValues().empty()) { + if(!res->MaxShear.getValues().empty()) { const std::vector& vec = res->MaxShear.getValues(); + if (vec.size()>1) { vtkSmartPointer data = vtkSmartPointer::New(); data->SetNumberOfValues(vec.size()); data->SetName("Max shear stress (Tresca)"); @@ -780,10 +782,11 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar data->SetValue(i, vec[i]); grid->GetPointData()->AddArray(data); - } + }} - if(!res->StressValues.getValues().empty()) { + if(!res->PrincipalMax.getValues().empty()) { const std::vector& vec = res->PrincipalMax.getValues(); + if (vec.size()>1) { vtkSmartPointer data = vtkSmartPointer::New(); data->SetNumberOfValues(vec.size()); data->SetName("Maximum Principal stress"); @@ -792,10 +795,11 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar data->SetValue(i, vec[i]); grid->GetPointData()->AddArray(data); - } + }} - if(!res->StressValues.getValues().empty()) { + if(!res->PrincipalMax.getValues().empty()) { const std::vector& vec = res->PrincipalMin.getValues(); + if (vec.size()>1) { vtkSmartPointer data = vtkSmartPointer::New(); data->SetNumberOfValues(vec.size()); data->SetName("Minimum Principal stress"); @@ -804,10 +808,11 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar data->SetValue(i, vec[i]); grid->GetPointData()->AddArray(data); - } + }} - if(!res->StressValues.getValues().empty()) { + if (!res->Temperature.getValues().empty()) { const std::vector& vec = res->Temperature.getValues(); + if (vec.size()>1) { vtkSmartPointer data = vtkSmartPointer::New(); data->SetNumberOfValues(vec.size()); data->SetName("Temperature"); @@ -816,10 +821,11 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar data->SetValue(i, vec[i]); grid->GetPointData()->AddArray(data); - } + }} - if(!res->StressValues.getValues().empty()) { + if (!res->UserDefined.getValues().empty()) { const std::vector& vec = res->UserDefined.getValues(); + if (vec.size()>1) { vtkSmartPointer data = vtkSmartPointer::New(); data->SetNumberOfValues(vec.size()); data->SetName("User Defined Results"); @@ -828,11 +834,12 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar data->SetValue(i, vec[i]); grid->GetPointData()->AddArray(data); - } + }} - if(!res->StressValues.getValues().empty()) { + if(!res->DisplacementVectors.getValues().empty()) { const std::vector& vec = res->DisplacementVectors.getValues(); + if (vec.size()>1) { vtkSmartPointer data = vtkSmartPointer::New(); data->SetNumberOfComponents(3); data->SetName("Displacement"); @@ -843,7 +850,38 @@ void FemVTKTools::exportMechanicalResult(const App::DocumentObject* obj, vtkSmar } grid->GetPointData()->AddArray(data); - } + }} + + if(!res->StressVectors.getValues().empty()) { + const std::vector& vec = res->StressVectors.getValues(); + if (vec.size()>1) { + vtkSmartPointer data = vtkSmartPointer::New(); + data->SetNumberOfComponents(3); + data->SetName("Stress Vectors"); + + for(std::vector::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& vec = res->StrainVectors.getValues(); + if (vec.size()>1) { + vtkSmartPointer data = vtkSmartPointer::New(); + data->SetNumberOfComponents(3); + data->SetName("Strain Vectors"); + + for(std::vector::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 diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index 6a318ae8c..308e3d580 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -55,6 +55,13 @@ INSTALL( FemMesh2Mesh.py _CommandFEMMesh2Mesh.py + FemMeshGroup.py + _FemMeshGroup.py + _ViewProviderFemMeshGroup.py + _CommandMeshGroup.py + _TaskPanelFemMeshGroup.py + TaskPanelFemMeshGroup.ui + FemMeshRegion.py _FemMeshRegion.py _ViewProviderFemMeshRegion.py diff --git a/src/Mod/Fem/FemGmshTools.py b/src/Mod/Fem/FemGmshTools.py index bd9258dc7..f214c03a6 100644 --- a/src/Mod/Fem/FemGmshTools.py +++ b/src/Mod/Fem/FemGmshTools.py @@ -215,17 +215,39 @@ class FemGmshTools(): print(' ' + self.gmsh_bin) 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: print(' Group meshing.') - self.group_elements = FemMeshTools.get_analysis_group_elements(self.analysis, self.part_obj) - print(' {}'.format(self.group_elements)) + new_group_elements = FemMeshTools.get_analysis_group_elements(self.analysis, 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") else: - print(' NO group meshing.') + print(' No anlysis members for group meshing.') + print(' {}'.format(self.group_elements)) + # mesh regions self.ele_length_map = {} # { 'ElementString' : element length } self.ele_node_map = {} # { 'ElementString' : [element nodes] } if not self.mesh_obj.MeshRegionList: - print (' No Mesh regions.') + print (' No mesh regions.') else: print (' Mesh regions, we need to get the elements.') if self.part_obj.Shape.ShapeType == 'Compound': @@ -279,7 +301,7 @@ class FemGmshTools(): geo = open(self.temp_file_geo, "w") geo.write('Merge "' + self.temp_file_geometry + '";\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.') geo.write("// group data\n") # we use the element name of FreeCAD which starts with 1 (example: 'Face1'), same as GMSH diff --git a/src/Mod/Fem/FemMeshGroup.py b/src/Mod/Fem/FemMeshGroup.py new file mode 100644 index 000000000..d937ca236 --- /dev/null +++ b/src/Mod/Fem/FemMeshGroup.py @@ -0,0 +1,49 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2016 - Bernd Hahnebach * +# * * +# * 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 + +# @} diff --git a/src/Mod/Fem/FemMeshTools.py b/src/Mod/Fem/FemMeshTools.py index 3cc1b6c4c..e35940d48 100644 --- a/src/Mod/Fem/FemMeshTools.py +++ b/src/Mod/Fem/FemMeshTools.py @@ -995,54 +995,43 @@ def get_ref_shape_node_sum_geom_table(node_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): ''' 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, ...], ...} ''' - aShape = aPart.Shape group_elements = {} # { name : [element, element, ... , element]} empty_references = [] for m in aAnalysis.Member: if hasattr(m, "References"): - # print(m.Name) - key = m.Name - elements = [] - stype = None if m.References: - for r in m.References: - parent = r[0] - childs = r[1] - # print(parent) - # print(childs) - for child in childs: - ref_shape = get_element(parent, child) # the method getElement(element) does not return Solid elements - if not stype: - stype = ref_shape.ShapeType - elif stype != ref_shape.ShapeType: - FreeCAD.Console.PrintError('Error, two refschapes in References with different ShapeTypes.\n') - # print(ref_shape) - found_element = find_element_in_shape(aShape, ref_shape) - if found_element is not None: - elements.append(found_element) - else: - FreeCAD.Console.PrintError('Problem: No element found for: ' + str(ref_shape) + '\n') - print(' ' + m.Name) - print(' ' + str(m.References)) - print(' ' + r[0].Name) - group_elements[key] = sorted(elements) + 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, aShape) + 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, aShape) + 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]) @@ -1051,6 +1040,37 @@ def get_analysis_group_elements(aAnalysis, aPart): 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 = [] + stype = None + for r in obj.References: + parent = r[0] + childs = r[1] + # print(parent) + # print(childs) + for child in childs: + ref_shape = get_element(parent, child) # the method getElement(element) does not return Solid elements + if not stype: + stype = ref_shape.ShapeType + elif stype != ref_shape.ShapeType: + FreeCAD.Console.PrintError('Error, two refschapes in References with different ShapeTypes.\n') + # print(ref_shape) + found_element = find_element_in_shape(aShape, ref_shape) + if found_element is not None: + elements.append(found_element) + else: + FreeCAD.Console.PrintError('Problem: No element found for: ' + str(ref_shape) + '\n') + print(' ' + obj.Name) + print(' ' + str(obj.References)) + print(' ' + r[0].Name) + return (key, sorted(elements)) + + def get_anlysis_empty_references_group_elements(group_elements, aAnalysis, aShape): '''get the elementIDs if the Reference shape is empty see get_analysis_group_elements() for more informatations diff --git a/src/Mod/Fem/FemTools.py b/src/Mod/Fem/FemTools.py index 3d11fb989..93ed055f2 100644 --- a/src/Mod/Fem/FemTools.py +++ b/src/Mod/Fem/FemTools.py @@ -378,9 +378,7 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): if self.analysis_type == "static": if not (self.fixed_constraints or self.displacement_constraints): message += "Static analysis: Neither constraint fixed nor constraint displacement defined.\n" - if self.analysis_type == "static": - 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" + # 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 self.analysis_type == "thermomech": if not self.initialtemperature_constraints: message += "Thermomechanical analysis: No initial temperature defined.\n" diff --git a/src/Mod/Fem/Gui/Workbench.cpp b/src/Mod/Fem/Gui/Workbench.cpp index 63b25d258..7f586c73b 100755 --- a/src/Mod/Fem/Gui/Workbench.cpp +++ b/src/Mod/Fem/Gui/Workbench.cpp @@ -71,6 +71,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const << "Fem_MeshNetgenFromShape" << "Fem_MeshGmshFromShape" << "Fem_MeshRegion" + << "Fem_MeshGroup" //<< "Fem_CreateNodesSet" << "Separator" << "Fem_Material" @@ -143,6 +144,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "Fem_MeshNetgenFromShape" << "Fem_MeshGmshFromShape" << "Fem_MeshRegion" + << "Fem_MeshGroup" << "Fem_CreateNodesSet" << "Separator" << "Fem_Material" diff --git a/src/Mod/Fem/InitGui.py b/src/Mod/Fem/InitGui.py index dbe0ce629..c9832faa7 100644 --- a/src/Mod/Fem/InitGui.py +++ b/src/Mod/Fem/InitGui.py @@ -54,6 +54,7 @@ class FemWorkbench (Workbench): import _CommandFEMMesh2Mesh import _CommandMeshGmshFromShape import _CommandMeshNetgenFromShape + import _CommandMeshGroup import _CommandMeshRegion import _CommandAnalysis import _CommandShellThickness diff --git a/src/Mod/Fem/TaskPanelFemMeshGroup.ui b/src/Mod/Fem/TaskPanelFemMeshGroup.ui new file mode 100644 index 000000000..6a7cf9bb9 --- /dev/null +++ b/src/Mod/Fem/TaskPanelFemMeshGroup.ui @@ -0,0 +1,120 @@ + + + Form + + + + 0 + 0 + 350 + 500 + + + + Mesh group + + + + + + + 16777215 + 1677215 + + + + Identifier used for mesh export + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Name + + + true + + + + + + + Label + + + + + + + + + + + + References + + + + + + Add reference + + + + + + + + + + + + Solid + + + + + + + Face, Edge, Vertex + + + true + + + + + + + <html><head/><body><p>Selection</p></body></html> + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/Mod/Fem/TaskPanelShowResult.ui b/src/Mod/Fem/TaskPanelShowResult.ui index af2b547d0..4b74bd91e 100644 --- a/src/Mod/Fem/TaskPanelShowResult.ui +++ b/src/Mod/Fem/TaskPanelShowResult.ui @@ -270,8 +270,38 @@
+ + + 0 + 17 + + + + + 16777215 + 16777215 + + + + 1 + - Available: Disp(x,y,z) Principal stresses (P1,P2,P3) + Available: Disp(x,y,z) Principal stresses(P1,P2,P3) Stress(sx,sy,sz) Strain (ex,ey,ez) + + + false + + + Qt::AlignCenter + + + true + + + -1 + + + Qt::NoTextInteraction diff --git a/src/Mod/Fem/_CommandMeshGroup.py b/src/Mod/Fem/_CommandMeshGroup.py new file mode 100644 index 000000000..242718ea7 --- /dev/null +++ b/src/Mod/Fem/_CommandMeshGroup.py @@ -0,0 +1,57 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2016 - Bernd Hahnebach * +# * * +# * 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()) diff --git a/src/Mod/Fem/_FemMeshGmsh.py b/src/Mod/Fem/_FemMeshGmsh.py index ebbffe79c..0cf2ebbca 100644 --- a/src/Mod/Fem/_FemMeshGmsh.py +++ b/src/Mod/Fem/_FemMeshGmsh.py @@ -46,6 +46,9 @@ class _FemMeshGmsh(): obj.addProperty("App::PropertyLinkList", "MeshRegionList", "Base", "Mesh regions of the mesh") 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.Part = None diff --git a/src/Mod/Fem/_FemMeshGroup.py b/src/Mod/Fem/_FemMeshGroup.py new file mode 100644 index 000000000..84380dac6 --- /dev/null +++ b/src/Mod/Fem/_FemMeshGroup.py @@ -0,0 +1,40 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2016 - Bernd Hahnebach * +# * * +# * 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 diff --git a/src/Mod/Fem/_TaskPanelFemMeshGroup.py b/src/Mod/Fem/_TaskPanelFemMeshGroup.py new file mode 100644 index 000000000..018d882a3 --- /dev/null +++ b/src/Mod/Fem/_TaskPanelFemMeshGroup.py @@ -0,0 +1,190 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2016 - Bernd Hahnebach * +# * * +# * 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) diff --git a/src/Mod/Fem/_TaskPanelShowResult.py b/src/Mod/Fem/_TaskPanelShowResult.py index 5c713eb97..8392df24d 100644 --- a/src/Mod/Fem/_TaskPanelShowResult.py +++ b/src/Mod/Fem/_TaskPanelShowResult.py @@ -21,7 +21,7 @@ # *************************************************************************** __title__ = "Result Control Task Panel" -__author__ = "Juergen Riegel" +__author__ = "Juergen Riegel, Michael Hindley" __url__ = "http://www.freecadweb.org" ## @package TaskPanelShowResult @@ -231,7 +231,15 @@ class _TaskPanelShowResult: x = np.array(dispvectors[:, 0]) y = np.array(dispvectors[:, 1]) 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 UserDefinedFormula = eval(userdefined_eq).tolist() self.result_object.UserDefined = UserDefinedFormula diff --git a/src/Mod/Fem/_ViewProviderFemMeshGmsh.py b/src/Mod/Fem/_ViewProviderFemMeshGmsh.py index 02f3a7880..7948933a8 100644 --- a/src/Mod/Fem/_ViewProviderFemMeshGmsh.py +++ b/src/Mod/Fem/_ViewProviderFemMeshGmsh.py @@ -112,7 +112,7 @@ class _ViewProviderFemMeshGmsh: return None def claimChildren(self): - return self.Object.MeshRegionList + return (self.Object.MeshRegionList + self.Object.MeshGroupList) def onDelete(self, feature, subelements): try: diff --git a/src/Mod/Fem/_ViewProviderFemMeshGroup.py b/src/Mod/Fem/_ViewProviderFemMeshGroup.py new file mode 100644 index 000000000..c03377f0b --- /dev/null +++ b/src/Mod/Fem/_ViewProviderFemMeshGroup.py @@ -0,0 +1,89 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2016 - Bernd Hahnebach * +# * * +# * 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 diff --git a/src/Mod/Fem/ccxFrdReader.py b/src/Mod/Fem/ccxFrdReader.py index d312ebc81..060baeabb 100644 --- a/src/Mod/Fem/ccxFrdReader.py +++ b/src/Mod/Fem/ccxFrdReader.py @@ -54,15 +54,19 @@ def readResult(frd_input): elements_quad4 = {} elements_quad8 = {} elements_seg2 = {} + elements_seg3 = {} results = [] mode_results = {} mode_disp = {} mode_stress = {} + mode_stressv = {} + mode_strain = {} mode_temp = {} mode_disp_found = False nodes_found = False mode_stress_found = False + mode_strain_found = False mode_temp_found = False mode_time_found = False elements_found = False @@ -250,6 +254,14 @@ def readResult(frd_input): nd1 = int(line[3:13]) nd2 = int(line[13:23]) 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 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) if line[5:11] == "STRESS": 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"): elem = int(line[4:13]) stress_1 = float(line[13:25]) @@ -276,6 +288,19 @@ def readResult(frd_input): stress_5 = float(line[61:73]) stress_6 = float(line[73:85]) 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 if line[4:10] == "1PSTEP": mode_time_found = True @@ -309,6 +334,8 @@ def readResult(frd_input): mode_results['number'] = eigenmode mode_results['disp'] = mode_disp mode_results['stress'] = mode_stress + mode_results['stressv'] = mode_stressv + mode_results['strainv'] = mode_strain mode_results['temp'] = mode_temp mode_results['time'] = timestep results.append(mode_results) @@ -322,6 +349,8 @@ def readResult(frd_input): mode_results['number'] = eigenmode mode_results['disp'] = mode_disp mode_results['stress'] = mode_stress + mode_results['stressv'] = mode_stressv + mode_results['strainv'] = mode_strain mode_results['time'] = 0 # Dont return time if static results.append(mode_results) mode_disp = {} @@ -336,7 +365,7 @@ def readResult(frd_input): return {'Nodes': nodes, 'Hexa8Elem': elements_hexa8, 'Penta6Elem': elements_penta6, 'Tetra4Elem': elements_tetra4, 'Tetra10Elem': elements_tetra10, '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} @@ -421,6 +450,8 @@ def importFrd(filename, analysis=None, result_name_prefix=None): break disp = result_set['disp'] + stressv = result_set['stressv'] + strainv = result_set['strainv'] no_of_values = len(disp) displacement = [] for k, v in disp.iteritems(): @@ -438,6 +469,8 @@ def importFrd(filename, analysis=None, result_name_prefix=None): if len(disp) > 0: 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() if(mesh_object): results.Mesh = mesh_object diff --git a/src/Mod/OpenSCAD/OpenSCADUtils.py b/src/Mod/OpenSCAD/OpenSCADUtils.py index 26ab35327..1785d430d 100644 --- a/src/Mod/OpenSCAD/OpenSCADUtils.py +++ b/src/Mod/OpenSCAD/OpenSCADUtils.py @@ -30,10 +30,10 @@ the module ''' try: + from PySide import QtGui _encoding = QtGui.QApplication.UnicodeUTF8 def translate(context, text): "convenience function for Qt translator" - from PySide import QtGui return QtGui.QApplication.translate(context, text, None, _encoding) except AttributeError: def translate(context, text): diff --git a/src/Mod/Part/App/TopoShapePy.xml b/src/Mod/Part/App/TopoShapePy.xml index ab11fe4eb..edc807ca9 100644 --- a/src/Mod/Part/App/TopoShapePy.xml +++ b/src/Mod/Part/App/TopoShapePy.xml @@ -186,7 +186,7 @@ Intersection of this and a given list of topo shapes. Supports: - 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 OCC 6.9.0 or later is required. @@ -203,7 +203,7 @@ Section of this and a given list of topo shapes. Supports: - 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 OCC 6.9.0 or later is required. diff --git a/src/Mod/Part/BOPTools/SplitFeatures.py b/src/Mod/Part/BOPTools/SplitFeatures.py index 5e9afcec9..1d639c72a 100644 --- a/src/Mod/Part/BOPTools/SplitFeatures.py +++ b/src/Mod/Part/BOPTools/SplitFeatures.py @@ -37,18 +37,18 @@ if FreeCAD.GuiUp: #-------------------------- translation-related code ---------------------------------------- #(see forum thread "A new Part tool is being born... JoinFeatures!" #http://forum.freecadweb.org/viewtopic.php?f=22&t=11112&start=30#p90239 ) -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except Exception: - def _fromUtf8(s): - return s -try: - _encoding = QtGui.QApplication.UnicodeUTF8 - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig, _encoding) -except AttributeError: - def _translate(context, text, disambig): - return QtGui.QApplication.translate(context, text, disambig) + try: + _fromUtf8 = QtCore.QString.fromUtf8 + except Exception: + def _fromUtf8(s): + return s + try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig, _encoding) + except AttributeError: + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig) #--------------------------/translation-related code ---------------------------------------- def getIconPath(icon_dot_svg): diff --git a/src/Mod/Part/Gui/TaskCheckGeometry.h b/src/Mod/Part/Gui/TaskCheckGeometry.h index 4910412b5..30822e608 100644 --- a/src/Mod/Part/Gui/TaskCheckGeometry.h +++ b/src/Mod/Part/Gui/TaskCheckGeometry.h @@ -99,7 +99,7 @@ public: ~TaskCheckGeometryResults(); QString getShapeContentString(); -private slots: +private Q_SLOTS: void currentRowChanged (const QModelIndex ¤t, const QModelIndex &previous); private: diff --git a/src/Mod/Part/Gui/TaskDimension.h b/src/Mod/Part/Gui/TaskDimension.h index 15f766c0b..6e05179f5 100644 --- a/src/Mod/Part/Gui/TaskDimension.h +++ b/src/Mod/Part/Gui/TaskDimension.h @@ -198,7 +198,7 @@ protected: QPixmap *stepActive; QPixmap *stepDone; -private slots: +private Q_SLOTS: void selectionSlot(bool checked); void buildPixmaps(); @@ -229,7 +229,7 @@ class DimensionControl : public QWidget public: explicit DimensionControl(QWidget* parent); QPushButton *resetButton; -public slots: +public Q_SLOTS: void toggle3dSlot(bool); void toggleDeltaSlot(bool); void clearAllSlot(bool); @@ -250,7 +250,7 @@ public: protected: virtual void onSelectionChanged(const Gui::SelectionChanges& msg); -protected slots: +protected Q_SLOTS: void selection1Slot(bool checked); void selection2Slot(bool checked); void resetDialogSlot(bool); @@ -326,7 +326,7 @@ public: protected: virtual void onSelectionChanged(const Gui::SelectionChanges& msg); -protected slots: +protected Q_SLOTS: void selection1Slot(bool checked); void selection2Slot(bool checked); void resetDialogSlot(bool); diff --git a/src/Mod/Path/App/TooltablePyImp.cpp b/src/Mod/Path/App/TooltablePyImp.cpp index d339b6195..0b2ad6607 100644 --- a/src/Mod/Path/App/TooltablePyImp.cpp +++ b/src/Mod/Path/App/TooltablePyImp.cpp @@ -121,12 +121,12 @@ int ToolPy::PyInit(PyObject* args, PyObject* kwd) else getToolPtr()->Material = Tool::MATUNDEFINED; - getToolPtr()->Diameter = PyFloat_AsDouble(dia); - getToolPtr()->LengthOffset = PyFloat_AsDouble(len); - getToolPtr()->FlatRadius = PyFloat_AsDouble(fla); - getToolPtr()->CornerRadius = PyFloat_AsDouble(cor); - getToolPtr()->CuttingEdgeAngle = PyFloat_AsDouble(ang); - getToolPtr()->CuttingEdgeHeight = PyFloat_AsDouble(hei); + getToolPtr()->Diameter = dia ? PyFloat_AsDouble(dia) : 0.0; + getToolPtr()->LengthOffset = len ? PyFloat_AsDouble(len) : 0.0; + getToolPtr()->FlatRadius = fla ? PyFloat_AsDouble(fla) : 0.0; + getToolPtr()->CornerRadius = cor ? PyFloat_AsDouble(cor) : 0.0; + getToolPtr()->CuttingEdgeAngle = ang ? PyFloat_AsDouble(ang) : 0.0; + getToolPtr()->CuttingEdgeHeight = hei ? PyFloat_AsDouble(hei) : 0.0; return 0; } diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index 701ba44b1..f01eb9b64 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -53,6 +53,7 @@ SET(PathScripts_SRCS PathScripts/PathSimpleCopy.py PathScripts/PathStock.py PathScripts/PathStop.py + PathScripts/PathHelix.py PathScripts/PathSurface.py PathScripts/PathToolLenOffset.py PathScripts/PathToolLibraryManager.py diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc index 5be33f8ea..5e701bc5c 100644 --- a/src/Mod/Path/Gui/Resources/Path.qrc +++ b/src/Mod/Path/Gui/Resources/Path.qrc @@ -18,6 +18,7 @@ icons/Path-FaceProfile.svg icons/Path-Face.svg icons/Path-Heights.svg + icons/Path-Helix.svg icons/Path-Hop.svg icons/Path-Inspect.svg icons/Path-Job.svg diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Helix.svg b/src/Mod/Path/Gui/Resources/icons/Path-Helix.svg new file mode 100644 index 000000000..48b2ed309 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-Helix.svg @@ -0,0 +1,548 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py index b5f020413..22b13882b 100644 --- a/src/Mod/Path/InitGui.py +++ b/src/Mod/Path/InitGui.py @@ -65,6 +65,7 @@ class PathWorkbench (Workbench): from PathScripts import PathCustom from PathScripts import PathInspect from PathScripts import PathSimpleCopy + from PathScripts import PathHelix from PathScripts import PathEngrave from PathScripts import PathSurface from PathScripts import PathSanity @@ -80,7 +81,7 @@ class PathWorkbench (Workbench): projcmdlist = ["Path_Job", "Path_Post", "Path_Inspect", "Path_Sanity"] toolcmdlist = ["Path_ToolLibraryEdit", "Path_LoadTool"] 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"] modcmdlist = ["Path_Copy", "Path_CompoundExtended", "Path_Array", "Path_SimpleCopy" ] dressupcmdlist = ["PathDressup_Dogbone", "PathDressup_DragKnife", "PathDressup_HoldingTags"] diff --git a/src/Mod/Path/PathScripts/PathDressupDogbone.py b/src/Mod/Path/PathScripts/PathDressupDogbone.py index 23e56841b..9aa008b7b 100644 --- a/src/Mod/Path/PathScripts/PathDressupDogbone.py +++ b/src/Mod/Path/PathScripts/PathDressupDogbone.py @@ -275,7 +275,7 @@ class Chord (object): return dir == 'Back' or dir == side def connectsTo(self, chord): - return PathGeom.isRoughly(self.End, chord.Start) + return PathGeom.pointsCoincide(self.End, chord.Start) class Bone: def __init__(self, boneId, obj, lastCommand, inChord, outChord, smooth): diff --git a/src/Mod/Path/PathScripts/PathHelix.py b/src/Mod/Path/PathScripts/PathHelix.py new file mode 100644 index 000000000..da2e63a72 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathHelix.py @@ -0,0 +1,818 @@ +# -*- coding: utf-8 -*- + +#*************************************************************************** +#* * +#* Copyright (c) 2016 Lorenz Hüdepohl * +#* * +#* 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()) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index d610f64e4..b2a1336c7 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -29,6 +29,7 @@ #endif #include +#include #include #include #include @@ -3252,14 +3253,34 @@ void CmdSketcherConstrainAngle::activated(int iMsg) Base::Vector3d p1b = lineSeg1->getEndPoint(); Base::Vector3d p2a = lineSeg2->getStartPoint(); Base::Vector3d p2b = lineSeg2->getEndPoint(); - double length = DBL_MAX; - for (int i=0; i <= 1; i++) { - for (int j=0; j <= 1; j++) { - double tmp = ((j?p2a:p2b)-(i?p1a:p1b)).Length(); - if (tmp < length) { - length = tmp; - PosId1 = i ? Sketcher::start : Sketcher::end; - PosId2 = j ? Sketcher::start : Sketcher::end; + + // 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; + for (int i=0; i <= 1; i++) { + for (int j=0; j <= 1; j++) { + double tmp = Base::DistanceP2((j?p2a:p2b), (i?p1a:p1b)); + if (tmp < length) { + length = tmp; + PosId1 = i ? Sketcher::start : Sketcher::end; + PosId2 = j ? Sketcher::start : Sketcher::end; + } } } } @@ -3280,8 +3301,8 @@ void CmdSketcherConstrainAngle::activated(int iMsg) } } - double ActAngle = atan2(-dir1.y*dir2.x+dir1.x*dir2.y, - dir1.x*dir2.x+dir1.y*dir2.y); + double ActAngle = atan2(dir1.x*dir2.y-dir1.y*dir2.x, + dir1.y*dir2.y+dir1.x*dir2.x); if (ActAngle < 0) { ActAngle *= -1; std::swap(GeoId1,GeoId2); diff --git a/src/Mod/Spreadsheet/Gui/qtcolorpicker.cpp b/src/Mod/Spreadsheet/Gui/qtcolorpicker.cpp index ea222095c..d40584f9e 100644 --- a/src/Mod/Spreadsheet/Gui/qtcolorpicker.cpp +++ b/src/Mod/Spreadsheet/Gui/qtcolorpicker.cpp @@ -1,17 +1,17 @@ /**************************************************************************** ** ** This file is part of a Qt Solutions component. -** +** ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). -** +** ** Contact: Qt Software Information (qt-info@nokia.com) -** -** Commercial Usage +** +** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Solutions Commercial License Agreement provided ** with the Software or, alternatively, in accordance with the terms ** contained in a written agreement between you and Nokia. -** +** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software @@ -19,29 +19,29 @@ ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** +** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this ** package. -** -** GNU General Public License Usage +** +** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. -** +** ** Please note Third Party Software included with Qt Solutions may impose ** additional restrictions and it is the user's responsibility to ensure ** that they have met the licensing requirements of the GPL, LGPL, or Qt ** Solutions Commercial license and the relevant license of the Third ** Party Software they are using. -** +** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at qt-sales@nokia.com. -** +** ****************************************************************************/ #include @@ -169,7 +169,7 @@ class ColorPickerItem : public QFrame public: ColorPickerItem(const QColor &color = Qt::white, const QString &text = QString::null, - QWidget *parent = 0); + QWidget *parent = 0); ~ColorPickerItem(); QColor color() const; @@ -181,7 +181,7 @@ signals: void clicked(); void selected(); -public slots: +public Q_SLOTS: void setColor(const QColor &color, const QString &text = QString()); protected: @@ -205,7 +205,7 @@ class ColorPickerPopup : public QFrame public: ColorPickerPopup(int width, bool withColorDialog, - QWidget *parent = 0); + QWidget *parent = 0); ~ColorPickerPopup(); void insertColor(const QColor &col, const QString &text, int index); @@ -217,17 +217,17 @@ public: ColorPickerItem *find(const QColor &col) const; QColor color(int index) const; - + void setLastSel(const QColor & col); signals: void selected(const QColor &); void hid(); -public slots: +public Q_SLOTS: void getColorFromDialog(); -protected slots: +protected Q_SLOTS: void updateSelected(); protected: @@ -268,7 +268,7 @@ private: \sa QFrame */ QtColorPicker::QtColorPicker(QWidget *parent, - int cols, bool enableColorDialog) + int cols, bool enableColorDialog) : QPushButton(parent), popup(0), withColorDialog(enableColorDialog) { setFocusPolicy(Qt::StrongFocus); @@ -288,7 +288,7 @@ QtColorPicker::QtColorPicker(QWidget *parent, // Create color grid popup and connect to it. popup = new ColorPickerPopup(cols, withColorDialog, this); connect(popup, SIGNAL(selected(const QColor &)), - SLOT(setCurrentColor(const QColor &))); + SLOT(setCurrentColor(const QColor &))); connect(popup, SIGNAL(hid()), SLOT(popupClosed())); // Connect this push button's pressed() signal. @@ -434,18 +434,18 @@ void QtColorPicker::setStandardColors() void QtColorPicker::setCurrentColor(const QColor &color) { if (color.isValid() && col == color) { - emit colorSet(color); + Q_EMIT colorSet(color); return; } if (col == color || !color.isValid()) - return; + return; ColorPickerItem *item = popup->find(color); if (!item) { - insertColor(color, tr("Custom")); - item = popup->find(color); + insertColor(color, tr("Custom")); + item = popup->find(color); } - + popup->setLastSel(color); col = color; @@ -457,8 +457,8 @@ void QtColorPicker::setCurrentColor(const QColor &color) repaint(); item->setSelected(true); - emit colorChanged(color); - emit colorSet(color); + Q_EMIT colorChanged(color); + Q_EMIT colorSet(color); } /*! @@ -471,9 +471,9 @@ void QtColorPicker::insertColor(const QColor &color, const QString &text, int in { popup->insertColor(color, text, index); if (!firstInserted) { - col = color; - setText(text); - firstInserted = true; + col = color; + setText(text); + firstInserted = true; } } @@ -504,7 +504,7 @@ bool QtColorPicker::colorDialogEnabled() const \code void Drawer::mouseReleaseEvent(QMouseEvent *e) { - if (e->button() & RightButton) { + if (e->button() & RightButton) { QColor color = QtColorPicker::getColor(mapToGlobal(e->pos())); } } @@ -542,7 +542,7 @@ QColor QtColorPicker::getColor(const QPoint &point, bool allowCustomColors) Constructs the popup widget. */ ColorPickerPopup::ColorPickerPopup(int width, bool withColorDialog, - QWidget *parent) + QWidget *parent) : QFrame(parent, Qt::Popup) { setFrameStyle(QFrame::StyledPanel); @@ -553,13 +553,13 @@ ColorPickerPopup::ColorPickerPopup(int width, bool withColorDialog, cols = width; if (withColorDialog) { - moreButton = new ColorPickerButton(this); - moreButton->setFixedWidth(24); - moreButton->setFixedHeight(21); - moreButton->setFrameRect(QRect(2, 2, 20, 17)); - connect(moreButton, SIGNAL(clicked()), SLOT(getColorFromDialog())); + moreButton = new ColorPickerButton(this); + moreButton->setFixedWidth(24); + moreButton->setFixedHeight(21); + moreButton->setFrameRect(QRect(2, 2, 20, 17)); + connect(moreButton, SIGNAL(clicked()), SLOT(getColorFromDialog())); } else { - moreButton = 0; + moreButton = 0; } eventLoop = 0; @@ -586,8 +586,8 @@ ColorPickerPopup::~ColorPickerPopup() ColorPickerItem *ColorPickerPopup::find(const QColor &col) const { for (int i = 0; i < items.size(); ++i) { - if (items.at(i) && items.at(i)->color() == col) - return items.at(i); + if (items.at(i) && items.at(i)->color() == col) + return items.at(i); } return 0; @@ -626,7 +626,7 @@ void ColorPickerPopup::insertColor(const QColor &col, const QString &text, int i connect(item, SIGNAL(selected()), SLOT(updateSelected())); if (index == -1) - index = items.count(); + index = items.count(); items.insert((unsigned int)index, item); regenerateGrid(); @@ -667,19 +667,19 @@ void ColorPickerPopup::updateSelected() QLayoutItem *layoutItem; int i = 0; while ((layoutItem = grid->itemAt(i)) != 0) { - QWidget *w = layoutItem->widget(); - if (w && w->inherits("ColorPickerItem")) { - ColorPickerItem *litem = reinterpret_cast(layoutItem->widget()); - if (litem != sender()) - litem->setSelected(false); - } - ++i; + QWidget *w = layoutItem->widget(); + if (w && w->inherits("ColorPickerItem")) { + ColorPickerItem *litem = reinterpret_cast(layoutItem->widget()); + if (litem != sender()) + litem->setSelected(false); + } + ++i; } if (sender() && sender()->inherits("ColorPickerItem")) { - ColorPickerItem *item = (ColorPickerItem *)sender(); - lastSel = item->color(); - emit selected(item->color()); + ColorPickerItem *item = (ColorPickerItem *)sender(); + lastSel = item->color(); + Q_EMIT selected(item->color()); } hide(); @@ -691,7 +691,7 @@ void ColorPickerPopup::updateSelected() void ColorPickerPopup::mouseReleaseEvent(QMouseEvent *e) { if (!rect().contains(e->pos())) - hide(); + hide(); } /*! \internal @@ -705,96 +705,96 @@ void ColorPickerPopup::keyPressEvent(QKeyEvent *e) bool foundFocus = false; for (int j = 0; !foundFocus && j < grid->rowCount(); ++j) { - for (int i = 0; !foundFocus && i < grid->columnCount(); ++i) { - if (widgetAt[j][i] && widgetAt[j][i]->hasFocus()) { - curRow = j; - curCol = i; - foundFocus = true; - break; - } - } + for (int i = 0; !foundFocus && i < grid->columnCount(); ++i) { + if (widgetAt[j][i] && widgetAt[j][i]->hasFocus()) { + curRow = j; + curCol = i; + foundFocus = true; + break; + } + } } switch (e->key()) { - case Qt::Key_Left: - if (curCol > 0) --curCol; - else if (curRow > 0) { --curRow; curCol = grid->columnCount() - 1; } - break; - case Qt::Key_Right: - if (curCol < grid->columnCount() - 1 && widgetAt[curRow][curCol + 1]) ++curCol; - else if (curRow < grid->rowCount() - 1) { ++curRow; curCol = 0; } - break; - case Qt::Key_Up: - if (curRow > 0) --curRow; - else curCol = 0; - break; - case Qt::Key_Down: - if (curRow < grid->rowCount() - 1) { - QWidget *w = widgetAt[curRow + 1][curCol]; - if (w) { - ++curRow; - } else for (int i = 1; i < grid->columnCount(); ++i) { - if (!widgetAt[curRow + 1][i]) { - curCol = i - 1; - ++curRow; - break; - } - } - } - break; - case Qt::Key_Space: - case Qt::Key_Return: - case Qt::Key_Enter: { - QWidget *w = widgetAt[curRow][curCol]; - if (w && w->inherits("ColorPickerItem")) { - ColorPickerItem *wi = reinterpret_cast(w); - wi->setSelected(true); + case Qt::Key_Left: + if (curCol > 0) --curCol; + else if (curRow > 0) { --curRow; curCol = grid->columnCount() - 1; } + break; + case Qt::Key_Right: + if (curCol < grid->columnCount() - 1 && widgetAt[curRow][curCol + 1]) ++curCol; + else if (curRow < grid->rowCount() - 1) { ++curRow; curCol = 0; } + break; + case Qt::Key_Up: + if (curRow > 0) --curRow; + else curCol = 0; + break; + case Qt::Key_Down: + if (curRow < grid->rowCount() - 1) { + QWidget *w = widgetAt[curRow + 1][curCol]; + if (w) { + ++curRow; + } else for (int i = 1; i < grid->columnCount(); ++i) { + if (!widgetAt[curRow + 1][i]) { + curCol = i - 1; + ++curRow; + break; + } + } + } + break; + case Qt::Key_Space: + case Qt::Key_Return: + case Qt::Key_Enter: { + QWidget *w = widgetAt[curRow][curCol]; + if (w && w->inherits("ColorPickerItem")) { + ColorPickerItem *wi = reinterpret_cast(w); + wi->setSelected(true); - QLayoutItem *layoutItem; + QLayoutItem *layoutItem; int i = 0; - while ((layoutItem = grid->itemAt(i)) != 0) { - QWidget *w = layoutItem->widget(); - if (w && w->inherits("ColorPickerItem")) { - ColorPickerItem *litem - = reinterpret_cast(layoutItem->widget()); - if (litem != wi) - litem->setSelected(false); - } - ++i; - } + while ((layoutItem = grid->itemAt(i)) != 0) { + QWidget *w = layoutItem->widget(); + if (w && w->inherits("ColorPickerItem")) { + ColorPickerItem *litem + = reinterpret_cast(layoutItem->widget()); + if (litem != wi) + litem->setSelected(false); + } + ++i; + } - lastSel = wi->color(); - emit selected(wi->color()); - hide(); - } else if (w && w->inherits("QPushButton")) { - ColorPickerItem *wi = reinterpret_cast(w); - wi->setSelected(true); + lastSel = wi->color(); + Q_EMIT selected(wi->color()); + hide(); + } else if (w && w->inherits("QPushButton")) { + ColorPickerItem *wi = reinterpret_cast(w); + wi->setSelected(true); - QLayoutItem *layoutItem; + QLayoutItem *layoutItem; int i = 0; - while ((layoutItem = grid->itemAt(i)) != 0) { - QWidget *w = layoutItem->widget(); - if (w && w->inherits("ColorPickerItem")) { - ColorPickerItem *litem - = reinterpret_cast(layoutItem->widget()); - if (litem != wi) - litem->setSelected(false); - } - ++i; - } + while ((layoutItem = grid->itemAt(i)) != 0) { + QWidget *w = layoutItem->widget(); + if (w && w->inherits("ColorPickerItem")) { + ColorPickerItem *litem + = reinterpret_cast(layoutItem->widget()); + if (litem != wi) + litem->setSelected(false); + } + ++i; + } - lastSel = wi->color(); - emit selected(wi->color()); - hide(); - } - } - break; + lastSel = wi->color(); + Q_EMIT selected(wi->color()); + hide(); + } + } + break; case Qt::Key_Escape: hide(); break; - default: - e->ignore(); - break; + default: + e->ignore(); + break; } widgetAt[curRow][curCol]->setFocus(); @@ -806,12 +806,12 @@ void ColorPickerPopup::keyPressEvent(QKeyEvent *e) void ColorPickerPopup::hideEvent(QHideEvent *e) { if (eventLoop) { - eventLoop->exit(); + eventLoop->exit(); } setFocus(); - emit hid(); + Q_EMIT hid(); QFrame::hideEvent(e); } @@ -832,23 +832,23 @@ void ColorPickerPopup::showEvent(QShowEvent *) { bool foundSelected = false; for (int i = 0; i < grid->columnCount(); ++i) { - for (int j = 0; j < grid->rowCount(); ++j) { - QWidget *w = widgetAt[j][i]; - if (w && w->inherits("ColorPickerItem")) { - if (((ColorPickerItem *)w)->isSelected()) { - w->setFocus(); - foundSelected = true; - break; - } - } - } + for (int j = 0; j < grid->rowCount(); ++j) { + QWidget *w = widgetAt[j][i]; + if (w && w->inherits("ColorPickerItem")) { + if (((ColorPickerItem *)w)->isSelected()) { + w->setFocus(); + foundSelected = true; + break; + } + } + } } if (!foundSelected) { - if (items.count() == 0) - setFocus(); - else - widgetAt[0][0]->setFocus(); + if (items.count() == 0) + setFocus(); + else + widgetAt[0][0]->setFocus(); } } @@ -861,7 +861,7 @@ void ColorPickerPopup::regenerateGrid() int columns = cols; if (columns == -1) - columns = (int) ceil(sqrt((float) items.count())); + columns = (int) ceil(sqrt((float) items.count())); // When the number of columns grows, the number of rows will // fall. There's no way to shrink a grid, so we create a new @@ -884,8 +884,8 @@ void ColorPickerPopup::regenerateGrid() } if (moreButton) { - grid->addWidget(moreButton, crow, ccol); - widgetAt[crow][ccol] = moreButton; + grid->addWidget(moreButton, crow, ccol); + widgetAt[crow][ccol] = moreButton; } updateGeometry(); } @@ -901,12 +901,12 @@ void ColorPickerPopup::getColorFromDialog() //QRgb rgb = QColorDialog::getRgba(lastSel.rgba(), &ok, parentWidget()); QColor col = QColorDialog::getColor(lastSel,parentWidget(),0,QColorDialog::ShowAlphaChannel); if (!col.isValid()) - return; + return; //QColor col = QColor::fromRgba(rgb); insertColor(col, tr("Custom"), -1); lastSel = col; - emit selected(col); + Q_EMIT selected(col); } void ColorPickerPopup::setLastSel(const QColor & col) { lastSel = col; } @@ -916,7 +916,7 @@ void ColorPickerPopup::setLastSel(const QColor & col) { lastSel = col; } whose name is set to \a text. */ ColorPickerItem::ColorPickerItem(const QColor &color, const QString &text, - QWidget *parent) + QWidget *parent) : QFrame(parent), c(color), t(text), sel(false) { setToolTip(t); @@ -994,7 +994,7 @@ void ColorPickerItem::mouseMoveEvent(QMouseEvent *) void ColorPickerItem::mouseReleaseEvent(QMouseEvent *) { sel = true; - emit selected(); + Q_EMIT selected(); } /*! @@ -1018,14 +1018,14 @@ void ColorPickerItem::paintEvent(QPaintEvent *) p.setPen( QPen( Qt::gray, 0, Qt::SolidLine ) ); if (sel) - p.drawRect(1, 1, w - 3, h - 3); + p.drawRect(1, 1, w - 3, h - 3); p.setPen( QPen( Qt::black, 0, Qt::SolidLine ) ); p.drawRect(3, 3, w - 7, h - 7); p.fillRect(QRect(4, 4, w - 8, h - 8), QBrush(c)); if (hasFocus()) - p.drawRect(0, 0, w - 1, h - 1); + p.drawRect(0, 0, w - 1, h - 1); } /*! @@ -1062,7 +1062,7 @@ void ColorPickerButton::mouseReleaseEvent(QMouseEvent *) { setFrameShadow(Raised); repaint(); - emit clicked(); + Q_EMIT clicked(); } /*! @@ -1071,15 +1071,15 @@ void ColorPickerButton::mouseReleaseEvent(QMouseEvent *) void ColorPickerButton::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Up - || e->key() == Qt::Key_Down - || e->key() == Qt::Key_Left - || e->key() == Qt::Key_Right) { - qApp->sendEvent(parent(), e); + || e->key() == Qt::Key_Down + || e->key() == Qt::Key_Left + || e->key() == Qt::Key_Right) { + qApp->sendEvent(parent(), e); } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Space || e->key() == Qt::Key_Return) { - setFrameShadow(Sunken); - update(); + setFrameShadow(Sunken); + update(); } else { - QFrame::keyPressEvent(e); + QFrame::keyPressEvent(e); } } @@ -1089,16 +1089,16 @@ void ColorPickerButton::keyPressEvent(QKeyEvent *e) void ColorPickerButton::keyReleaseEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Up - || e->key() == Qt::Key_Down - || e->key() == Qt::Key_Left - || e->key() == Qt::Key_Right) { - qApp->sendEvent(parent(), e); + || e->key() == Qt::Key_Down + || e->key() == Qt::Key_Left + || e->key() == Qt::Key_Right) { + qApp->sendEvent(parent(), e); } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Space || e->key() == Qt::Key_Return) { - setFrameShadow(Raised); - repaint(); - emit clicked(); + setFrameShadow(Raised); + repaint(); + Q_EMIT clicked(); } else { - QFrame::keyReleaseEvent(e); + QFrame::keyReleaseEvent(e); } } @@ -1144,8 +1144,8 @@ void ColorPickerButton::paintEvent(QPaintEvent *e) p.drawRect(r.center().x() + offset , r.center().y() + offset, 1, 1); p.drawRect(r.center().x() + offset + 4, r.center().y() + offset, 1, 1); if (hasFocus()) { - p.setPen( QPen( Qt::black, 0, Qt::SolidLine ) ); - p.drawRect(0, 0, width() - 1, height() - 1); + p.setPen( QPen( Qt::black, 0, Qt::SolidLine ) ); + p.drawRect(0, 0, width() - 1, height() - 1); } p.end(); diff --git a/src/Tools/plugins/widget/customwidgets.cpp b/src/Tools/plugins/widget/customwidgets.cpp index d96b9fb9c..51f70a6df 100644 --- a/src/Tools/plugins/widget/customwidgets.cpp +++ b/src/Tools/plugins/widget/customwidgets.cpp @@ -182,7 +182,7 @@ void FileChooser::chooseFile() if (!fn.isEmpty()) { lineEdit->setText(fn); - emit fileNameSelected(fn); + Q_EMIT fileNameSelected(fn); } } @@ -595,9 +595,9 @@ QAbstractSpinBox::StepEnabled QuantitySpinBox::stepEnabled() const } return ret; } - -void QuantitySpinBox::stepBy(int steps) -{ + +void QuantitySpinBox::stepBy(int steps) +{ double step = StepSize * steps; double val = Value + step; if (val > Maximum) @@ -607,7 +607,7 @@ void QuantitySpinBox::stepBy(int steps) lineEdit()->setText(QString::fromUtf8("%L1 %2").arg(val).arg(UnitStr)); update(); -} +} void QuantitySpinBox::setUnitText(QString str) { @@ -792,9 +792,9 @@ public: UIntSpinBox::UIntSpinBox (QWidget* parent) : QSpinBox (parent) { - d = new UIntSpinBoxPrivate; + d = new UIntSpinBoxPrivate; d->mValidator = new UnsignedValidator(this->minimum(), this->maximum(), this); - connect(this, SIGNAL(valueChanged(int)), + connect(this, SIGNAL(valueChanged(int)), this, SLOT(valueChange(int))); setRange(0, 99); setValue(0); @@ -802,7 +802,7 @@ UIntSpinBox::UIntSpinBox (QWidget* parent) } UIntSpinBox::~UIntSpinBox() -{ +{ delete d->mValidator; delete d; d = 0; } @@ -1032,7 +1032,7 @@ void ColorButton::onChooseColor() if ( c.isValid() ) { setColor( c ); - emit changed(); + Q_EMIT changed(); } } diff --git a/src/Tools/plugins/widget/wizard.cpp b/src/Tools/plugins/widget/wizard.cpp index 7d788baf6..6aae3ac0a 100644 --- a/src/Tools/plugins/widget/wizard.cpp +++ b/src/Tools/plugins/widget/wizard.cpp @@ -153,7 +153,7 @@ void Wizard::setCurrentIndex(int index) textLabel->setText(stackWidget->currentWidget()->windowTitle()); _backButton->setEnabled(index > 0); _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); textLabel->setText(newTitle); - emit pageTitleChanged(newTitle); + Q_EMIT pageTitleChanged(newTitle); } WizardExtension::WizardExtension(Wizard *widget, QObject *parent)