File indexing completed on 2024-05-12 15:28:10
0001 /*************************************************************************** 0002 File : MatrixFunctionDialog.cpp 0003 Project : LabPlot 0004 Description : Dialog for generating matrix values from a mathematical function 0005 ------------------------------------------------------------------------ 0006 Copyright : (C) 2015-2019 by Alexander Semke (alexander.semke@web.de) 0007 Copyright : (C) 2016 by Stefan Gerlach (stefan.gerlach@uni.kn) 0008 0009 ***************************************************************************/ 0010 0011 /*************************************************************************** 0012 * * 0013 * This program is free software; you can redistribute it and/or modify * 0014 * it under the terms of the GNU General Public License as published by * 0015 * the Free Software Foundation; either version 2 of the License, or * 0016 * (at your option) any later version. * 0017 * * 0018 * This program is distributed in the hope that it will be useful, * 0019 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0021 * GNU General Public License for more details. * 0022 * * 0023 * You should have received a copy of the GNU General Public License * 0024 * along with this program; if not, write to the Free Software * 0025 * Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0026 * Boston, MA 02110-1301 USA * 0027 * * 0028 ***************************************************************************/ 0029 #include "MatrixFunctionDialog.h" 0030 #include "backend/lib/macros.h" 0031 #include "backend/gsl/ExpressionParser.h" 0032 #include "backend/matrix/Matrix.h" 0033 #include "kdefrontend/widgets/ConstantsWidget.h" 0034 #include "kdefrontend/widgets/FunctionsWidget.h" 0035 0036 #include <KWindowConfig> 0037 #include <KLocalizedString> 0038 0039 #include <QMenu> 0040 #include <QWidgetAction> 0041 #include <QDialogButtonBox> 0042 #include <QPushButton> 0043 #include <QThreadPool> 0044 #include <QWindow> 0045 #ifndef NDEBUG 0046 #include <QElapsedTimer> 0047 #endif 0048 0049 extern "C" { 0050 #include "backend/gsl/parser.h" 0051 } 0052 #include <cmath> 0053 0054 /*! 0055 \class MatrixFunctionDialog 0056 \brief Dialog for generating matrix values from a mathematical function. 0057 0058 \ingroup kdefrontend 0059 */ 0060 MatrixFunctionDialog::MatrixFunctionDialog(Matrix* m, QWidget* parent) : QDialog(parent), m_matrix(m) { 0061 Q_ASSERT(m_matrix); 0062 setWindowTitle(i18nc("@title:window", "Function Values")); 0063 0064 ui.setupUi(this); 0065 setAttribute(Qt::WA_DeleteOnClose); 0066 ui.tbConstants->setIcon( QIcon::fromTheme("labplot-format-text-symbol") ); 0067 ui.tbFunctions->setIcon( QIcon::fromTheme("preferences-desktop-font") ); 0068 0069 QStringList vars; 0070 vars << "x" << "y"; 0071 ui.teEquation->setVariables(vars); 0072 ui.teEquation->setFocus(); 0073 ui.teEquation->setMaximumHeight(QLineEdit().sizeHint().height()*2); 0074 0075 SET_NUMBER_LOCALE 0076 QString info = '[' + numberLocale.toString(m_matrix->xStart()) + ", " + numberLocale.toString(m_matrix->xEnd()) + "], " + i18np("%1 value", "%1 values", m_matrix->columnCount()); 0077 ui.lXInfo->setText(info); 0078 info = '[' + numberLocale.toString(m_matrix->yStart()) + ", " +numberLocale.toString(m_matrix->yEnd()) + "], " + i18np("%1 value", "%1 values", m_matrix->rowCount()); 0079 ui.lYInfo->setText(info); 0080 0081 ui.teEquation->setPlainText(m_matrix->formula()); 0082 auto* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 0083 0084 ui.gridLayout_2->addWidget(btnBox, 3, 0, 1, 3); 0085 m_okButton = btnBox->button(QDialogButtonBox::Ok); 0086 0087 connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &MatrixFunctionDialog::close); 0088 connect(btnBox, &QDialogButtonBox::accepted, this, &MatrixFunctionDialog::accept); 0089 connect(btnBox, &QDialogButtonBox::rejected, this, &MatrixFunctionDialog::reject); 0090 0091 m_okButton->setText(i18n("&Generate")); 0092 m_okButton->setToolTip(i18n("Generate function values")); 0093 0094 connect(ui.teEquation, &ExpressionTextEdit::expressionChanged, this, &MatrixFunctionDialog::checkValues); 0095 connect(ui.tbConstants, &QToolButton::clicked, this, &MatrixFunctionDialog::showConstants); 0096 connect(ui.tbFunctions, &QToolButton::clicked, this, &MatrixFunctionDialog::showFunctions); 0097 connect(m_okButton, &QPushButton::clicked, this, &MatrixFunctionDialog::generate); 0098 0099 //restore saved settings if available 0100 create(); // ensure there's a window created 0101 KConfigGroup conf(KSharedConfig::openConfig(), "MatrixFunctionDialog"); 0102 if (conf.exists()) { 0103 KWindowConfig::restoreWindowSize(windowHandle(), conf); 0104 resize(windowHandle()->size()); // workaround for QTBUG-40584 0105 } else 0106 resize(QSize(300, 0).expandedTo(minimumSize())); 0107 } 0108 0109 MatrixFunctionDialog::~MatrixFunctionDialog() { 0110 KConfigGroup conf(KSharedConfig::openConfig(), "MatrixFunctionDialog"); 0111 KWindowConfig::saveWindowSize(windowHandle(), conf); 0112 } 0113 0114 void MatrixFunctionDialog::checkValues() { 0115 if (!ui.teEquation->isValid()) { 0116 m_okButton->setEnabled(false); 0117 return; 0118 } 0119 0120 m_okButton->setEnabled(true); 0121 } 0122 0123 void MatrixFunctionDialog::showConstants() { 0124 QMenu menu; 0125 ConstantsWidget constants(&menu); 0126 connect(&constants, &ConstantsWidget::constantSelected, this, &MatrixFunctionDialog::insertConstant); 0127 connect(&constants, &ConstantsWidget::constantSelected, &menu, &QMenu::close); 0128 connect(&constants, &ConstantsWidget::canceled, &menu, &QMenu::close); 0129 0130 auto* widgetAction = new QWidgetAction(this); 0131 widgetAction->setDefaultWidget(&constants); 0132 menu.addAction(widgetAction); 0133 0134 QPoint pos(-menu.sizeHint().width()+ui.tbConstants->width(),-menu.sizeHint().height()); 0135 menu.exec(ui.tbConstants->mapToGlobal(pos)); 0136 } 0137 0138 void MatrixFunctionDialog::showFunctions() { 0139 QMenu menu; 0140 FunctionsWidget functions(&menu); 0141 connect(&functions, &FunctionsWidget::functionSelected, this, &MatrixFunctionDialog::insertFunction); 0142 connect(&functions, &FunctionsWidget::functionSelected, &menu, &QMenu::close); 0143 connect(&functions, &FunctionsWidget::canceled, &menu, &QMenu::close); 0144 0145 auto* widgetAction = new QWidgetAction(this); 0146 widgetAction->setDefaultWidget(&functions); 0147 menu.addAction(widgetAction); 0148 0149 QPoint pos(-menu.sizeHint().width()+ui.tbFunctions->width(),-menu.sizeHint().height()); 0150 menu.exec(ui.tbFunctions->mapToGlobal(pos)); 0151 } 0152 0153 void MatrixFunctionDialog::insertFunction(const QString& functionName) const { 0154 ui.teEquation->insertPlainText(functionName + ExpressionParser::functionArgumentString(functionName, XYEquationCurve::EquationType::Cartesian)); 0155 } 0156 0157 void MatrixFunctionDialog::insertConstant(const QString& constantsName) const { 0158 ui.teEquation->insertPlainText(constantsName); 0159 } 0160 0161 /* task class for parallel fill (not used) */ 0162 class GenerateValueTask : public QRunnable { 0163 public: 0164 GenerateValueTask(int startCol, int endCol, QVector<QVector<double>>& matrixData, double xStart, double yStart, 0165 double xStep, double yStep, char* func): m_startCol(startCol), m_endCol(endCol), m_matrixData(matrixData), 0166 m_xStart(xStart), m_yStart(yStart), m_xStep(xStep), m_yStep(yStep), m_func(func) { 0167 }; 0168 0169 void run() override { 0170 const int rows = m_matrixData[m_startCol].size(); 0171 double x = m_xStart; 0172 double y = m_yStart; 0173 DEBUG("FILL col"<<m_startCol<<"-"<<m_endCol<<" x/y ="<<x<<'/'<<y<<" steps ="<<m_xStep<<'/'<<m_yStep<<" rows ="<<rows); 0174 0175 parser_var vars[] = { {"x", x}, {"y", y}}; 0176 SET_NUMBER_LOCALE 0177 for (int col = m_startCol; col < m_endCol; ++col) { 0178 vars[0].value = x; 0179 for (int row = 0; row < rows; ++row) { 0180 vars[1].value = y; 0181 double z = parse_with_vars(m_func, vars, 2, qPrintable(numberLocale.name())); 0182 //qDebug()<<" z ="<<z; 0183 m_matrixData[col][row] = z; 0184 y += m_yStep; 0185 } 0186 0187 y = m_yStart; 0188 x += m_xStep; 0189 } 0190 } 0191 0192 private: 0193 int m_startCol; 0194 int m_endCol; 0195 QVector<QVector<double>>& m_matrixData; 0196 double m_xStart; 0197 double m_yStart; 0198 double m_xStep; 0199 double m_yStep; 0200 char* m_func; 0201 }; 0202 0203 void MatrixFunctionDialog::generate() { 0204 WAIT_CURSOR; 0205 0206 m_matrix->beginMacro(i18n("%1: fill matrix with function values", m_matrix->name())); 0207 0208 //TODO: data types 0209 auto* new_data = static_cast<QVector<QVector<double>>*>(m_matrix->data()); 0210 0211 // check if rows or cols == 1 0212 double diff = m_matrix->xEnd() - m_matrix->xStart(); 0213 double xStep = 0.0; 0214 if (m_matrix->columnCount() > 1) 0215 xStep = diff/double(m_matrix->columnCount() - 1); 0216 0217 diff = m_matrix->yEnd() - m_matrix->yStart(); 0218 double yStep = 0.0; 0219 if (m_matrix->rowCount() > 1) 0220 yStep = diff/double(m_matrix->rowCount() - 1); 0221 0222 #ifndef NDEBUG 0223 QElapsedTimer timer; 0224 timer.start(); 0225 #endif 0226 0227 //TODO: too slow because every parser thread needs an own symbol_table 0228 // idea: use pool->maxThreadCount() symbol tables and reuse them? 0229 /* double yStart = m_matrix->yStart(); 0230 const int cols = m_matrix->columnCount(); 0231 QThreadPool* pool = QThreadPool::globalInstance(); 0232 int range = ceil(double(cols)/pool->maxThreadCount()); 0233 DEBUG("Starting" << pool->maxThreadCount() << "threads. cols =" << cols << ": range =" << range); 0234 0235 for (int i = 0; i < pool->maxThreadCount(); ++i) { 0236 const int start = i*range; 0237 int end = (i+1)*range; 0238 if (end > cols) end = cols; 0239 qDebug() << "start/end: " << start << end; 0240 const double xStart = m_matrix->xStart() + xStep*start; 0241 GenerateValueTask* task = new GenerateValueTask(start, end, new_data, xStart, yStart, xStep, yStep, 0242 qPrintable(ui.teEquation->toPlainText())); 0243 task->setAutoDelete(false); 0244 pool->start(task); 0245 } 0246 pool->waitForDone(); 0247 */ 0248 double x = 0, y = 0; 0249 parser_var vars[] = {{"x", x}, {"y", y}}; 0250 SET_NUMBER_LOCALE 0251 for (int col = 0; col < m_matrix->columnCount(); ++col) { 0252 vars[0].value = x; 0253 for (int row = 0; row < m_matrix->rowCount(); ++row) { 0254 vars[1].value = y; 0255 (new_data->operator[](col))[row] = parse_with_vars(qPrintable(ui.teEquation->toPlainText()), 0256 vars, 2, qPrintable(numberLocale.name())); 0257 y += yStep; 0258 } 0259 y = m_matrix->yStart(); 0260 x += xStep; 0261 } 0262 0263 // Timing 0264 DEBUG("elapsed time =" << timer.elapsed() << "ms"); 0265 0266 m_matrix->setFormula(ui.teEquation->toPlainText()); 0267 m_matrix->setData(new_data); 0268 0269 m_matrix->endMacro(); 0270 RESET_CURSOR; 0271 }