From 1a433eba27f3105023bbae139a0cd240aa94e21a Mon Sep 17 00:00:00 2001 From: Ad5001 Date: Tue, 18 Oct 2022 00:45:53 +0200 Subject: [PATCH] Propagated property updates to dependent objects. + Fixing object expression dependency at setup. + Fixing function calls in utils + Removing some unnecessary comments --- .../LogarithmPlotter/LogarithmPlotter.qml | 11 ++++-- .../js/history/editproperty.js | 2 + .../LogarithmPlotter/js/math/expression.js | 23 ++++++++++-- .../ad5001/LogarithmPlotter/js/objs/common.js | 37 ++++++++++++++++--- .../LogarithmPlotter/js/objs/repartition.js | 8 ---- .../js/objs/sommephasesbode.js | 4 -- .../ad5001/LogarithmPlotter/js/objs/text.js | 6 --- .../eu/ad5001/LogarithmPlotter/js/utils.js | 14 +++---- 8 files changed, 68 insertions(+), 37 deletions(-) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml index 9b69b47..ca1b087 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml @@ -249,11 +249,12 @@ ApplicationWindow { // Importing objects Objects.currentObjects = {} - for(var objType in data['objects']) { + Objects.currentObjectsByName = {} + for(let objType in data['objects']) { if(Object.keys(Objects.types).indexOf(objType) > -1) { Objects.currentObjects[objType] = [] - for(var objData of data['objects'][objType]) { - var obj = new Objects.types[objType](...objData) + for(let objData of data['objects'][objType]) { + let obj = new Objects.types[objType](...objData) Objects.currentObjects[objType].push(obj) Objects.currentObjectsByName[obj.name] = obj } @@ -262,6 +263,10 @@ ApplicationWindow { } } + // Updating object dependencies. + for(let objName in Objects.currentObjectsByName) + Objects.currentObjectsByName[objName].update() + // Importing history if("history" in data) history.unserialize(...data["history"]) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/editproperty.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/editproperty.js index 140a54b..87be759 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/editproperty.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/editproperty.js @@ -58,10 +58,12 @@ class EditedProperty extends C.Action { undo() { Objects.currentObjectsByName[this.targetName][this.targetProperty] = this.previousValue + Objects.currentObjectsByName[this.targetName].update() } redo() { Objects.currentObjectsByName[this.targetName][this.targetProperty] = this.newValue + Objects.currentObjectsByName[this.targetName].update() } export() { diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js index 6007a3b..88ebac6 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js @@ -30,7 +30,7 @@ class Expression { this.expr = expr this.calc = C.parser.parse(expr).simplify() this.cached = this.isConstant() - this.cachedValue = this.cached ? this.calc.evaluate(C.currentObjectsByName) : null + this.cachedValue = this.cached && this.allRequirementsFullfilled() ? this.calc.evaluate(C.currentObjectsByName) : null this.latexMarkup = Latex.expression(this.calc.tokens) } @@ -39,8 +39,25 @@ class Expression { return !vars.includes("x") && !vars.includes("n") } + requiredObjects() { + return this.calc.variables().filter(objName => objName != "x" && objName != "n") + } + + allRequirementsFullfilled() { + return this.requiredObjects().every(objName => objName in C.currentObjectsByName) + } + + recache() { + if(this.cached) + this.cachedValue = this.calc.evaluate(C.currentObjectsByName) + } + execute(x = 1) { - if(this.cached) return this.cachedValue + if(this.cached) { + if(this.cachedValue == null) + this.cachedValue = this.calc.evaluate(C.currentObjectsByName) + return this.cachedValue + } C.currentVars = Object.assign({'x': x}, C.currentObjectsByName) //console.log("Executing", this.expr, "with", JSON.stringify(C.currentVars)) return this.calc.evaluate(C.currentVars) @@ -68,7 +85,7 @@ class Expression { } toString(forceSign=false) { - var str = Utils.makeExpressionReadable(this.calc.toString()) + let str = Utils.makeExpressionReadable(this.calc.toString()) if(str[0] != '-' && forceSign) str = '+' + str return str } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js index d8e8d45..e37ad75 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js @@ -21,6 +21,8 @@ .import "../utils.js" as Utils .import "../objects.js" as Objects .import "../math/latex.js" as Latex +.import "../parameters.js" as P +.import "../math/common.js" as C // This file contains the default data to be imported from all other objects @@ -116,6 +118,7 @@ class DrawableObject { this.color = color this.labelContent = labelContent // "null", "name", "name + value" this.requiredBy = [] + this.requires = [] } /** @@ -187,18 +190,40 @@ class DrawableObject { * Callback method when one of the properties of the object is updated. */ update() { - for(let req of this.requiredBy) { + // Refreshing dependencies. + for(let obj of this.requires) + obj.requiredBy = obj.requiredBy.filter(dep => dep != this) + // Checking objects this one depends on + this.requires = [] + let properties = this.constructor.properties() + for(let property in properties) + if(properties[property] == 'Expression' && this[property] != null) { + // Expressions with dependencies + for(let objName of this[property].requiredObjects()) { + this.requires.push(C.currentObjectsByName[objName]) + C.currentObjectsByName[objName].requiredBy.push(this) + } + if(this[property].cached && this[property].requiredObjects().length > 0) + // Recalculate + this[property].recache() + + } else if(typeof properties[property] == 'object' && 'type' in properties[property] && properties[property] == 'ObjectType' && this[property] != null) { + // Object dependency + this.requires.push(this[property]) + this[property].requiredBy.push(this) + } + + // Updating objects dependent on this one + for(let req of this.requiredBy) req.update() - } } /** * Callback method when the object is about to get deleted. */ delete() { - for(var toRemove of this.requiredBy) { - toRemove.delete() - Objects.currentObjects[toRemove.type] = Objects.currentObjects[toRemove.type].filter(obj => obj.name != toRemove.name) + for(let toRemove of this.requiredBy) { + Objects.deleteObject(toRemove.name) } } @@ -261,7 +286,7 @@ class DrawableObject { } /** - * Automaticly draw text (by default the label of the object on the \c canvas with + * Automatically draw text (by default the label of the object on the \c canvas with * the 2D context \c ctx depending on user settings. * This method takes into account both the \c posX and \c posY of where the label * should be displayed, including the \c labelPosition relative to it. diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.js index da8c4e1..0f25c4c 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.js @@ -27,14 +27,6 @@ class RepartitionFunction extends Common.ExecutableObject { static type(){return 'Repartition'} static displayType(){return qsTr('Repartition')} static displayTypeMultiple(){return qsTr('Repartition functions')} - /*static properties() {return { - 'beginIncluded': 'boolean', - 'drawLineEnds': 'boolean', - 'comment1': 'Note: Specify the properties for each potential result.', - 'probabilities': new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d\.,]+$/, 'P({name} = ', ') = '), - 'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'), - 'labelX': 'number' - }}*/ static properties() {return { [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, [QT_TRANSLATE_NOOP('prop','labelX')]: 'number', diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommephasesbode.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommephasesbode.js index bfa6c4b..5d3f0da 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommephasesbode.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommephasesbode.js @@ -30,10 +30,6 @@ class SommePhasesBode extends Common.ExecutableObject { static displayType(){return qsTr('Bode Phases Sum')} static displayTypeMultiple(){return qsTr('Bode Phases Sum')} static createable() {return false} - /*static properties() {return { - 'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'), - 'labelX': 'number' - }}*/ static properties() {return { [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, [QT_TRANSLATE_NOOP('prop','labelX')]: 'number', diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.js index 0224089..77f5c5d 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.js @@ -29,12 +29,6 @@ class Text extends Common.DrawableObject { static type(){return 'Text'} static displayType(){return qsTr('Text')} static displayTypeMultiple(){return qsTr('Texts')} - /*static properties() {return { - 'x': 'Expression', - 'y': 'Expression', - 'labelPosition': new P.Enum('center', 'above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'), - 'text': 'string', - }}*/ static properties() {return { [QT_TRANSLATE_NOOP('prop','x')]: 'Expression', [QT_TRANSLATE_NOOP('prop','y')]: 'Expression', diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/utils.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/utils.js index 017b411..a383846 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/utils.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/utils.js @@ -129,11 +129,11 @@ function simplifyExpression(str) { var replacements = [ // Operations not done by parser. [// Decomposition way 2 - /(^.?|[+-] |\()([-.\d\w]+) ([*/]) \((([-.\d\w] [*/] )?[-\d\w.]+) ([+\-]) (([-.\d\w] [*/] )?[\d\w.+]+)\)(.?$| [+-]|\))/g, + /(^|[+-] |\()([-.\d\w]+) ([*/]) \((([-.\d\w] [*/] )?[-\d\w.]+) ([+\-]) (([-.\d\w] [*/] )?[\d\w.+]+)\)($| [+-]|\))/g, "$1$2 $3 $4 $6 $2 $3 $7$9" ], [ // Decomposition way 2 - /(^.?|[+-] |\()\((([-.\d\w] [*/] )?[-\d\w.]+) ([+\-]) (([-.\d\w] [*/] )?[\d\w.+]+)\) ([*/]) ([-.\d\w]+)(.?$| [+-]|\))/g, + /(^|[+-] |\()\((([-.\d\w] [*/] )?[-\d\w.]+) ([+\-]) (([-.\d\w] [*/] )?[\d\w.+]+)\) ([*/]) ([-.\d\w]+)($| [+-]|\))/g, "$1$2 $7 $8 $4 $5 $7 $8$9" ], [ // Factorisation of π elements. @@ -159,19 +159,19 @@ function simplifyExpression(str) { } ], [ // Removing parenthesis when content is only added from both sides. - /(^.?|[+-] |\()\(([^)(]+)\)(.?$| [+-]|\))/g, + /(^|[+-] |\()\(([^)(]+)\)($| [+-]|\))/g, function(match, b4, middle, after) {return `${b4}${middle}${after}`} ], [ // Removing parenthesis when content is only multiplied. - /(^.?|[*\/] |\()\(([^)(+-]+)\)(.?$| [*\/+-]|\))/g, + /(^|[*\/] |\()\(([^)(+-]+)\)($| [*\/+-]|\))/g, function(match, b4, middle, after) {return `${b4}${middle}${after}`} ], [ // Removing parenthesis when content is only multiplied. - /(^.?|[*\/-+] |\()\(([^)(+-]+)\)(.?$| [*\/]|\))/g, + /(^|[*\/-+] |\()\(([^)(+-]+)\)($| [*\/]|\))/g, function(match, b4, middle, after) {return `${b4}${middle}${after}`} ], [// Simplification additions/substractions. - /(^.?|[^*\/] |\()([-.\d]+) (\+|\-) (\([^)(]+\)|[^)(]+) (\+|\-) ([-.\d]+)(.?$| [^*\/]|\))/g, + /(^|[^*\/] |\()([-.\d]+) (\+|\-) (\([^)(]+\)|[^)(]+) (\+|\-) ([-.\d]+)($| [^*\/]|\))/g, function(match, b4, n1, op1, middle, op2, n2, after) { var total if(op2 == '+') { @@ -258,7 +258,7 @@ function makeExpressionReadable(str) { [/\[([^\[\]]+)\]/g, function(match, p1) { return textsub(p1) }], [/(\d|\))×/g, '$1'], //[/×(\d|\()/g, '$1'], - [/\(([^)(+.\/-]+)\)/g, "$1"], + [/[^a-z]\(([^)(+.\/-]+)\)/g, "$1"], [/integral\((.+),\s?(.+),\s?("|')(.+)("|'),\s?("|')(.+)("|')\)/g, function(match, a, b, p1, body, p2, p3, by, p4) { if(a.length < b.length) { return `∫${textsub(a)}${textsup(b)} ${body} d${by}`