summaryrefslogtreecommitdiffstats
path: root/Project/MathExpression.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Project/MathExpression.cpp')
-rw-r--r--Project/MathExpression.cpp332
1 files changed, 332 insertions, 0 deletions
diff --git a/Project/MathExpression.cpp b/Project/MathExpression.cpp
new file mode 100644
index 0000000..f8be598
--- /dev/null
+++ b/Project/MathExpression.cpp
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2017 Thales Lima Oliveira <thales@ufu.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "ConnectionLine.h"
+#include "MathExpression.h"
+#include "MathExpressionForm.h"
+
+MathExpression::MathExpression(int id) : ControlElement(id)
+{
+ m_variablesVector.push_back("x");
+ m_variablesVector.push_back("y");
+
+ for(unsigned int i = 0; i < m_variablesVector.size(); ++i) {
+ m_glTextInputVector.push_back(new OpenGLText(m_variablesVector[i]));
+ }
+
+ // Symbol
+ m_symbol.SetFontSize(12);
+ m_symbol.SetFontWeight(wxFONTWEIGHT_BOLD);
+ m_symbol.SetFontStyle(wxFONTSTYLE_ITALIC);
+ m_symbol.SetFontFamily(wxFONTFAMILY_ROMAN);
+ m_symbol.SetText("f(x)");
+ m_symbolSize = wxSize(m_symbol.GetWidth(), m_symbol.GetHeight());
+
+ CalculateBlockSize(static_cast<double>(m_variablesVector.size()));
+
+ for(unsigned int i = 0; i < m_variablesVector.size(); ++i) {
+ wxPoint2DDouble nodePosition(0, 0);
+ if(m_variablesVector.size() == 1) {
+ nodePosition = m_position + wxPoint2DDouble(-m_width / 2, 0);
+ } else {
+ nodePosition = m_position + wxPoint2DDouble(-m_width / 2, 9 + 18 * i - m_height / 2);
+ }
+ Node* nodeIn = new Node(nodePosition, Node::NODE_IN, m_borderSize);
+ nodeIn->StartMove(m_position);
+ m_nodeList.push_back(nodeIn);
+ }
+ Node* nodeOut = new Node(m_position + wxPoint2DDouble(m_width / 2, 0), Node::NODE_OUT, m_borderSize);
+ nodeOut->SetAngle(180.0);
+ nodeOut->StartMove(m_position);
+ m_nodeList.push_back(nodeOut);
+
+ UpdatePoints();
+}
+
+MathExpression::~MathExpression()
+{
+ for(auto it = m_glTextInputVector.begin(), itEnd = m_glTextInputVector.end(); it != itEnd; ++it) { delete *it; }
+ m_glTextInputVector.clear();
+}
+
+void MathExpression::Draw(wxPoint2DDouble translation, double scale) const
+{
+ glLineWidth(1.0);
+ if(m_selected) {
+ glColor4dv(m_selectionColour.GetRGBA());
+ double borderSize = (m_borderSize * 2.0 + 1.0) / scale;
+ DrawRectangle(m_position, m_width + borderSize, m_height + borderSize);
+ }
+ glColor4d(1.0, 1.0, 1.0, 1.0);
+ DrawRectangle(m_position, m_width, m_height);
+ glColor4d(0.0, 0.0, 0.0, 1.0);
+ DrawRectangle(m_position, m_width, m_height, GL_LINE_LOOP);
+
+ // Plot input variables and symbol.
+ glColor4d(0.0, 0.3, 1.0, 1.0);
+ if(m_angle == 0.0) {
+ m_symbol.Draw(m_nodeList[m_nodeList.size() - 1]->GetPosition() -
+ wxPoint2DDouble(m_symbolSize.GetWidth() / 2.0 + 6.0, 0));
+ glColor4d(0.0, 0.0, 0.0, 1.0);
+ for(unsigned int i = 0; i < m_glTextInputVector.size(); ++i) {
+ m_glTextInputVector[i]->Draw(m_nodeList[i]->GetPosition() +
+ wxPoint2DDouble(m_glTextInputVector[i]->GetWidth() / 2.0 + 6, 0));
+ }
+ } else if(m_angle == 90.0) {
+ m_symbol.Draw(m_nodeList[m_nodeList.size() - 1]->GetPosition() -
+ wxPoint2DDouble(0, m_symbolSize.GetHeight() / 2.0 + 6.0));
+ glColor4d(0.0, 0.0, 0.0, 1.0);
+ for(unsigned int i = 0; i < m_glTextInputVector.size(); ++i) {
+ m_glTextInputVector[i]->Draw(
+ m_nodeList[i]->GetPosition() +
+ wxPoint2DDouble(m_glTextInputVector[i]->GetWidth() / 2.0 + m_glTextInputVector[i]->GetHeight() / 2,
+ 15),
+ 90);
+ }
+ } else if(m_angle == 180.0) {
+ m_symbol.Draw(m_nodeList[m_nodeList.size() - 1]->GetPosition() +
+ wxPoint2DDouble(m_symbolSize.GetWidth() / 2.0 + 6.0, 0));
+ glColor4d(0.0, 0.0, 0.0, 1.0);
+ for(unsigned int i = 0; i < m_glTextInputVector.size(); ++i) {
+ m_glTextInputVector[i]->Draw(m_nodeList[i]->GetPosition() -
+ wxPoint2DDouble(m_glTextInputVector[i]->GetWidth() / 2.0 + 6, 0));
+ }
+ } else if(m_angle == 270.0) {
+ m_symbol.Draw(m_nodeList[m_nodeList.size() - 1]->GetPosition() +
+ wxPoint2DDouble(0, m_symbolSize.GetHeight() / 2.0 + 6.0));
+ glColor4d(0.0, 0.0, 0.0, 1.0);
+ for(unsigned int i = 0; i < m_glTextInputVector.size(); ++i) {
+ m_glTextInputVector[i]->Draw(
+ m_nodeList[i]->GetPosition() + wxPoint2DDouble(m_glTextInputVector[i]->GetWidth() / 2.0 +
+ m_glTextInputVector[i]->GetHeight() / 2.0,
+ -m_glTextInputVector[i]->GetWidth()),
+ 90);
+ }
+ }
+
+ glColor4d(0.0, 0.0, 0.0, 1.0);
+ DrawNodes();
+}
+
+bool MathExpression::ShowForm(wxWindowMSW* parent, Element* element)
+{
+ MathExpressionForm* mathExprForm = new MathExpressionForm(parent, this);
+ if(mathExprForm->ShowModal() == wxID_OK) {
+ mathExprForm->Destroy();
+ return true;
+ }
+ mathExprForm->Destroy();
+ return false;
+}
+
+void MathExpression::Rotate(bool clockwise)
+{
+ if(clockwise)
+ m_angle += 90.0;
+ else
+ m_angle -= 90.0;
+ if(m_angle >= 360.0)
+ m_angle = 0.0;
+ else if(m_angle < 0)
+ m_angle = 270.0;
+
+ UpdatePoints();
+
+ for(auto it = m_nodeList.begin(), itEnd = m_nodeList.end(); it != itEnd; ++it) {
+ Node* node = *it;
+ node->Rotate(clockwise);
+ }
+}
+
+bool MathExpression::Solve(double* input, double timeStep)
+{
+ if(!input) {
+ m_output = 0.0;
+ return true;
+ }
+ // Get the input vector from connection lines (can't use default (one) input argument)
+ m_inputValues[0] = input[1]; // Current time
+ m_inputValues[1] = timeStep;
+ m_inputValues[2] = input[2]; // Switch status
+ int i = 3;
+ for(auto itN = m_nodeList.begin(), itNEnd = m_nodeList.end(); itN != itNEnd; ++itN) {
+ Node* node = *itN;
+ if(node->GetNodeType() != Node::NODE_OUT) {
+ if(!node->IsConnected()) {
+ m_inputValues[i] = 0.0; // Node not connected means zero value as input.
+ } else {
+ for(auto itC = m_childList.begin(), itCEnd = m_childList.end(); itC != itCEnd; ++itC) {
+ ConnectionLine* cLine = static_cast<ConnectionLine*>(*itC);
+ auto nodeList = cLine->GetNodeList();
+ for(auto itCN = nodeList.begin(), itCNEnd = nodeList.end(); itCN != itCNEnd; ++itCN) {
+ Node* childNode = *itCN;
+ if(childNode == node) {
+ m_inputValues[i] = cLine->GetValue();
+ break;
+ }
+ }
+ }
+ }
+ ++i;
+ }
+ }
+
+ // Solve the math expression using fparser
+ double result = m_fparser.Eval(m_inputValues);
+ if(m_fparser.EvalError() != 0) return false;
+ m_output = result;
+ return true;
+}
+
+Element* MathExpression::GetCopy()
+{
+ MathExpression* copy = new MathExpression(m_elementID);
+ *copy = *this;
+ copy->m_glTextInputVector.clear();
+ for(auto it = m_glTextInputVector.begin(), itEnd = m_glTextInputVector.end(); it != itEnd; ++it) {
+ copy->m_glTextInputVector.push_back((*it)->GetCopy());
+ }
+ return copy;
+}
+
+void MathExpression::UpdatePoints()
+{
+ CalculateBlockSize(static_cast<double>(m_nodeList.size()) - 1.0);
+
+ if(m_nodeList.size() == 2) // Only one input (and the output).
+ {
+ if(m_angle == 0.0)
+ m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(-m_width / 2, 0));
+ else if(m_angle == 90.0)
+ m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(0, -m_height / 2));
+ else if(m_angle == 180.0)
+ m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(m_width / 2, 0));
+ else if(m_angle == 270.0)
+ m_nodeList[0]->SetPosition(m_position + wxPoint2DDouble(0, m_height / 2));
+ } else {
+ for(unsigned int i = 0; i < m_nodeList.size() - 1; ++i) {
+ if(m_angle == 0.0)
+ m_nodeList[i]->SetPosition(m_position + wxPoint2DDouble(-m_width / 2, 9 + 18 * i - m_height / 2));
+ else if(m_angle == 90.0)
+ m_nodeList[i]->SetPosition(m_position + wxPoint2DDouble(m_width / 2 - 9 - 18 * i, -m_height / 2));
+ else if(m_angle == 180.0)
+ m_nodeList[i]->SetPosition(m_position + wxPoint2DDouble(m_width / 2, m_height / 2 - 9 - 18 * i));
+ else if(m_angle == 270.0)
+ m_nodeList[i]->SetPosition(m_position + wxPoint2DDouble(9 + 18 * i - m_width / 2, m_height / 2));
+ }
+ }
+ if(m_angle == 0.0)
+ m_nodeList[m_nodeList.size() - 1]->SetPosition(m_position + wxPoint2DDouble(m_width / 2, 0));
+ else if(m_angle == 90.0)
+ m_nodeList[m_nodeList.size() - 1]->SetPosition(m_position + wxPoint2DDouble(0, m_height / 2));
+ else if(m_angle == 180.0)
+ m_nodeList[m_nodeList.size() - 1]->SetPosition(m_position + wxPoint2DDouble(-m_width / 2, 0));
+ else if(m_angle == 270.0)
+ m_nodeList[m_nodeList.size() - 1]->SetPosition(m_position + wxPoint2DDouble(0, -m_height / 2));
+
+ SetPosition(m_position); // Update rect.
+}
+
+void MathExpression::AddInNode()
+{
+ Node* newNode = new Node(wxPoint2DDouble(0, 0), Node::NODE_IN, m_borderSize);
+ newNode->SetAngle(m_angle);
+ m_nodeList.insert(m_nodeList.end() - 1, newNode);
+}
+
+void MathExpression::RemoveInNode()
+{
+ Node* nodeToRemove = *(m_nodeList.end() - 2);
+ bool foundChild = false;
+ for(auto it = m_childList.begin(), itEnd = m_childList.end(); it != itEnd; ++it) {
+ ControlElement* child = static_cast<ControlElement*>(*it);
+ auto childNodeList = child->GetNodeList();
+ for(auto itN = childNodeList.begin(), itEndN = childNodeList.end(); itN != itEndN; ++itN) {
+ Node* node = *itN;
+ if(node == nodeToRemove) {
+ child->RemoveParent(this);
+ RemoveChild(child);
+ foundChild = true;
+ break;
+ }
+ }
+ if(foundChild) break;
+ }
+ m_nodeList.erase(m_nodeList.end() - 2);
+}
+
+void MathExpression::CalculateBlockSize(double numInNodes)
+{
+ m_maxSringSize = 0;
+ for(auto it = m_glTextInputVector.begin(), itEnd = m_glTextInputVector.end(); it != itEnd; ++it) {
+ if(m_maxSringSize < (*it)->GetWidth()) m_maxSringSize = (*it)->GetWidth();
+ }
+ if(m_angle == 0.0 || m_angle == 180.0) {
+ m_height = 18.0 * numInNodes;
+ if(m_height < m_minimumSize) m_height = m_minimumSize; // minimum height
+ m_width = m_maxSringSize + m_symbolSize.GetWidth() + 18;
+ } else {
+ m_width = 18.0 * numInNodes;
+ if(m_width < m_minimumSize) m_width = m_minimumSize; // minimum width
+ m_height = m_maxSringSize + m_symbolSize.GetHeight() + 18;
+ }
+}
+
+bool MathExpression::UpdateText()
+{
+ bool isTextureOK = true;
+ m_symbol.SetText(m_symbol.GetText());
+ if(!m_symbol.IsTextureOK()) isTextureOK = false;
+ for(auto it = m_glTextInputVector.begin(), itEnd = m_glTextInputVector.end(); it != itEnd; ++it) {
+ (*it)->SetText((*it)->GetText());
+ if(!(*it)->IsTextureOK()) isTextureOK = false;
+ }
+ return isTextureOK;
+}
+
+void MathExpression::SetVariables(std::vector<wxString> variablesVector)
+{
+ m_variablesVector = variablesVector;
+ // Clear old glTextVector
+ for(auto it = m_glTextInputVector.begin(), itEnd = m_glTextInputVector.end(); it != itEnd; ++it) { delete *it; }
+ m_glTextInputVector.clear();
+
+ for(auto it = m_variablesVector.begin(), itEnd = m_variablesVector.end(); it != itEnd; ++it)
+ m_glTextInputVector.push_back(new OpenGLText(*(it)));
+}
+
+bool MathExpression::Initialize()
+{
+ m_variables = "time,step,switch,";
+ for(auto it = m_variablesVector.begin(), itEnd = m_variablesVector.end(); it != itEnd; ++it)
+ m_variables += *(it) + ",";
+ m_variables.RemoveLast();
+
+ // Set locale to ENGLISH_US to avoid parser error in numbers (eg. comma).
+ int currentLang = wxLocale::GetSystemLanguage();
+ wxLocale newLocale(wxLANGUAGE_ENGLISH_US);
+ int parserRes = m_fparser.Parse(static_cast<std::string>(m_mathExpression), static_cast<std::string>(m_variables));
+ if(parserRes != -1) return false; // Parse error.
+ wxLocale oldLocale(currentLang); // Return to current language.
+
+ if(m_inputValues) delete m_inputValues;
+ m_inputValues = new double[m_variablesVector.size() + 3]; // Custom variables + time + step + switch
+
+ // Optimize only once to gain performance.
+ m_fparser.Optimize();
+ return true;
+}