diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index daeb4c2..f43730b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,6 +11,7 @@ This is a brief guide on using **visma(VISualMAth)** and for making any contribu * **Integration** - integrate a polynomial expression wrt a chosen variable * **Differentiation** - differentiate a polynomial expression wrt a chosen variable * **Plot** - plots an interactive 2D or 3D graph +* **Matrix Operations** - This feature will allow you to add, subtract, and multiply two matrices. Can also perform various simplifications on an individual matrix. ![visma](https://raw.githubusercontent.com/wiki/aerospaceresearch/visma/assets/demo.gif) diff --git a/README.md b/README.md index bf4b1a5..50ed8fa 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@

visma - VISual MAth

- + +

A math equation solver and visualizer

diff --git a/main.py b/main.py index e51e48b..67c46a4 100644 --- a/main.py +++ b/main.py @@ -14,7 +14,7 @@ def init(): class VisMa_Prompt(Cmd): '''This inititates the main VisMa Prompt from where user may move to CLI/GUI''' - userManual = "|_________________________________________________________________________________________________|\n"\ + userManual = " _________________________________________________________________________________________________ \n"\ "| gui ->> opens Visma in GUI mode. |\n"\ "| Ctrl + D ->> Closes the prompt. |\n"\ "| exit ->> Closes the prompt. |\n"\ @@ -31,6 +31,10 @@ class VisMa_Prompt(Cmd): "|-------------------------------------------------------------------------------------------------|\n"\ "| integrate( expression , variable ) ->> Integrates the expression by the given variable. |\n"\ "| differentiate( expression , variable ) ->> Differentiates the expression by the given variable. |\n"\ + "|-------------------------------------------------------------------------------------------------|\n"\ + "| Matrices should be input in the following format: |\n"\ + "| Single: mat_([x00 x01; x10 x11]) |\n"\ + "| Double: mat_([x00 x01; x10 x11], [y00 y01; y10 y11]) |\n"\ "|_________________________________________________________________________________________________|\n"\ prompt = '>>> ' diff --git a/tests/test_simplify.py b/tests/test_simplify.py index f6983f6..54f9261 100644 --- a/tests/test_simplify.py +++ b/tests/test_simplify.py @@ -10,6 +10,9 @@ def test_simplify(): + assert quickTest("4 + (3/(3-4)*3)", simplify) == "-5" + + assert quickTest("1 + 2 - 3", simplify) == "0" assert quickTest("1 + 2 - 4", simplify) == "-1.0" assert quickTest("0 + 0 + 1", simplify) == "1.0" diff --git a/visma/functions/structure.py b/visma/functions/structure.py index 8e389ab..4ef7624 100644 --- a/visma/functions/structure.py +++ b/visma/functions/structure.py @@ -1,6 +1,104 @@ import copy +def tokensToString(tokens): + """Converts tokens to text string + + Arguments: + tokens {list} -- list of function tokens + + Returns: + tokenString {string} -- equation string + """ + # FIXME: tokensToString method + # tokenString = '' + # for token in tokens: + # if isinstance(token, Constant): + # if isinstance(token.value, list): + # for j, val in token.value: + # if token['power'][j] != 1: + # tokenString += (str(val) + '^(' + str(token.power[j]) + ')') + # else: + # tokenString += str(val) + # elif isNumber(token.value): + # if token.power != 1: + # tokenString += (str(token.value) + '^(' + str(token.power) + ')') + # else: + # tokenString += str(token.value) + # elif isinstance(token, Variable): + # if token.coefficient == 1: + # pass + # elif token.coefficient == -1: + # tokenString += '-' + # else: + # tokenString += str(token.coefficient) + # for j, val in enumerate(token.value): + # if token.power[j] != 1: + # tokenString += (str(val) + '^(' + str(token.power[j]) + ')') + # else: + # tokenString += str(val) + # elif isinstance(token, Binary): + # tokenString += ' ' + str(token.value) + ' ' + # elif isinstance(token, Expression): + # if token.coefficient != 1: + # tokenString += str(token.coefficient) + '*' + # tokenString += '(' + # tokenString += tokensToString(token.tokens) + # tokenString += ')' + # if token.power != 1: + # tokenString += '^(' + str(token.power) + ')' + # elif isinstance(token, Sqrt): + # tokenString += 'sqrt[' + # if isinstance(token.power, Constant): + # tokenString += tokensToString([token.power]) + # elif isinstance(token.power, Variable): + # tokenString += tokensToString([token.power]) + # elif isinstance(token.power, Expression): + # tokenString += tokensToString(token.power.tokens) + # tokenString += '](' + # if isinstance(token.operand, Constant): + # tokenString += tokensToString([token.operand]) + # elif isinstance(token.operand, Variable): + # tokenString += tokensToString([token.operand]) + # elif isinstance(token.operand, Expression): + # tokenString += tokensToString(token.operand.tokens) + # tokenString += ')' + # elif isinstance(token, Logarithm): + # if token.coefficient == 1: + # pass + # elif token.coefficient == -1: + # tokenString += '-' + # else: + # tokenString += str(token.coefficient) + # if token.operand is not None: + # tokenString += token.value + # if token.power != 1: + # tokenString += "^" + "(" + str(token.power) + ")" + # tokenString += "(" + tokensToString([token.operand]) + ")" + # elif isinstance(token, Trigonometric): + # if token.coefficient == 1: + # pass + # elif token.coefficient == -1: + # tokenString += '-' + # else: + # tokenString += str(token.coefficient) + # if token.operand is not None: + # tokenString += token.value + # if token.power != 1: + # tokenString += "^" + "(" + str(token.power) + ")" + # tokenString += "(" + tokensToString([token.operand]) + ")" + # elif isinstance(token, Matrix): + # tokenString += "[" + # for i in range(token.dim[0]): + # for j in range(token.dim[1]): + # tokenString += tokensToString(token.value[i][j]) + # tokenString += "," + # tokenString = tokenString[:-1] + ";" + # tokenString = tokenString[:-1] + "]" + # + # return tokenString + + class Function(object): """Basis function class for all functions diff --git a/visma/gui/cli.py b/visma/gui/cli.py index 0532e4d..cdf01f6 100644 --- a/visma/gui/cli.py +++ b/visma/gui/cli.py @@ -1,6 +1,7 @@ import copy import sys from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QTabWidget, QVBoxLayout +from PyQt5.QtCore import Qt from visma.calculus.differentiation import differentiate from visma.calculus.integration import integrate from visma.discreteMaths.combinatorics import factorial, combination, permutation diff --git a/visma/gui/logger.py b/visma/gui/logger.py index 8f550d4..f4ee87e 100644 --- a/visma/gui/logger.py +++ b/visma/gui/logger.py @@ -16,6 +16,7 @@ def logTextBox(workspace): workspace.logBox = QTextEdit() workspace.logBox.setReadOnly(True) + workspace.logBox.setStyleSheet("background-color: rgb(210, 210, 210)") # colors inside of logger textLayout = QVBoxLayout() textLayout.addWidget(workspace.logBox) return textLayout diff --git a/visma/gui/plotter.py b/visma/gui/plotter.py index 562964b..06c7631 100644 --- a/visma/gui/plotter.py +++ b/visma/gui/plotter.py @@ -193,6 +193,8 @@ class NavigationCustomToolbar(NavigationToolbar): layout = QVBoxLayout() layout.addWidget(workspace.canvas2D) layout.addWidget(workspace.toolbar2D) + workspace.figure2D.set_facecolor("none") # makes color transparent + workspace.canvas2D.setStyleSheet("background-color: rgb(210, 210, 210)") return layout @@ -216,6 +218,9 @@ class NavigationCustomToolbar(NavigationToolbar): layout = QVBoxLayout() layout.addWidget(workspace.canvas3D) layout.addWidget(workspace.toolbar3D) + workspace.figure3D.set_facecolor("none") # makes color transparent + workspace.canvas3D.setStyleSheet("background-color: rgb(210, 210, 210)") + return layout @@ -307,6 +312,7 @@ def plot(workspace, tokens=None): renderPlot(workspace, graphVars, func, variables, tokens) + def selectAdditionalVariable(var1): if var1 == 'z': var2 = 'a' diff --git a/visma/gui/qsolver.py b/visma/gui/qsolver.py index adfccaf..9a6e36f 100644 --- a/visma/gui/qsolver.py +++ b/visma/gui/qsolver.py @@ -81,8 +81,7 @@ def qSolveFigure(workspace): """ bg = workspace.palette().window().color() - bgcolor = (bg.redF(), bg.greenF(), bg.blueF()) - workspace.qSolveFigure = Figure(edgecolor=bgcolor, facecolor=bgcolor) + workspace.qSolveFigure = Figure(edgecolor="none", facecolor="none") # Make transparent so parent can override background workspace.solcanvas = FigureCanvas(workspace.qSolveFigure) workspace.qSolveFigure.clear() qSolLayout = QtWidgets.QVBoxLayout() diff --git a/visma/gui/steps.py b/visma/gui/steps.py index 3adf3a7..e438acb 100644 --- a/visma/gui/steps.py +++ b/visma/gui/steps.py @@ -18,9 +18,11 @@ def stepsFigure(workspace): """ workspace.stepsfigure = Figure() workspace.stepscanvas = FigureCanvas(workspace.stepsfigure) + workspace.stepsfigure.set_facecolor("none") # make figure transparent so that background of scrollbar is visible workspace.stepsfigure.clear() workspace.scroll = QScrollArea() workspace.scroll.setWidget(workspace.stepscanvas) + workspace.scroll.setStyleSheet("background-color: rgb(210, 210, 210)") # color background of scrollbar stepslayout = QVBoxLayout() stepslayout.addWidget(workspace.scroll) return stepslayout @@ -37,7 +39,7 @@ def showSteps(workspace): verticalalignment='top', size=qApp.font().pointSize()*workspace.stepsFontSize) workspace.stepscanvas.draw() hbar = workspace.scroll.horizontalScrollBar() - hbar.setValue((hbar.minimum()+hbar.maximum())/2) + hbar.setValue((hbar.minimum()+hbar.maximum())//2) ############### diff --git a/visma/gui/window.py b/visma/gui/window.py index 8454172..8cb0ebc 100644 --- a/visma/gui/window.py +++ b/visma/gui/window.py @@ -35,7 +35,7 @@ from visma.solvers.simulEqn import simulSolver from visma.transform.factorization import factorize from visma.gui import logger - +from PyQt5.QtGui import QPalette, QColor class Window(QtWidgets.QMainWindow): @@ -77,6 +77,8 @@ def initUI(self): helpMenu = menubar.addMenu('&Help') helpMenu.addAction(wikiAction) self.workSpace = WorkSpace() + self.setStyleSheet('background-color: rgb(90, 90, 90);') + menubar.setStyleSheet("background-color: rgb(210, 210, 210)") self.setCentralWidget(self.workSpace) self.GUIwidth = 1300 self.GUIheight = 900 @@ -113,7 +115,7 @@ def loadEquations(self): class WorkSpace(QWidget): - inputGreek = ['x', 'y', 'z', '(', ')', '7', '8', '9', 'DEL', 'C', 'f', 'g', 'h', '{', '}', '4', '5', '6', '/', '*', 'sin', 'cos', 'tan', '[', ']', '1', '2', '3', '+', '-', 'log', 'exp', '^', 'i', u'\u03C0', '.', '0', '=', '<', '>'] + inputGreek = ['x', 'y', 'z', '(', ')', '7', '8', '9', 'DEL', 'C', 'f', 'g', 'h', '{', '}', '4', '5', '6', '/', '*', 'sin', 'cos', 'tan', '[', ']', '1', '2', '3', '+', '-', 'log', 'exp', '^', '<', '>', '.', '0', '=', 'i', 'Ans'] inputLaTeX = ['x', 'y', 'z', '(', ')', '7', '8', '9', 'DEL', 'C', 'f', 'g', 'h', '{', '}', '4', '5', '6', '\\div', '\\times', '\\sin', '\\cos', '\\tan', '[', ']', '1', '2', '3', '+', '-', 'log', 'exp', '^', 'i', '\\pi', '.', '0', '=', '<', '>'] mode = 'interaction' @@ -164,15 +166,31 @@ def __init__(self): def initUI(self): hbox = QHBoxLayout(self) + #self.setStyleSheet("border-color: rgb(60, 60, 60);") # changes color of nearly everything to blue + #self.setStyleSheet("color: lightblue") # changes color of all text to blue + #self.setStyleSheet("border: black") # removes button colors + # self.setStyleSheet(""" + # background-color: rgb(90, 90, 90); + # border-color: rgb(90, 90, 90); + # """ + # ) # changes nothing basically self.equationList = QTabWidget() self.equationList.tab1 = QWidget() # self.equationList.tab2 = QWidget() self.equationList.addTab(self.equationList.tab1, "History") # self.equationList.addTab(self.equationList.tab2, "favourites") - self.equationList.tab1.setLayout(self.equationsLayout()) + + + self.equationList.tab1.setStyleSheet("background-color: rgb(120, 120, 120)") # colors border of history widget + self.equationList.tab1.setLayout(self.equationsLayout()) # color modified + self.myQListWidget.setStyleSheet("background-color: rgb(210, 210, 210);") # colors inside of widget + self.clearButton.setStyleSheet("background-color: rgb(210, 210, 210)") # colors button + + self.equationList.tab1.setStatusTip("Track of old equations") self.equationList.setFixedWidth(300) + self.equationList.setDocumentMode(True) # removes white borders inputSpace = QTabWidget() inputSpace.tab1 = QWidget() @@ -183,12 +201,15 @@ def initUI(self): inputSpace.tab2.setLayout(preferenceLayout(self)) inputSpace.tab1.setStatusTip("Input characters") inputSpace.setFixedHeight(200) + inputSpace.tab1.setStyleSheet("background-color: rgb(120, 120, 120)") # colors border of step by step + inputSpace.tab2.setStyleSheet("background-color: rgb(210, 210, 210)") # colors border of logger + inputSpace.setDocumentMode(True) # removes white borders buttonSpace = QWidget() buttonSpace.setLayout(self.buttonsLayout()) buttonSpace.setFixedWidth(300) buttonSpace.setStatusTip("Interact") - + self.tabPlot = QTabWidget() self.tabPlot.tab1 = QWidget() self.tabPlot.tab2 = QWidget() @@ -198,6 +219,10 @@ def initUI(self): self.tabPlot.tab1.setStatusTip("Visualize equation in 2D") self.tabPlot.tab2.setLayout(plotFigure3D(self)) self.tabPlot.tab2.setStatusTip("Visualize equation in 3D") + self.tabPlot.tab1.setStyleSheet("background-color: rgb(120, 120, 120)") # colors 2D plots + self.tabPlot.tab2.setStyleSheet("background-color: rgb(120, 120, 120)") # colors 3D plots + + self.tabPlot.setDocumentMode(True) # removes white borders tabStepsLogs = QTabWidget() tabStepsLogs.tab1 = QWidget() @@ -208,6 +233,10 @@ def initUI(self): tabStepsLogs.tab1.setStatusTip("Step-by-step solver") tabStepsLogs.tab2.setLayout(logger.logTextBox(self)) tabStepsLogs.tab2.setStatusTip("Logger") + tabStepsLogs.tab1.setStyleSheet("background-color: rgb(120, 120, 120)") + tabStepsLogs.tab2.setStyleSheet("background-color: rgb(120, 120, 120)") + + tabStepsLogs.setDocumentMode(True) # removes white borders font = QtGui.QFont() font.setPointSize(16) @@ -222,6 +251,7 @@ def initUI(self): quickSolve.setLayout(qSolveFigure(self)) quickSolve.setFixedHeight(45) quickSolve.setStatusTip("Quick solver") + quickSolve.setStyleSheet("background-color: rgb(150, 150, 150)") splitter4 = QSplitter(Qt.Vertical) splitter4.addWidget(self.textedit) @@ -244,8 +274,21 @@ def initUI(self): hbox.addWidget(splitter1) self.setLayout(hbox) + self.previousAnswer = '' + + self.textedit.setStyleSheet("background-color: rgb(210, 210, 210)") + + self.setStyleSheet( + """ + background-color: rgb(90, 90, 90); + border-color: rgb(90, 90, 90); + """ + ) + self.logBox.append(logger.info('UI Initialised...')) + + def textChangeTrigger(self): self.enableInteraction = True self.clearButtons() @@ -283,6 +326,7 @@ def clearAll(self): def equationsLayout(self): self.myQListWidget = QtWidgets.QListWidget(self) + for index, name in self.equations: myQCustomQWidget = QCustomQWidget() myQCustomQWidget.setTextUp(index) @@ -292,6 +336,7 @@ def equationsLayout(self): self.myQListWidget.addItem(myQListWidgetItem) self.myQListWidget.setItemWidget( myQListWidgetItem, myQCustomQWidget) + myQCustomQWidget.setStyleSheet("background-color: rgb(210, 210, 210);") # colors border around equations self.myQListWidget.resize(400, 300) self.equationListVbox.addWidget(self.myQListWidget) self.myQListWidget.itemClicked.connect(self.Clicked) @@ -325,6 +370,7 @@ def clearHistory(self): self.myQListWidget.setItemWidget( myQListWidgetItem, myQCustomQWidget) i += 1 + myQCustomQWidget.setStyleSheet("background-color: rgb(210, 210, 210);") # colors equation border on every iteration file.close() self.myQListWidget.resize(400, 300) self.myQListWidget.itemClicked.connect(self.Clicked) @@ -332,6 +378,8 @@ def clearHistory(self): self.clearButton = QtWidgets.QPushButton('Clear equations') self.clearButton.clicked.connect(self.clearHistory) self.equationListVbox.addWidget(self.clearButton) + self.myQListWidget.setStyleSheet("background-color: rgb(210, 210, 210);") # colors inside of widget again + self.clearButton.setStyleSheet("background-color: rgb(210, 210, 210)") # colors button again return self.equationListVbox def Clicked(self, item): @@ -344,6 +392,7 @@ def buttonsLayout(self): interactionModeLayout = QVBoxLayout() self.interactionModeButton = QtWidgets.QPushButton('visma') self.interactionModeButton.clicked.connect(self.interactionMode) + self.interactionModeButton.setEnabled(False) interactionModeLayout.addWidget(self.interactionModeButton) interactionModeWidget = QWidget(self) interactionModeWidget.setLayout(interactionModeLayout) @@ -357,6 +406,12 @@ def buttonsLayout(self): self.buttonSplitter.addWidget(topButtonSplitter) self.buttonSplitter.addWidget(self.bottomButton) vbox.addWidget(self.buttonSplitter) + self.interactionModeButton.setStyleSheet( + """ + background-color: rgb(210, 210, 210); + font-size: 16px; + """ + ) return vbox def interactionMode(self): @@ -416,12 +471,14 @@ def interactionMode(self): operations, self.solutionType = checkTypes(lhs, rhs) if isinstance(operations, list) and showbuttons: opButtons = [] + if 'differentiate' in operations: + opButtons.append('graph') if len(operations) > 0: if len(operations) == 1: if (operations[0] not in ['integrate', 'differentiate', 'find roots', 'factorize']) and (not self.simul): - opButtons = ['simplify'] + opButtons.append('simplify') else: - opButtons = ['simplify'] + opButtons.append('simplify') for operation in operations: if operation == '+': opButtons.append("addition") @@ -503,6 +560,12 @@ def interactionMode(self): self.onSolvePress(opButtons[i * 2 + j])) self.solutionOptionsBox.addWidget( self.solutionButtons[(i, j)], i, j) + self.solutionButtons[(i, j)].setStyleSheet( + """ + background-color: rgb(210, 210, 210); + font-size: 16px; + """ + ) else: self.bottomButton.setParent(None) self.solutionWidget = QWidget() @@ -516,6 +579,12 @@ def interactionMode(self): self.onSolvePress(opButtons[i * 2 + j])) self.solutionOptionsBox.addWidget( self.solutionButtons[(i, j)], i, j) + self.solutionButtons[(i, j)].setStyleSheet( + """ + background-color: rgb(210, 210, 210); + font-size: 16px; + """ + ) self.solutionWidget.setLayout(self.solutionOptionsBox) self.buttonSplitter.addWidget(self.solutionWidget) self.buttonSet = True @@ -523,12 +592,14 @@ def interactionMode(self): def refreshButtons(self, operations): if isinstance(operations, list): opButtons = [] + if 'differentiate' in operations: + opButtons.append('graph') if len(operations) > 0: if len(operations) == 1: if operations[0] == 'solve': - opButtons = ['simplify'] + opButtons.append('simplify') else: - opButtons = ['simplify'] + opButtons.append('simplify') for operation in operations: if operation == '+': opButtons.append("addition") @@ -610,6 +681,7 @@ def addEquation(self): self.myQListWidget.setItemWidget( myQListWidgetItem, myQCustomQWidget) i += 1 + myQCustomQWidget.setStyleSheet("background-color: rgb(210, 210, 210);") file.close() self.myQListWidget.resize(400, 300) self.myQListWidget.itemClicked.connect(self.Clicked) @@ -618,6 +690,8 @@ def addEquation(self): self.clearButton = QtWidgets.QPushButton('Clear equations') self.clearButton.clicked.connect(self.clearHistory) self.equationListVbox.addWidget(self.clearButton) + self.myQListWidget.setStyleSheet("background-color: rgb(210, 210, 210);") # colors inside of widget + self.clearButton.setStyleSheet("background-color: rgb(210, 210, 210)") # colors button return self.equationListVbox def inputsLayout(self, loadList="Greek"): @@ -630,6 +704,7 @@ def inputsLayout(self, loadList="Greek"): if (i * 10 + j) < len(self.inputGreek): self.buttons[(i, j)] = QtWidgets.QPushButton( self.inputGreek[i * 10 + j]) + self.checkForColorChange(self.buttons[(i, j)]) # color change function self.buttons[(i, j)].resize(100, 100) self.buttons[(i, j)].clicked.connect( self.onInputPress(self.inputGreek[i * 10 + j])) @@ -647,8 +722,33 @@ def inputsLayout(self, loadList="Greek"): # inputSplitter.addWidget(inputTypeSplitter) # inputSplitter.addWidget(inputWidget) inputLayout.addWidget(inputWidget) + inputWidget.setStyleSheet("background-color: rgb(120, 120, 120);") # colors the space around input buttons + return inputLayout + def checkForColorChange(self, button): # changes button color + color = "rgb(210, 210, 210)" + bold = "normal" + match button.text(): + case "Ans": + color = "green" + bold = "bold" + case "C" | "DEL": + color = "red" + bold = "bold" + case "+" | "*" | "/" | "-": + color = "orange" + bold = "bold" + + button.setStyleSheet(f""" + background-color: {color}; + font-weight: {bold}; + font-size: 16px; + """ + ) + + + def onActivated(self, text): for i in reversed(range(self.inputBox.count())): self.inputBox.itemAt(i).widget().setParent(None) @@ -679,6 +779,8 @@ def calluser(): elif name == 'DEL': cursor = self.textedit.textCursor() cursor.deletePreviousChar() + elif name == 'Ans': + self.textedit.insertPlainText(self.previousAnswer) else: self.textedit.insertPlainText(str(name)) return calluser @@ -761,6 +863,13 @@ def calluser(): variables = getVariables(lhs, rhs) self.wrtVariableButtons(variables, name) self.resultOut = False + elif name == 'graph': + if self.solutionType == 'expression': + self.tokens, availableOperations, tokenString, equationTokens, comments = simplify(self.tokens) + else: + self.lTokens, self.rTokens, availableOperations, tokenString, equationTokens, comments = simplifyEquation(self.lTokens, self.rTokens) + self.showPlotter = True + self.previousAnswer = tokenString else: """ This part handles the cases when VisMa is dealing with matrices. @@ -824,6 +933,7 @@ def calluser(): showSteps(self) if self.showPlotter is True: plot(self) + self.previousAnswer = tokenString else: if self.dualOperandMatrix: if not self.scalarOperationsMatrix: @@ -844,6 +954,7 @@ def calluser(): cursor.insertText(tokenString) if self.showStepByStep is True: showSteps(self) + self.previousAnswer = tokenString return calluser def onWRTVariablePress(self, varName, operation): @@ -881,7 +992,6 @@ def calluser(): self.rTokens = rhs operations, self.solutionType = checkTypes(lhs, rhs) self.refreshButtons(operations) - else: if operation == 'solve': if not self.simul: @@ -938,10 +1048,10 @@ def __init__(self, parent=None): self.setLayout(self.allQHBoxLayout) self.textUpQLabel.setStyleSheet(''' color: black; - ''') + ''') # Colors the text in history tab: the equation number self.textDownQLabel.setStyleSheet(''' color: black; - ''') + ''') # Colors the text in history tab: the input def setTextUp(self, text): self.textUpQLabel.setText(text) diff --git a/visma/io/checks.py b/visma/io/checks.py index 5faa405..4486c1a 100644 --- a/visma/io/checks.py +++ b/visma/io/checks.py @@ -1,11 +1,13 @@ import math import copy from visma.config.values import ROUNDOFF +from visma.functions.exponential import Logarithm from visma.functions.structure import Function, Expression from visma.functions.constant import Constant +from visma.functions.trigonometry import Trigonometric from visma.functions.variable import Variable -from visma.functions.operator import Operator, Binary - +from visma.functions.operator import Operator, Binary, Sqrt +from visma.matrix.structure import Matrix greek = [u'\u03B1', u'\u03B2', u'\u03B3'] @@ -94,6 +96,105 @@ def isEquation(lTokens, rTokens): return False +def tokensToString(tokens): + """Converts tokens to text string + + Arguments: + tokens {list} -- list of function tokens + + Returns: + tokenString {string} -- equation string + """ + # FIXME: tokensToString method + tokenString = '' + for token in tokens: + if isinstance(token, Constant): + if isinstance(token.value, list): + for j, val in token.value: + if token['power'][j] != 1: + tokenString += (str(val) + '^(' + str(token.power[j]) + ')') + else: + tokenString += str(val) + elif isNumber(token.value): + if token.power != 1: + tokenString += (str(token.value) + '^(' + str(token.power) + ')') + else: + tokenString += str(token.value) + elif isinstance(token, Variable): + if token.coefficient == 1: + pass + elif token.coefficient == -1: + tokenString += '-' + else: + tokenString += str(token.coefficient) + for j, val in enumerate(token.value): + if token.power[j] != 1: + tokenString += (str(val) + '^(' + str(token.power[j]) + ')') + else: + tokenString += str(val) + elif isinstance(token, Binary): + tokenString += ' ' + str(token.value) + ' ' + elif isinstance(token, Expression): + if token.coefficient != 1: + tokenString += str(token.coefficient) + '*' + tokenString += '(' + tokenString += tokensToString(token.tokens) + tokenString += ')' + if token.power != 1: + tokenString += '^(' + str(token.power) + ')' + elif isinstance(token, Sqrt): + tokenString += 'sqrt[' + if isinstance(token.power, Constant): + tokenString += tokensToString([token.power]) + elif isinstance(token.power, Variable): + tokenString += tokensToString([token.power]) + elif isinstance(token.power, Expression): + tokenString += tokensToString(token.power.tokens) + tokenString += '](' + if isinstance(token.operand, Constant): + tokenString += tokensToString([token.operand]) + elif isinstance(token.operand, Variable): + tokenString += tokensToString([token.operand]) + elif isinstance(token.operand, Expression): + tokenString += tokensToString(token.operand.tokens) + tokenString += ')' + elif isinstance(token, Logarithm): + if token.coefficient == 1: + pass + elif token.coefficient == -1: + tokenString += '-' + else: + tokenString += str(token.coefficient) + if token.operand is not None: + tokenString += token.value + if token.power != 1: + tokenString += "^" + "(" + str(token.power) + ")" + tokenString += "(" + tokensToString([token.operand]) + ")" + elif isinstance(token, Trigonometric): + if token.coefficient == 1: + pass + elif token.coefficient == -1: + tokenString += '-' + else: + tokenString += str(token.coefficient) + if token.operand is not None: + tokenString += token.value + if token.power != 1: + tokenString += "^" + "(" + str(token.power) + ")" + tokenString += "(" + tokensToString([token.operand]) + ")" + elif isinstance(token, Matrix): + tokenString += "[" + for i in range(token.dim[0]): + for j in range(token.dim[1]): + tokenString += tokensToString(token.value[i][j]) + tokenString += "," + tokenString = tokenString[:-1] + ";" + tokenString = tokenString[:-1] + "]" + + return tokenString + + + def getVariables(lTokens, rTokens=None, variables=None): """Finds all the variables present in the expression @@ -107,6 +208,7 @@ def getVariables(lTokens, rTokens=None, variables=None): Returns: variables {list} -- list of variables """ + #print("Alpha: "+tokensToString(lTokens)) if rTokens is None: rTokens = [] if variables is None: @@ -126,6 +228,8 @@ def getVariables(lTokens, rTokens=None, variables=None): variables.append(val) elif isinstance(token, Expression): variables.extend(getVariables(token.tokens, [], variables)) + #print("Beta: "+str(variables)) + #print() return variables diff --git a/visma/io/parser.py b/visma/io/parser.py index f6120cc..6bcf94a 100644 --- a/visma/io/parser.py +++ b/visma/io/parser.py @@ -36,22 +36,75 @@ def resultLatex(equationTokens, operation, comments, solutionType, simul=False, else: finalSteps = 'INPUT: ' + '(Multiple ' + r'$' + ' equations)' + r'$' + '\n' finalSteps += 'OPERATION: ' + operation + '\n' - finalSteps += 'OUTPUT: ' + r'$' + equationLatex[-1] + r'$' + 2*'\n' + # print(equationLatex[-1]) + roundedStep = roundEquationLatexOutput(equationLatex, -1, 6) + # print(equationLatex[-1]) + finalSteps += 'OUTPUT: ' + r'$' + roundedStep + r'$' + 2*'\n' + # finalSteps += 'OUTPUT: ' + r'$' + equationLatex[-1] + r'$' + 2*'\n' for i, _ in enumerate(equationLatex): if comments[i] != [] and equationLatex[i] != '': finalSteps += '(' + str(comments[i][0]) + ')' + '\n' - finalSteps += r'$' + equationLatex[i] + r'$' + 2*"\n" + if i == len(equationLatex) - 1: + # print(equationLatex[i]) + roundedStep = roundEquationLatexOutput(equationLatex, i, 6) + # print(equationLatex[i]) + pass + else: + # print(equationLatex[i]) + roundedStep = roundEquationLatexOutput(equationLatex, i, 2) + # print(equationLatex[i]) + pass + finalSteps += r'$' + roundedStep + r'$' + 2*"\n" + # finalSteps += '\n' + r'$' + equationLatex[-1] + r'$' + 2*'\n' elif comments[i] != [] and equationLatex[i] == '': finalSteps += '\n' + '[' + str(comments[i][0]) + ']' + '\n' elif comments[i] == [] and equationLatex[i] != '': - finalSteps += '\n' + r'$' + equationLatex[i] + r'$' + 2*'\n' + if i == len(equationLatex) - 1: + # print(equationLatex[i]) + roundedStep = roundEquationLatexOutput(equationLatex, i, 6) + # print(equationLatex[i]) + pass + else: + # print(equationLatex[i]) + roundedStep = roundEquationLatexOutput(equationLatex, i, 2) + # print(equationLatex[i]) + pass + finalSteps += '\n' + r'$' + roundedStep + r'$' + 2*'\n' + # finalSteps += '\n' + r'$' + equationLatex[i] + r'$' + 2*'\n' if mathError(equationTokens[-1]) and (not simul): finalSteps += 'Math Error: LHS not equal to RHS' + "\n" return finalSteps +def roundEquationLatexOutput(equationLatex, index, roundLength): + equationSlice = equationLatex[index][0:] + while '{' in equationSlice: + openBracketIndex = equationSlice.index("{") + closeBracketIndex = equationSlice.index("}") + temp_openBracketIndex = openBracketIndex + while '{' in equationSlice[temp_openBracketIndex + 1: closeBracketIndex] and closeBracketIndex != len(equationSlice) - 1: + temp_openBracketIndex = equationSlice[openBracketIndex + 1: closeBracketIndex].index('{') + openBracketIndex + 1 + closeBracketIndex = equationSlice[closeBracketIndex + 1:].index('}') + closeBracketIndex + 1 + # print(equationSlice[openBracketIndex:closeBracketIndex + 1]) + # print(openBracketIndex, closeBracketIndex) + if not equationSlice[closeBracketIndex - 1].isnumeric(): + value = '' + try: + value = round(float(equationSlice[:openBracketIndex]), roundLength) + except ValueError: + pass + equationLatex[index] = equationLatex[index][0:equationLatex[index].index(equationSlice)] \ + + str(value) + equationSlice[openBracketIndex:] + equationSlice = equationSlice[closeBracketIndex + 1:] + pass + else: + value = round(float(equationSlice[openBracketIndex + 1:closeBracketIndex]), roundLength) + equationLatex[index] = equationLatex[index][0:equationLatex[index].index(equationSlice)] \ + + equationSlice[0: openBracketIndex] + '{' + str(value) + '}' + equationSlice[closeBracketIndex + 1:] + equationSlice = equationSlice[closeBracketIndex + 1:] + return equationLatex[index] def resultStringCLI(equationTokens, operation, comments, solutionType, simul=False, mat=False): """Converts tokens to final string format for displaying in terminal in CLI diff --git a/visma/io/tokenize.py b/visma/io/tokenize.py index d0a0ac7..49dffbc 100644 --- a/visma/io/tokenize.py +++ b/visma/io/tokenize.py @@ -25,7 +25,6 @@ from visma.matrix.structure import Matrix from visma.matrix.checks import isMatrix from visma.io.parser import latexToTerms -# from visma.gui import logger symbols = ['+', '-', '*', '/', '(', ')', '{', '}', '[', ']', '^', '=', '<', '>', '<=', '>=', ',', ';', '$'] greek = [u'\u03B1', u'\u03B2', u'\u03B3'] @@ -1379,7 +1378,6 @@ def tokenizer(eqnString): _, tokens = constantConversion(preprocess(eqnString)) return tokens - def changeToken(tokens, variables, scope_times=0): if len(variables) != 0: diff --git a/visma/simplify/simplify.py b/visma/simplify/simplify.py index 2b951ac..9ea3253 100644 --- a/visma/simplify/simplify.py +++ b/visma/simplify/simplify.py @@ -250,18 +250,21 @@ def expressionSimplification(tokens_now, scope, tokens1): if len(tokens1[i + 1].tokens) == 1 and isinstance(tokens1[i + 1].tokens[0], Constant): tokens1[i + 1] = Constant(tokens1[i + 1].tokens[0].calculate(), 1, 1) if (isinstance(tokens1[i], Binary) and tokens1[i].value == '^') and isinstance(tokens1[i + 1], Constant): - if float(tokens1[i + 1].calculate()).is_integer(): - rep = int(tokens1[i + 1].calculate()) - for _ in range(rep - 1): - pfTokens.extend([Binary('*'), tokens1[i - 1]]) - i += 1 - else: - pfTokens.append(tokens1[i]) + value = str(tokens1[i - 1])[1:len(str(tokens1[i - 1])) - 1] + value = float(value) + value **= tokens1[i + 1].calculate() + tokens1[i + 1] = Constant(value, 1, 1) + + del pfTokens[len(pfTokens) - 1] + pfTokens.append(tokens1[i + 1]) + del tokens1[i] + del tokens1[i - 1] else: pfTokens.append(tokens1[i]) else: pfTokens.append(tokens1[i]) i += 1 + tokens1 = copy.deepcopy(pfTokens) animation.append(pfTokens) comments.append(['Expanding the powers of expressions']) @@ -440,68 +443,3 @@ def simplifification(tokens): tokens, animation = postSimplification(tokens, animation) token_string = tokensToString(tokens) return tokens, availableOperations, token_string, animation, comments - - -''' -def defineScopeVariable(variable, scope): - token = copy.deepcopy(variable) - local_scope = copy.deepcopy(scope) - if isinstance(token.value, list): - for j, val in enumerate(token.value): - if val.__class__ in [Binary, Variable, Constant, Expression]: - local_scope_value = copy.deepcopy(local_scope) - local_scope_value.extend(-1) - local_scope_value.extend(j) - val.scope = local_scope_value - - if isinstance(token.power, list): - for j, val in enumerate(token.value): - if val.__class__ in [Binary, Variable, Constant, Expression]: - local_scope_value = copy.deepcopy(local_scope) - local_scope_value.extend(-2) - local_scope_value.extend(j) - val.scope = local_scope_value - - return token - - -def defineScopeConstant(constant, scope): - token = copy.deepcopy(constant) - local_scope = copy.deepcopy(scope) - if isinstance(token.value, list): - for j, val in enumerate(token.value): - if val.__class__ in [Binary, Variable, Constant, Expression]: - local_scope_value = copy.deepcopy(local_scope) - local_scope_value.extend(-1) - local_scope_value.extend(j) - val.scope = local_scope_value - - if isinstance(token.power, list): - for j, val in enumerate(token.value): - if val.__class__ in [Binary, Variable, Constant, Expression]: - local_scope_value = copy.deepcopy(local_scope) - local_scope_value.extend(-2) - local_scope_value.extend(j) - val.scope = local_scope_value - return token - - -def defineScope(tokens, scope=None): - if scope is None: - scope = [] - i = 0 - for token in tokens: - local_scope = copy.deepcopy(scope) - local_scope.extend(i) - token.scope = local_scope - if isinstance(token, Variable): - token = defineScopeVariable(token, copy.deepcopy(local_scope)) - elif isinstance(token, Constant): - token = defineScopeConstant(token, copy.deepcopy(local_scope)) - elif isinstance(token, Expression): - token.tokens = defineScope(token.tokens, local_scope) - elif isinstance(token, Binary): - pass - i += 1 - return tokens -'''