From 6b535dd8a256e412f453fc63111a5cc3dde1f40b Mon Sep 17 00:00:00 2001 From: Ad5001 Date: Mon, 17 Oct 2022 22:30:59 +0200 Subject: [PATCH] Adding object properties usable in expressions --- .../qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js | 6 +++++- .../eu/ad5001/LogarithmPlotter/js/math/common.js | 13 +++++++++---- .../ad5001/LogarithmPlotter/js/math/expression.js | 10 ++++++---- .../eu/ad5001/LogarithmPlotter/js/math/sequence.js | 9 +++++---- .../qml/eu/ad5001/LogarithmPlotter/js/objects.js | 7 ++++--- .../eu/ad5001/LogarithmPlotter/js/objs/common.js | 2 +- 6 files changed, 30 insertions(+), 17 deletions(-) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js index 9253edc..23b164f 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js @@ -194,6 +194,7 @@ function evaluate(tokens, expr, values) { nstack.push(f(resolveExpression(n1, values), resolveExpression(n2, values), resolveExpression(n3, values))); } } else if (type === IVAR) { + // Check for variable value if (item.value in expr.functions) { nstack.push(expr.functions[item.value]); } else if (item.value in expr.unaryOps && expr.parser.isOperatorEnabled(item.value)) { @@ -219,8 +220,11 @@ function evaluate(tokens, expr, values) { f = nstack.pop(); if (f.apply && f.call) { nstack.push(f.apply(undefined, args)); + } else if(f.execute) { + // Objects & expressions execution + nstack.push(f.execute.apply(undefined, args)); } else { - throw new Error(f + ' is not a function'); + throw new Error(f + ' cannot be executed'); } } else if (type === IFUNDEF) { // Create closure to keep references to arguments and expression diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js index 4cadcf5..80f9402 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js @@ -22,7 +22,9 @@ .import "../utils.js" as Utils .import "latex.js" as Latex -var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manualy +const DERIVATION_PRECISION = 0.1 + +var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manually "pi": Math.PI, "π": Math.PI, "inf": Infinity, @@ -33,18 +35,21 @@ var evalVariables = { // Variables not provided by expr-eval.js, needs to be pro } var currentVars = {} +var currentObjectsByName = {} // Mirror of currentObjectsByName in objects.js const parser = new ExprEval.Parser() + +parser.consts = Object.assign({}, parser.consts, evalVariables) + +// Function definition parser.functions.integral = function(a, b, f, variable) { // https://en.wikipedia.org/wiki/Simpson%27s_rule + // Simpler, faster than tokenizing the expression f = parser.parse(f).toJSFunction(variable, currentVars) return (b-a)/6*(f(a)+4*f((a+b)/2)+f(b)) } -const DERIVATION_PRECISION = 0.1 - parser.functions.derivative = function(f, variable, x) { f = parser.parse(f).toJSFunction(variable, currentVars) return (f(x+DERIVATION_PRECISION/2)-f(x-DERIVATION_PRECISION/2))/DERIVATION_PRECISION } - diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js index 58c4528..6007a3b 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js @@ -30,23 +30,25 @@ class Expression { this.expr = expr this.calc = C.parser.parse(expr).simplify() this.cached = this.isConstant() - this.cachedValue = this.cached ? this.calc.evaluate(C.evalVariables) : null + this.cachedValue = this.cached ? this.calc.evaluate(C.currentObjectsByName) : null this.latexMarkup = Latex.expression(this.calc.tokens) } isConstant() { - return !this.expr.includes("x") && !this.expr.includes("n") + let vars = this.calc.variables() + return !vars.includes("x") && !vars.includes("n") } execute(x = 1) { if(this.cached) return this.cachedValue - C.currentVars = Object.assign({'x': x}, C.evalVariables) + C.currentVars = Object.assign({'x': x}, C.currentObjectsByName) + //console.log("Executing", this.expr, "with", JSON.stringify(C.currentVars)) return this.calc.evaluate(C.currentVars) } simplify(x) { var expr = this.calc.substitute('x', x).simplify() - if(expr.evaluate(C.evalVariables) == 0) return '0' + if(expr.evaluate() == 0) return '0' var str = Utils.makeExpressionReadable(expr.toString()); if(str != undefined && str.match(/^\d*\.\d+$/)) { if(str.split('.')[1].split('0').length > 7) { diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.js index cdee74c..ddfe4e0 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.js @@ -39,7 +39,7 @@ class Sequence extends Expr.Expression { if(['string', 'number'].includes(typeof this.calcValues[n])) { let parsed = C.parser.parse(this.calcValues[n].toString()).simplify() this.latexValues[n] = Latex.expression(parsed.tokens) - this.calcValues[n] = parsed.evaluate(C.evalVariables) + this.calcValues[n] = parsed.evaluate() } this.valuePlus = parseInt(valuePlus) } @@ -65,9 +65,10 @@ class Sequence extends Expr.Expression { cache(n = 1) { var str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString()) var expr = C.parser.parse(str).simplify() - var l = {'n': n-this.valuePlus} // Just in case, add n (for custom functions) - l[this.name] = this.calcValues - C.currentVars = Object.assign(l, C.evalVariables) + C.currentVars = Object.assign( + {'n': n-this.valuePlus, [this.name]: this.calcValues}, // Just in case, add n (for custom functions) + C.currentObjectsByName + ) this.calcValues[n] = expr.evaluate(C.currentVars) } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.js index b2a5b15..1a23f7c 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.js @@ -19,14 +19,15 @@ .pragma library .import "utils.js" as Utils -.import "mathlib.js" as MathLib +.import "math/common.js" as MathCommons .import "parameters.js" as P var types = {} var currentObjects = {} var currentObjectsByName = {} - +MathCommons.currentObjectsByName = currentObjectsByName // Required for using objects in variables. + function renameObject(oldName, newName) { /** * Renames an object from its old name to the new one. @@ -46,7 +47,7 @@ function deleteObject(objName) { */ let obj = currentObjectsByName[objName] delete currentObjectsByName[objName] - currentObjects[obj.type].splice(currentObjects.indexOf(obj),1) + currentObjects[obj.type].splice(currentObjects[obj.type].indexOf(obj),1) obj.delete() } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js index 7af97f7..d8e8d45 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js @@ -187,7 +187,7 @@ class DrawableObject { * Callback method when one of the properties of the object is updated. */ update() { - for(var req of this.requiredBy) { + for(let req of this.requiredBy) { req.update() } }