From 73cba855921276d5e0f452e0f2a1b0102f2a3c95 Mon Sep 17 00:00:00 2001 From: Ad5001 Date: Fri, 29 Mar 2024 01:55:13 +0100 Subject: [PATCH] Creating Canvas JS Module. --- LogarithmPlotter/logarithmplotter.py | 2 +- .../eu/ad5001/LogarithmPlotter/AppMenuBar.qml | 10 +- .../LogarithmPlotter/History/History.qml | 6 +- .../LogarithmPlotter/LogGraphCanvas.qml | 331 +---------- .../LogarithmPlotter/LogarithmPlotter.qml | 26 +- .../ObjectLists/Editor/CustomPropertyList.qml | 19 +- .../ObjectLists/Editor/Dialog.qml | 12 +- .../ObjectLists/ObjectCreationGrid.qml | 10 +- .../ObjectLists/ObjectLists.qml | 20 +- .../ObjectLists/ObjectRow.qml | 12 +- .../LogarithmPlotter/PickLocationOverlay.qml | 19 +- .../Setting/ExpressionEditor.qml | 16 +- .../eu/ad5001/LogarithmPlotter/Settings.qml | 8 +- .../ViewPositionChangeOverlay.qml | 5 +- .../js/{modules.js => autoload.js} | 1 + .../eu/ad5001/LogarithmPlotter/js/canvas.mjs | 523 ++++++++++++++++++ .../LogarithmPlotter/js/history/common.mjs | 22 +- .../js/lib/expr-eval/integration.js | 10 +- .../LogarithmPlotter/js/lib/qmlpolyfills.mjs | 15 +- .../LogarithmPlotter/js/math/expression.mjs | 20 +- .../ad5001/LogarithmPlotter/js/math/latex.mjs | 41 +- .../LogarithmPlotter/js/math/sequence.mjs | 10 +- .../js/{runtime.mjs => modules.mjs} | 6 +- .../eu/ad5001/LogarithmPlotter/js/objects.mjs | 8 +- .../LogarithmPlotter/js/objs/autoload.mjs | 2 +- .../LogarithmPlotter/js/objs/common.mjs | 43 +- .../LogarithmPlotter/js/objs/function.mjs | 26 +- .../LogarithmPlotter/js/objs/gainbode.mjs | 27 +- .../LogarithmPlotter/js/objs/phasebode.mjs | 11 +- .../ad5001/LogarithmPlotter/js/objs/point.mjs | 20 +- .../LogarithmPlotter/js/objs/repartition.mjs | 42 +- .../LogarithmPlotter/js/objs/sequence.mjs | 6 +- .../js/objs/sommegainsbode.mjs | 10 +- .../js/objs/sommephasesbode.mjs | 10 +- .../ad5001/LogarithmPlotter/js/objs/text.mjs | 4 +- .../LogarithmPlotter/js/objs/xcursor.mjs | 34 +- 36 files changed, 797 insertions(+), 590 deletions(-) rename LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/{modules.js => autoload.js} (87%) create mode 100644 LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/canvas.mjs rename LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/{runtime.mjs => modules.mjs} (89%) diff --git a/LogarithmPlotter/logarithmplotter.py b/LogarithmPlotter/logarithmplotter.py index f8670bd..5270534 100644 --- a/LogarithmPlotter/logarithmplotter.py +++ b/LogarithmPlotter/logarithmplotter.py @@ -113,7 +113,7 @@ def run(): helper = Helper(pwd, tmpfile) latex = Latex(tempdir) modules = engine.newObject() - engine.globalObject().setProperty('Runtime', modules) + engine.globalObject().setProperty('Modules', modules) engine.globalObject().setProperty('Helper', engine.newQObject(helper)) engine.globalObject().setProperty("Latex", engine.newQObject(latex)) # engine.rootContext().setContextProperty("Helper", helper) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml index a77de0c..3de96ca 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml @@ -103,17 +103,17 @@ MenuBar { title: qsTr("&Create") // Services repeater Repeater { - model: Object.keys(Runtime.Objects.types) + model: Object.keys(Modules.Objects.types) MenuItem { - text: Runtime.Objects.types[modelData].displayType() - visible: Runtime.Objects.types[modelData].createable() + text: Modules.Objects.types[modelData].displayType() + visible: Modules.Objects.types[modelData].createable() height: visible ? implicitHeight : 0 icon.name: modelData icon.source: './icons/objects/' + modelData + '.svg' icon.color: sysPalette.buttonText onTriggered: { - var newObj = Runtime.Objects.createNewRegisteredObject(modelData) + var newObj = Modules.Objects.createNewRegisteredObject(modelData) history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export())) objectLists.update() } @@ -151,7 +151,7 @@ MenuBar { checked: Helper.getSettingBool("enable_latex") onTriggered: { Helper.setSettingBool("enable_latex", checked) - Runtime.Latex.enabled = checked + Modules.Latex.enabled = checked drawCanvas.requestPaint() } icon.name: 'Expression' diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/History.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/History.qml index 8a9a6ba..afc7281 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/History.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/History.qml @@ -212,8 +212,8 @@ Item { } Component.onCompleted: { - Runtime.History.history = historyObj - Runtime.History.themeTextColor = sysPalette.windowText - Runtime.History.imageDepth = Screen.devicePixelRatio + Modules.History.history = historyObj + Modules.History.themeTextColor = sysPalette.windowText + Modules.History.imageDepth = Screen.devicePixelRatio } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml index 908bdb6..973142f 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml @@ -126,37 +126,6 @@ Canvas { */ property int maxgradx: 20 - /*! - \qmlproperty var LogGraphCanvas::yaxisstepExpr - Expression for the y axis step (used to create labels). - */ - property var yaxisstepExpr: (new MathLib.Expression(`x*(${yaxisstep})`)) - /*! - \qmlproperty double LogGraphCanvas::yaxisstep1 - Value of the for the y axis step. - */ - property double yaxisstep1: yaxisstepExpr.execute(1) - /*! - \qmlproperty int LogGraphCanvas::drawMaxY - Minimum value of y that should be drawn onto the canvas. - */ - property int drawMaxY: Math.ceil(Math.max(Math.abs(ymax), Math.abs(px2y(canvasSize.height)))/yaxisstep1) - /*! - \qmlproperty var LogGraphCanvas::xaxisstepExpr - Expression for the x axis step (used to create labels). - */ - property var xaxisstepExpr: (new MathLib.Expression(`x*(${xaxisstep})`)) - /*! - \qmlproperty double LogGraphCanvas::xaxisstep1 - Value of the for the x axis step. - */ - property double xaxisstep1: xaxisstepExpr.execute(1) - /*! - \qmlproperty int LogGraphCanvas::drawMaxX - Maximum value of x that should be drawn onto the canvas. - */ - property int drawMaxX: Math.ceil(Math.max(Math.abs(xmin), Math.abs(px2x(canvasSize.width)))/xaxisstep1) - /*! \qmlproperty var LogGraphCanvas::imageLoaders Dictionary of format {image: [callback.image data]} containing data for defered image loading. @@ -168,7 +137,10 @@ Canvas { */ property var ctx - Component.onCompleted: imageLoaders = {} + Component.onCompleted: { + imageLoaders = {} + Modules.Canvas.initialize(canvas, drawingErrorDialog) + } Native.MessageDialog { id: drawingErrorDialog @@ -183,27 +155,7 @@ Canvas { onPaint: function(rect) { //console.log('Redrawing') if(rect.width == canvas.width) { // Redraw full canvas - ctx = getContext("2d"); - reset(ctx) - drawGrille(ctx) - drawAxises(ctx) - drawLabels(ctx) - ctx.lineWidth = linewidth - for(var objType in Runtime.Objects.currentObjects) { - for(var obj of Runtime.Objects.currentObjects[objType]){ - ctx.strokeStyle = obj.color - ctx.fillStyle = obj.color - if(obj.visible) - try { - obj.draw(canvas, ctx) - } catch(e) { - // Drawing throws an error. Generally, it's due to a new modification (or the opening of a file) - drawingErrorDialog.showDialog(objType, obj.name, e.message) - history.undo() - } - } - } - ctx.lineWidth = 1 + Modules.Canvas.redraw() } } @@ -216,277 +168,4 @@ Canvas { } }) } - - /*! - \qmlmethod void LogGraphCanvas::reset(var ctx) - Resets the canvas to a blank one with default setting using 2D \c ctx. - */ - function reset(ctx){ - // Reset - ctx.fillStyle = "#FFFFFF" - ctx.strokeStyle = "#000000" - ctx.font = `${canvas.textsize}px sans-serif` - ctx.fillRect(0,0,width,height) - } - - // Drawing the log based graph - - /*! - \qmlmethod void LogGraphCanvas::drawGrille(var ctx) - Draws the grid using 2D \c ctx. - */ - function drawGrille(ctx) { - ctx.strokeStyle = "#C0C0C0" - if(logscalex) { - for(var xpow = -maxgradx; xpow <= maxgradx; xpow++) { - for(var xmulti = 1; xmulti < 10; xmulti++) { - drawXLine(ctx, Math.pow(10, xpow)*xmulti) - } - } - } else { - for(var x = 0; x < drawMaxX; x+=1) { - drawXLine(ctx, x*xaxisstep1) - drawXLine(ctx, -x*xaxisstep1) - } - } - for(var y = 0; y < drawMaxY; y+=1) { - drawYLine(ctx, y*yaxisstep1) - drawYLine(ctx, -y*yaxisstep1) - } - } - - /*! - \qmlmethod void LogGraphCanvas::drawAxises(var ctx) - Draws the graph axises using 2D \c ctx. - */ - function drawAxises(ctx) { - ctx.strokeStyle = "#000000" - var axisypos = logscalex ? 1 : 0 - drawXLine(ctx, axisypos) - drawYLine(ctx, 0) - var axisypx = x2px(axisypos) // X coordinate of Y axis - var axisxpx = y2px(0) // Y coordinate of X axis - // Drawing arrows - drawLine(ctx, axisypx, 0, axisypx-10, 10) - drawLine(ctx, axisypx, 0, axisypx+10, 10) - drawLine(ctx, canvasSize.width, axisxpx, canvasSize.width-10, axisxpx-10) - drawLine(ctx, canvasSize.width, axisxpx, canvasSize.width-10, axisxpx+10) - } - - /*! - \qmlmethod void LogGraphCanvas::drawLabels(var ctx) - Draws all labels (graduation & axises labels) using 2D \c ctx. - */ - function drawLabels(ctx) { - var axisypx = x2px(logscalex ? 1 : 0) // X coordinate of Y axis - var axisxpx = y2px(0) // Y coordinate of X axis - // Labels - ctx.fillStyle = "#000000" - ctx.font = `${canvas.textsize}px sans-serif` - ctx.fillText(ylabel, axisypx+10, 24) - var textSize = ctx.measureText(xlabel).width - ctx.fillText(xlabel, canvasSize.width-14-textSize, axisxpx-5) - // Axis graduation labels - ctx.font = `${canvas.textsize-4}px sans-serif` - - var txtMinus = ctx.measureText('-').width - if(showxgrad) { - if(logscalex) { - for(var xpow = -maxgradx; xpow <= maxgradx; xpow+=1) { - var textSize = ctx.measureText("10"+Utils.textsup(xpow)).width - if(xpow != 0) - drawVisibleText(ctx, "10"+Utils.textsup(xpow), x2px(Math.pow(10,xpow))-textSize/2, axisxpx+16+(6*(xpow==1))) - } - } else { - for(var x = 1; x < drawMaxX; x += 1) { - var drawX = x*xaxisstep1 - var txtX = xaxisstepExpr.simplify(x).replace(/^\((.+)\)$/, '$1') - var textSize = measureText(ctx, txtX, 6).height - drawVisibleText(ctx, txtX, x2px(drawX)-4, axisxpx+textsize/2+textSize) - drawVisibleText(ctx, '-'+txtX, x2px(-drawX)-4, axisxpx+textsize/2+textSize) - } - } - } - if(showygrad) { - for(var y = 0; y < drawMaxY; y += 1) { - var drawY = y*yaxisstep1 - var txtY = yaxisstepExpr.simplify(y).replace(/^\((.+)\)$/, '$1') - var textSize = ctx.measureText(txtY).width - drawVisibleText(ctx, txtY, axisypx-6-textSize, y2px(drawY)+4+(10*(y==0))) - if(y != 0) - drawVisibleText(ctx, '-'+txtY, axisypx-6-textSize-txtMinus, y2px(-drawY)+4) - } - } - ctx.fillStyle = "#FFFFFF" - } - - /*! - \qmlmethod void LogGraphCanvas::drawXLine(var ctx, double x) - Draws an horizontal line at \c x plot coordinate using 2D \c ctx. - */ - function drawXLine(ctx, x) { - if(isVisible(x, ymax)) { - drawLine(ctx, x2px(x), 0, x2px(x), canvasSize.height) - } - } - - /*! - \qmlmethod void LogGraphCanvas::drawXLine(var ctx, double x) - Draws an vertical line at \c y plot coordinate using 2D \c ctx. - */ - function drawYLine(ctx, y) { - if(isVisible(xmin, y)) { - drawLine(ctx, 0, y2px(y), canvasSize.width, y2px(y)) - } - } - - /*! - \qmlmethod void LogGraphCanvas::drawVisibleText(var ctx, string text, double x, double y) - Writes multline \c text onto the canvas using 2D \c ctx. - \note The \c x and \c y properties here are relative to the canvas, not the plot. - */ - function drawVisibleText(ctx, text, x, y) { - if(x > 0 && x < canvasSize.width && y > 0 && y < canvasSize.height) { - text.toString().split("\n").forEach(function(txt, i){ - ctx.fillText(txt, x, y+(canvas.textsize*i)) - }) - } - } - - /*! - \qmlmethod void LogGraphCanvas::drawVisibleImage(var ctx, var image, double x, double y) - Draws an \c image onto the canvas using 2D \c ctx. - \note The \c x, \c y \c width and \c height properties here are relative to the canvas, not the plot. - */ - function drawVisibleImage(ctx, image, x, y, width, height) { - //console.log("Drawing image", isImageLoaded(image), isImageError(image)) - markDirty(Qt.rect(x, y, width, height)); - ctx.drawImage(image, x, y, width, height) - /*if(true || (x > 0 && x < canvasSize.width && y > 0 && y < canvasSize.height)) { - }*/ - } - - /*! - \qmlmethod var LogGraphCanvas::measureText(var ctx, string text) - Measures the wicth and height of a multiline \c text that would be drawn onto the canvas using 2D \c ctx. - Return format: dictionary {"width": width, "height": height} - */ - function measureText(ctx, text) { - let theight = 0 - let twidth = 0 - let defaultHeight = ctx.measureText("M").width // Approximate but good enough! - text.split("\n").forEach(function(txt, i){ - theight += defaultHeight - if(ctx.measureText(txt).width > twidth) twidth = ctx.measureText(txt).width - }) - return {'width': twidth, 'height': theight} - } - - /*! - \qmlmethod double LogGraphCanvas::x2px(double x) - Converts an \c x coordinate to it's relative position on the canvas. - It supports both logarithmic and non logarithmic scale depending on the currently selected mode. - */ - function x2px(x) { - if(logscalex) { - var logxmin = Math.log(xmin) - return (Math.log(x)-logxmin)*xzoom - } else return (x - xmin)*xzoom - } - - /*! - \qmlmethod double LogGraphCanvas::y2px(double y) - Converts an \c y coordinate to it's relative position on the canvas. - The y axis not supporting logarithmic scale, it only support linear convertion. - */ - function y2px(y) { - return (ymax-y)*yzoom - } - - /*! - \qmlmethod double LogGraphCanvas::px2x(double px) - Converts an x \c px position on the canvas to it's corresponding coordinate on the plot. - It supports both logarithmic and non logarithmic scale depending on the currently selected mode. - */ - function px2x(px) { - if(logscalex) { - return Math.exp(px/xzoom+Math.log(xmin)) - } else return (px/xzoom+xmin) - } - - /*! - \qmlmethod double LogGraphCanvas::px2x(double px) - Converts an x \c px position on the canvas to it's corresponding coordinate on the plot. - It supports both logarithmic and non logarithmic scale depending on the currently selected mode. - */ - function px2y(px) { - return -(px/yzoom-ymax) - } - - /*! - \qmlmethod bool LogGraphCanvas::isVisible(double x, double y) - Checks whether a plot point (\c x, \c y) is visible or not on the canvas. - */ - function isVisible(x, y) { - return (x2px(x) >= 0 && x2px(x) <= canvasSize.width) && (y2px(y) >= 0 && y2px(y) <= canvasSize.height) - } - - /*! - \qmlmethod bool LogGraphCanvas::drawLine(var ctx, double x1, double y1, double x2, double y2) - Draws a line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx. - */ - function drawLine(ctx, x1, y1, x2, y2) { - ctx.beginPath(); - ctx.moveTo(x1, y1); - ctx.lineTo(x2, y2); - ctx.stroke(); - } - - /*! - \qmlmethod bool LogGraphCanvas::drawDashedLine2(var ctx, double x1, double y1, double x2, double y2) - Draws a dashed line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx. - */ - function drawDashedLine2(ctx, x1, y1, x2, y2, dashPxSize = 5) { - ctx.setLineDash([dashPxSize, dashPxSize]); - drawLine(ctx, x1, y1, x2, y2) - ctx.setLineDash([]); - } - - /*! - \qmlmethod bool LogGraphCanvas::drawDashedLine(var ctx, double x1, double y1, double x2, double y2) - Draws a dashed line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx. - (Legacy slower method) - */ - function drawDashedLine(ctx, x1, y1, x2, y2, dashPxSize = 10) { - var distance = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)) - var progPerc = dashPxSize/distance - ctx.beginPath(); - ctx.moveTo(x1, y1); - for(var i = 0; i < 1; i += progPerc) { - ctx.lineTo(x1-(x1-x2)*i, y1-(y1-y2)*i) - ctx.moveTo(x1-(x1-x2)*(i+progPerc/2), y1-(y1-y2)*(i+progPerc/2)) - } - ctx.stroke(); - } - - /*! - \qmlmethod var LogGraphCanvas::renderLatexImage(string ltxText, color) - Renders latex markup \c ltxText to an image and loads it. Returns a dictionary with three values: source, width and height. - */ - function renderLatexImage(ltxText, color, callback) { - let [ltxSrc, ltxWidth, ltxHeight] = Latex.render(ltxText, textsize, color).split(",") - let imgData = { - "source": ltxSrc, - "width": parseFloat(ltxWidth), - "height": parseFloat(ltxHeight) - }; - if(!isImageLoaded(ltxSrc) && !isImageLoading(ltxSrc)){ - // Wait until the image is loaded to callback. - loadImage(ltxSrc) - imageLoaders[ltxSrc] = [callback, imgData] - } else { - // Callback directly - callback(canvas, ctx, imgData) - } - } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml index 4c9a7b4..3fb9697 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml @@ -23,7 +23,7 @@ import QtQuick.Layouts 1.12 import QtQuick // Auto loading all modules. -import "js/modules.js" as Modules +import "js/autoload.js" as ModulesAutoload import eu.ad5001.LogarithmPlotter.History 1.0 import eu.ad5001.LogarithmPlotter.ObjectLists 1.0 @@ -191,9 +191,9 @@ ApplicationWindow { filename += '.lpf' settings.saveFilename = filename var objs = {} - for(var objType in Runtime.Objects.currentObjects){ + for(var objType in Modules.Objects.currentObjects){ objs[objType] = [] - for(var obj of Runtime.Objects.currentObjects[objType]) { + for(var obj of Modules.Objects.currentObjects[objType]) { objs[objType].push(obj.export()) } } @@ -255,19 +255,19 @@ ApplicationWindow { root.width = data["width"] // Importing objects - Runtime.Objects.currentObjects = {} - Runtime.Object.keys(Objects.currentObjectsByName).forEach(key => { - delete Runtime.Objects.currentObjectsByName[key]; + Modules.Objects.currentObjects = {} + Modules.Object.keys(Objects.currentObjectsByName).forEach(key => { + delete Modules.Objects.currentObjectsByName[key]; // Required to keep the same reference for the copy of the object used in expression variable detection. // Another way would be to change the reference as well, but I feel like the code would be less clean. }) for(let objType in data['objects']) { - if(Object.keys(Runtime.Objects.types).indexOf(objType) > -1) { - Runtime.Objects.currentObjects[objType] = [] + if(Object.keys(Modules.Objects.types).indexOf(objType) > -1) { + Modules.Objects.currentObjects[objType] = [] for(let objData of data['objects'][objType]) { - let obj = new Runtime.Objects.types[objType](...objData) - Runtime.Objects.currentObjects[objType].push(obj) - Runtime.Objects.currentObjectsByName[obj.name] = obj + let obj = new Modules.Objects.types[objType](...objData) + Modules.Objects.currentObjects[objType].push(obj) + Modules.Objects.currentObjectsByName[obj.name] = obj } } else { error += qsTr("Unknown object type: %1.").arg(objType) + "\n"; @@ -275,8 +275,8 @@ ApplicationWindow { } // Updating object dependencies. - for(let objName in Runtime.Objects.currentObjectsByName) - Runtime.Objects.currentObjectsByName[objName].update() + for(let objName in Modules.Objects.currentObjectsByName) + Modules.Objects.currentObjectsByName[objName].update() // Importing history if("history" in data) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/CustomPropertyList.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/CustomPropertyList.qml index eb163f3..a6f57d6 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/CustomPropertyList.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/CustomPropertyList.qml @@ -130,6 +130,7 @@ Repeater { } } catch(e) { // Error in expression or domain + console.trace() parsingErrorDialog.showDialog(propertyName, newValue, e.message) } } @@ -187,8 +188,8 @@ Repeater { // Base, untranslated version of the model. property var baseModel: selectObjMode ? - Runtime.Objects.getObjectsName(propertyType.objType).concat( - isRealObject ? [qsTr("+ Create new %1").arg(Runtime.Objects.types[propertyType.objType].displayType())] : []) + Modules.Objects.getObjectsName(propertyType.objType).concat( + isRealObject ? [qsTr("+ Create new %1").arg(Modules.Objects.types[propertyType.objType].displayType())] : []) : propertyType.values // Translated version of the model. model: selectObjMode ? baseModel : propertyType.translatedValues @@ -198,20 +199,20 @@ Repeater { if(selectObjMode) { // This is only done when what we're selecting are Objects. // Setting object property. - var selectedObj = Runtime.Objects.currentObjectsByName[baseModel[newIndex]] + var selectedObj = Modules.Objects.currentObjectsByName[baseModel[newIndex]] if(newIndex != 0) { // Make sure we don't set the object to null. if(selectedObj == null) { // Creating new object. - selectedObj = Runtime.Objects.createNewRegisteredObject(propertyType.objType) + selectedObj = Modules.Objects.createNewRegisteredObject(propertyType.objType) history.addToHistory(new HistoryLib.CreateNewObject(selectedObj.name, propertyType.objType, selectedObj.export())) - baseModel = Runtime.Objects.getObjectsName(propertyType.objType).concat( - isRealObject ? [qsTr("+ Create new %1").arg(Runtime.Objects.types[propertyType.objType].displayType())] : + baseModel = Modules.Objects.getObjectsName(propertyType.objType).concat( + isRealObject ? [qsTr("+ Create new %1").arg(Modules.Objects.types[propertyType.objType].displayType())] : []) currentIndex = baseModel.indexOf(selectedObj.name) } - selectedObj.requiredBy.push(Runtime.Objects.currentObjects[objType][objIndex]) - //Runtime.Objects.currentObjects[objType][objIndex].requiredBy = obj[propertyName].filter((obj) => obj.name != obj.name) + selectedObj.requiredBy.push(Modules.Objects.currentObjects[objType][objIndex]) + //Modules.Objects.currentObjects[objType][objIndex].requiredBy = obj[propertyName].filter((obj) => obj.name != obj.name) } obj.requiredBy = obj.requiredBy.filter((obj) => obj.name != obj.name) history.addToHistory(new HistoryLib.EditedProperty( @@ -255,7 +256,7 @@ Repeater { obj.name, objType, propertyName, obj[propertyName], exported )) - //Runtime.Objects.currentObjects[objType][objIndex][propertyName] = exported + //Modules.Objects.currentObjects[objType][objIndex][propertyName] = exported obj[propertyName] = exported root.changed() } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/Dialog.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/Dialog.qml index 2c2b9d0..6f650ef 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/Dialog.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/Dialog.qml @@ -52,7 +52,7 @@ Popup.BaseDialog { \qmlproperty var EditorDialog::obj Instance of the object being edited. */ - property var obj: Runtime.Objects.currentObjects[objType][objIndex] + property var obj: Modules.Objects.currentObjects[objType][objIndex] /*! \qmlproperty var EditorDialog::posPicker Reference to the global PositionPicker QML object. @@ -85,7 +85,7 @@ Popup.BaseDialog { Label { id: dlgTitle verticalAlignment: TextInput.AlignVCenter - text: qsTr("Edit properties of %1 %2").arg(Runtime.Objects.types[objEditor.objType].displayType()).arg(objEditor.obj.name) + text: qsTr("Edit properties of %1 %2").arg(Modules.Objects.types[objEditor.objType].displayType()).arg(objEditor.obj.name) font.pixelSize: 20 color: sysPalette.windowText } @@ -111,14 +111,14 @@ Popup.BaseDialog { onChanged: function(newValue) { let newName = Utils.parseName(newValue) if(newName != '' && objEditor.obj.name != newName) { - if(newName in Runtime.Objects.currentObjectsByName) { + if(newName in Modules.Objects.currentObjectsByName) { invalidNameDialog.showDialog(newName) } else { history.addToHistory(new HistoryLib.NameChanged( objEditor.obj.name, objEditor.objType, newName )) - Runtime.Objects.renameObject(obj.name, newName) - objEditor.obj = Runtime.Objects.currentObjects[objEditor.objType][objEditor.objIndex] + Modules.Objects.renameObject(obj.name, newName) + objEditor.obj = Modules.Objects.currentObjects[objEditor.objType][objEditor.objIndex] objectListList.update() } } @@ -163,7 +163,7 @@ Popup.BaseDialog { */ function open() { dlgCustomProperties.model = [] // Reset - let objProps = Runtime.Objects.types[objEditor.objType].properties() + let objProps = Modules.Objects.types[objEditor.objType].properties() dlgCustomProperties.model = Object.keys(objProps).map(prop => [prop, objProps[prop]]) // Converted to 2-dimentional array. objEditor.show() } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml index 6fd42c9..3e621b0 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml @@ -43,7 +43,7 @@ Column { // Open editor objectEditor.obj = obj objectEditor.objType = obj.type - objectEditor.objIndex = Runtime.Objects.currentObjects[obj.type].indexOf(obj) + objectEditor.objIndex = Modules.Objects.currentObjects[obj.type].indexOf(obj) objectEditor.open() // Disconnect potential link posPicker.picked.disconnect(openEditorDialog) @@ -60,12 +60,12 @@ Column { width: parent.width columns: 3 Repeater { - model: Object.keys(Runtime.Objects.types) + model: Object.keys(Modules.Objects.types) Button { id: createBtn width: 96 - visible: Runtime.Objects.types[modelData].createable() + visible: Modules.Objects.types[modelData].createable() height: visible ? width*0.8 : 0 // The KDE SDK is kinda buggy, so it respects neither specified color nor display propreties. //display: AbstractButton.TextUnderIcon @@ -93,7 +93,7 @@ Column { anchors.rightMargin: 4 horizontalAlignment: Text.AlignHCenter font.pixelSize: 14 - text: Runtime.Objects.types[modelData].displayType() + text: Modules.Objects.types[modelData].displayType() wrapMode: Text.WordWrap clip: true } @@ -103,7 +103,7 @@ Column { ToolTip.text: label.text onClicked: { - let newObj = Runtime.Objects.createNewRegisteredObject(modelData) + let newObj = Modules.Objects.createNewRegisteredObject(modelData) history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export())) objectLists.update() diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml index f7d9e27..d92c36b 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml @@ -46,7 +46,7 @@ ScrollView { ListView { id: objectsListView - model: Object.keys(Runtime.Objects.types) + model: Object.keys(Modules.Objects.types) //width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0) implicitHeight: contentItem.childrenRect.height + footerItem.height + 10 @@ -54,7 +54,7 @@ ScrollView { id: objTypeList property string objType: objectsListView.model[index] property var editingRows: [] - model: Runtime.Objects.currentObjects[objType] + model: Modules.Objects.currentObjects[objType] width: objectsListView.width implicitHeight: contentItem.childrenRect.height visible: model != undefined && model.length > 0 @@ -69,23 +69,23 @@ ScrollView { CheckBox { id: typeVisibilityCheckBox - checked: Runtime.Objects.currentObjects[objType] != undefined ? Runtime.Objects.currentObjects[objType].every(obj => obj.visible) : true + checked: Modules.Objects.currentObjects[objType] != undefined ? Modules.Objects.currentObjects[objType].every(obj => obj.visible) : true onClicked: { - for(var obj of Runtime.Objects.currentObjects[objType]) obj.visible = this.checked + for(var obj of Modules.Objects.currentObjects[objType]) obj.visible = this.checked for(var obj of objTypeList.editingRows) obj.objVisible = this.checked objectListList.changed() } ToolTip.visible: hovered ToolTip.text: checked ? - qsTr("Hide all %1").arg(Runtime.Objects.types[objType].displayTypeMultiple()) : - qsTr("Show all %1").arg(Runtime.Objects.types[objType].displayTypeMultiple()) + qsTr("Hide all %1").arg(Modules.Objects.types[objType].displayTypeMultiple()) : + qsTr("Show all %1").arg(Modules.Objects.types[objType].displayTypeMultiple()) } Label { id: typeHeaderText verticalAlignment: TextInput.AlignVCenter - text: qsTranslate("control", "%1: ").arg(Runtime.Objects.types[objType].displayTypeMultiple()) + text: qsTranslate("control", "%1: ").arg(Modules.Objects.types[objType].displayTypeMultiple()) font.pixelSize: 20 } } @@ -93,11 +93,11 @@ ScrollView { delegate: ObjectRow { id: controlRow width: objTypeList.width - obj: Runtime.Objects.currentObjects[objType][index] + obj: Modules.Objects.currentObjects[objType][index] posPicker: positionPicker onChanged: { - obj = Runtime.Objects.currentObjects[objType][index] + obj = Modules.Objects.currentObjects[objType][index] objectListList.update() } @@ -129,7 +129,7 @@ ScrollView { function update() { objectListList.changed() for(var objType in objectListList.listViews) { - objectListList.listViews[objType].model = Runtime.Objects.currentObjects[objType] + objectListList.listViews[objType].model = Modules.Objects.currentObjects[objType] } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml index cdd9e3a..0772da8 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml @@ -89,16 +89,16 @@ Item { id: objDescription anchors.left: objVisibilityCheckBox.right anchors.right: deleteButton.left - height: Runtime.Latex.enabled ? Math.max(parent.minHeight, latexDescription.height+4) : parent.minHeight + height: Modules.Latex.enabled ? Math.max(parent.minHeight, latexDescription.height+4) : parent.minHeight verticalAlignment: TextInput.AlignVCenter - text: Runtime.Latex.enabled ? "" : obj.getReadableString() + text: Modules.Latex.enabled ? "" : obj.getReadableString() font.pixelSize: 14 Image { id: latexDescription anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left - visible: Runtime.Latex.enabled + visible: Modules.Latex.enabled property double depth: Screen.devicePixelRatio property var ltxInfo: visible ? Latex.render(obj.getLatexString(), depth*(parent.font.pixelSize+2), parent.color).split(",") : ["","0","0"] source: visible ? ltxInfo[0] : "" @@ -109,7 +109,7 @@ Item { MouseArea { anchors.fill: parent onClicked: { - objEditor.obj = Runtime.Objects.currentObjects[obj.type][index] + objEditor.obj = Modules.Objects.currentObjects[obj.type][index] objEditor.objType = obj.type objEditor.objIndex = index //objEditor.editingRow = objectRow @@ -211,14 +211,14 @@ Item { function deleteRecursively(object) { for(let toRemove of object.requiredBy) deleteRecursively(toRemove) - if(Runtime.Objects.currentObjectsByName[object.name] != undefined) { + if(Modules.Objects.currentObjectsByName[object.name] != undefined) { // Object still exists // Temporary fix for objects require not being propertly updated. object.requiredBy = [] history.addToHistory(new HistoryLib.DeleteObject( object.name, object.type, object.export() )) - Runtime.Objects.deleteObject(object.name) + Modules.Objects.deleteObject(object.name) } } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml index c92182a..d8a6ac3 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml @@ -113,7 +113,7 @@ Item { if(mouse.button == Qt.LeftButton) { // Validate let newValueX = !parent.userPickX ? null : parseValue(picked.mouseX.toString(), objType, propertyX) let newValueY = !parent.userPickY ? null : parseValue(picked.mouseY.toString(), objType, propertyY) - let obj = Runtime.Objects.currentObjectsByName[objName] + let obj = Modules.Objects.currentObjectsByName[objName] // Set values if(parent.userPickX && parent.userPickY) { history.addToHistory(new HistoryLib.EditedPosition( @@ -262,7 +262,7 @@ Item { color: 'black' anchors.top: parent.top anchors.left: parent.left - anchors.leftMargin: canvas.x2px(picked.mouseX) + anchors.leftMargin: Modules.Canvas.x2px(picked.mouseX) visible: parent.userPickX } @@ -273,7 +273,7 @@ Item { color: 'black' anchors.top: parent.top anchors.left: parent.left - anchors.topMargin: canvas.y2px(picked.mouseY) + anchors.topMargin: Modules.Canvas.y2px(picked.mouseY) visible: parent.userPickY } @@ -282,25 +282,26 @@ Item { x: picker.mouseX - width - 5 y: picker.mouseY - height - 5 color: 'black' - property double axisX: canvas.xaxisstep1 + property double axisX: Modules.Canvas.axesStep.x.value + property double axisY: Modules.Canvas.axesStep.y.value property double mouseX: { - let xpos = canvas.px2x(picker.mouseX) + let xpos = Modules.Canvas.px2x(picker.mouseX) if(snapToGridCheckbox.checked) { if(canvas.logscalex) { // Calculate the logged power let pow = Math.pow(10, Math.floor(Math.log10(xpos))) return pow*Math.round(xpos/pow) } else { - return canvas.xaxisstep1*Math.round(xpos/canvas.xaxisstep1) + return axisX*Math.round(xpos/axisX) } } else { return xpos.toFixed(parent.precision) } } property double mouseY: { - let ypos = canvas.px2y(picker.mouseY) + let ypos = Modules.Canvas.px2y(picker.mouseY) if(snapToGridCheckbox.checked) { - return canvas.yaxisstep1*Math.round(ypos/canvas.yaxisstep1) + return axisY*Math.round(ypos/axisY) } else { return ypos.toFixed(parent.precision) } @@ -323,7 +324,7 @@ Item { Parses a given \c value as an expression or a number depending on the type of \c propertyName of all \c objType. */ function parseValue(value, objType, propertyName) { - if(Runtime.Objects.types[objType].properties()[propertyName] == 'number') + if(Modules.Objects.types[objType].properties()[propertyName] == 'number') return parseFloat(value) else return new MathLib.Expression(value) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml index be5160b..ad7e67c 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml @@ -391,9 +391,9 @@ Item { property string objectName: isEnteringProperty ? (parent.currentToken.dot ? parent.previousToken.value : parent.previousToken2.value) : "" - property bool doesObjectExist: isEnteringProperty && (objectName in Runtime.Objects.currentObjectsByName) + property bool doesObjectExist: isEnteringProperty && (objectName in Modules.Objects.currentObjectsByName) property var objectProperties: doesObjectExist ? - Runtime.Objects.currentObjectsByName[objectName].constructor.properties() : + Modules.Objects.currentObjectsByName[objectName].constructor.properties() : {} categoryItems: Object.keys(objectProperties) autocompleteGenerator: (item) => { @@ -460,9 +460,9 @@ Item { visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot itemStartIndex: functionsList.itemStartIndex + functionsList.model.length itemSelected: parent.itemSelected - categoryItems: Runtime.Objects.getObjectsName("ExecutableObject").filter(obj => obj != self) + categoryItems: Modules.Objects.getObjectsName("ExecutableObject").filter(obj => obj != self) autocompleteGenerator: (item) => {return { - 'text': item, 'annotation': Runtime.Objects.currentObjectsByName[item] == null ? '' : Objects.currentObjectsByName[item].constructor.displayType(), + 'text': item, 'annotation': Modules.Objects.currentObjectsByName[item] == null ? '' : Objects.currentObjectsByName[item].constructor.displayType(), 'autocomplete': item+'()', 'cursorFinalOffset': -1 }} baseText: parent.visible ? parent.currentToken.value : "" @@ -475,9 +475,9 @@ Item { visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot itemStartIndex: executableObjectsList.itemStartIndex + executableObjectsList.model.length itemSelected: parent.itemSelected - categoryItems: Object.keys(Runtime.Objects.currentObjectsByName).filter(obj => obj != self) + categoryItems: Object.keys(Modules.Objects.currentObjectsByName).filter(obj => obj != self) autocompleteGenerator: (item) => {return { - 'text': item, 'annotation': `${Runtime.Objects.currentObjectsByName[item].constructor.displayType()}`, + 'text': item, 'annotation': `${Modules.Objects.currentObjectsByName[item].constructor.displayType()}`, 'autocomplete': item+'.', 'cursorFinalOffset': 0 }} baseText: parent.visible ? parent.currentToken.value : "" @@ -537,8 +537,8 @@ Item { throw new Error(qsTranslate('error', 'Object cannot be dependent on itself.')) // Recursive dependencies let dependentOnSelfObjects = expr.requiredObjects().filter( - (obj) => Runtime.Objects.currentObjectsByName[obj].getDependenciesList() - .includes(Runtime.Objects.currentObjectsByName[control.self]) + (obj) => Modules.Objects.currentObjectsByName[obj].getDependenciesList() + .includes(Modules.Objects.currentObjectsByName[control.self]) ) if(dependentOnSelfObjects.length == 1) throw new Error(qsTranslate('error', 'Circular dependency detected. Object %1 depends on %2.').arg(dependentOnSelfObjects[0].toString()).arg(control.self)) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml index 2c50c56..b15caed 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml @@ -222,10 +222,10 @@ ScrollView { label: qsTr("Max X") icon: "settings/xmax.svg" width: settings.settingWidth - value: canvas.px2x(canvas.canvasSize.width).toFixed(2) + value: Modules.Canvas.px2x(canvas.width).toFixed(2) onChanged: function(xvaluemax) { if(xvaluemax > settings.xmin) { - settings.xzoom = settings.xzoom * canvas.canvasSize.width/(canvas.x2px(xvaluemax)) // Adjusting zoom to fit. = (end)/(px of current point) + settings.xzoom = settings.xzoom * canvas.width/(Modules.Canvas.x2px(xvaluemax)) // Adjusting zoom to fit. = (end)/(px of current point) settings.changed() } else { alert.show("Maximum x value must be superior to minimum.") @@ -241,10 +241,10 @@ ScrollView { label: qsTr("Min Y") icon: "settings/ymin.svg" width: settings.settingWidth - defValue: canvas.px2y(canvas.canvasSize.height).toFixed(2) + defValue: Modules.Canvas.px2y(canvas.height).toFixed(2) onChanged: function(yvaluemin) { if(yvaluemin < settings.ymax) { - settings.yzoom = settings.yzoom * canvas.canvasSize.height/(canvas.y2px(yvaluemin)) // Adjusting zoom to fit. = (end)/(px of current point) + settings.yzoom = settings.yzoom * canvas.height/(Modules.Canvas.y2px(yvaluemin)) // Adjusting zoom to fit. = (end)/(px of current point) settings.changed() } else { alert.show("Minimum y value must be inferior to maximum.") diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ViewPositionChangeOverlay.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ViewPositionChangeOverlay.qml index 85b461e..3d5ff68 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ViewPositionChangeOverlay.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ViewPositionChangeOverlay.qml @@ -94,7 +94,7 @@ Item { property int positionChangeTimer: 0 function updatePosition(deltaX, deltaY) { - settingsInstance.xmin = (canvas.px2x(canvas.x2px(settingsInstance.xmin)-deltaX)) + settingsInstance.xmin = (Modules.Canvas.px2x(Modules.Canvas.x2px(settingsInstance.xmin)-deltaX)) settingsInstance.ymax += deltaY/canvas.yzoom settingsInstance.ymax = settingsInstance.ymax.toFixed(4) settingsInstance.changed() @@ -107,6 +107,7 @@ Item { prevY = mouse.y parent.beginPositionChange() } + onPositionChanged: function(mouse) { positionChangeTimer++ if(positionChangeTimer == 3) { @@ -118,12 +119,14 @@ Item { positionChangeTimer = 0 } } + onReleased: function(mouse) { let deltaX = mouse.x - prevX let deltaY = mouse.y - prevY updatePosition(deltaX, deltaY) parent.endPositionChange(deltaX, deltaY) } + onWheel: function(wheel) { // Scrolling let scrollSteps = Math.round(wheel.angleDelta.y / 120) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/modules.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/autoload.js similarity index 87% rename from LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/modules.js rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/autoload.js index 773f1c3..4c79c64 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/modules.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/autoload.js @@ -5,3 +5,4 @@ .import "objs/autoload.mjs" as Autoload .import "math/latex.mjs" as Latex .import "history/common.mjs" as HistoryCommon +.import "canvas.mjs" as CanvasAPI \ No newline at end of file diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/canvas.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/canvas.mjs new file mode 100644 index 0000000..d68bd9d --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/canvas.mjs @@ -0,0 +1,523 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * 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 3 of the License, or + * (at your option) 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 . + */ + +import {Module} from "./modules.mjs" +import {textsup} from "./utils.mjs" +import {Expression} from "./mathlib.mjs" + + +class CanvasAPI extends Module { + + constructor() { + super('Canvas', [ + Modules.Objects, + Modules.History + ]) + + /** @type {HTMLCanvasElement} */ + this._canvas = null + + /** @type {CanvasRenderingContext2D} */ + this._ctx = null + + /** + * @type {Object} + * @property {function(string, string, string)} showDialog + * @private + */ + this._drawingErrorDialog = null + /** + * + * @type {Object.} + */ + this.axesSteps = { + x: { + expression: null, + value: -1, + maxDraw: -1 + }, + y: { + expression: null, + value: -1, + maxDraw: -1 + } + } + } + + initialize(canvasObject, drawingErrorDialog) { + this._canvas = canvasObject + this._drawingErrorDialog = drawingErrorDialog + } + + get width() { return this._canvas.width } + + get height() { return this._canvas.height } + + /** + * Minimum x of the diagram, provided from settings. + * @returns {number} + */ + get xmin() { return this._canvas.xmin } + + /** + * Zoom on the x-axis of the diagram, provided from settings. + * @returns {number} + */ + get xzoom() { return this._canvas.xzoom } + + /** + * Maximum y of the diagram, provided from settings. + * @returns {number} + */ + get ymax() { return this._canvas.ymax } + + /** + * Zoom on the y-axis of the diagram, provided from settings. + * @returns {number} + */ + get yzoom() { return this._canvas.yzoom } + + /** + * Label used on the x-axis, provided from settings. + * @returns {string} + */ + get xlabel() { return this._canvas.xlabel } + + /** + * Label used on the y-axis, provided from settings. + * @returns {string} + */ + get ylabel() { return this._canvas.ylabel } + + /** + * Width of lines that will be drawn into the canvas, provided from settings. + * @returns {number} + */ + get linewidth() { return this._canvas.linewidth } + + /** + * Font size of the text that will be drawn into the canvas, provided from settings. + * @returns {number} + */ + get textsize() { return this._canvas.textsize } + + /** + * True if the canvas should be in logarithmic mode, false otherwise. + * @returns {boolean} + */ + get logscalex() { return this._canvas.logscalex } + + /** + * True if the x graduation should be shown, false otherwise. + * @returns {boolean} + */ + get showxgrad() { return this._canvas.showxgrad } + + /** + * True if the y graduation should be shown, false otherwise. + * @returns {boolean} + */ + get showygrad() { return this._canvas.showygrad } + + /** + * Max power of the logarithmic scaled on the x axis in logarithmic mode. + * @returns {number} + */ + get maxgradx() { return this._canvas.maxgradx } + + // + // Methods to draw the canvas + // + + /** + * Redraws the entire canvas + */ + redraw() { + this._ctx = this._canvas.getContext("2d") + this._computeAxes() + this._reset() + this._drawGrid() + this._drawAxes() + this._drawLabels() + this._ctx.lineWidth = this.linewidth + for(let objType in Modules.Objects.currentObjects) { + for(let obj of Modules.Objects.currentObjects[objType]){ + this._ctx.strokeStyle = obj.color + this._ctx.fillStyle = obj.color + if(obj.visible) + try { + obj.draw(this) + } catch(e) { + // Drawing throws an error. Generally, it's due to a new modification (or the opening of a file) + this._drawingErrorDialog.showDialog(objType, obj.name, e.message) + Modules.History.undo() + } + } + } + this._ctx.lineWidth = 1 + } + + /** + * Calculates informations for drawing gradations for axes. + * @private + */ + _computeAxes() { + let exprY = new Expression(`x*(${this._canvas.yaxisstep})`) + let y1 = exprY.execute(1) + let exprX = new Expression(`x*(${this._canvas.xaxisstep})`) + let x1 = exprX.execute(1) + this.axesSteps = { + x: { + expression: exprX, + value: x1, + maxDraw: Math.ceil(Math.max(Math.abs(this.xmin), Math.abs(this.px2x(this.width)))/x1) + }, + y: { + expression: exprY, + value: y1, + maxDraw: Math.ceil(Math.max(Math.abs(this.ymax), Math.abs(this.px2y(this.height)))/y1) + } + } + } + + /** + * Resets the canvas to a blank one with default setting. + * @private + */ + _reset(){ + // Reset + this._ctx.fillStyle = "#FFFFFF" + this._ctx.strokeStyle = "#000000" + this._ctx.font = `${this.textsize}px sans-serif` + this._ctx.fillRect(0,0,this.width,this.height) + } + + /** + * Draws the grid. + * @private + */ + _drawGrid() { + this._ctx.strokeStyle = "#C0C0C0" + if(this.logscalex) { + for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow++) { + for(let xmulti = 1; xmulti < 10; xmulti++) { + this.drawXLine(Math.pow(10, xpow)*xmulti) + } + } + } else { + for(let x = 0; x < this.axesSteps.x.maxDraw; x+=1) { + this.drawXLine(x*this.axesSteps.x.value) + this.drawXLine(-x*this.axesSteps.x.value) + } + } + for(let y = 0; y < this.axesSteps.y.maxDraw; y+=1) { + this.drawYLine(y*this.axesSteps.y.value) + this.drawYLine(-y*this.axesSteps.y.value) + } + } + + /** + * Draws the graph axes. + * @private + */ + _drawAxes() { + this._ctx.strokeStyle = "#000000" + let axisypos = this.logscalex ? 1 : 0 + this.drawXLine(axisypos) + this.drawYLine(0) + let axisypx = this.x2px(axisypos) // X coordinate of Y axis + let axisxpx = this.y2px(0) // Y coordinate of X axis + // Drawing arrows + this.drawLine(axisypx, 0, axisypx-10, 10) + this.drawLine(axisypx, 0, axisypx+10, 10) + this.drawLine(this.width, axisxpx, this.width-10, axisxpx-10) + this.drawLine(this.width, axisxpx, this.width-10, axisxpx+10) + } + + /** + * Resets the canvas to a blank one with default setting. + * @private + */ + _drawLabels() { + let axisypx = this.x2px(this.logscalex ? 1 : 0) // X coordinate of Y axis + let axisxpx = this.y2px(0) // Y coordinate of X axis + // Labels + this._ctx.fillStyle = "#000000" + this._ctx.font = `${this.textsize}px sans-serif` + this._ctx.fillText(this.ylabel, axisypx+10, 24) + let textWidth = this._ctx.measureText(this.xlabel).width + this._ctx.fillText(this.xlabel, this.width-14-textWidth, axisxpx-5) + // Axis graduation labels + this._ctx.font = `${this.textsize-4}px sans-serif` + + let txtMinus = this._ctx.measureText('-').width + if(this.showxgrad) { + if(this.logscalex) { + for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow+=1) { + textWidth = this._ctx.measureText("10"+textsup(xpow)).width + if(xpow !== 0) + this.drawVisibleText("10"+textsup(xpow), this.x2px(Math.pow(10,xpow))-textWidth/2, axisxpx+16+(6*(xpow===1))) + } + } else { + for(let x = 1; x < this.axesSteps.x.maxDraw; x += 1) { + let drawX = x*this.axesSteps.x.value + let txtX = this.axesSteps.x.expression.simplify(x).replace(/^\((.+)\)$/, '$1') + let textHeight = this.measureText(txtX).height + this.drawVisibleText(txtX, this.x2px(drawX)-4, axisxpx+this.textsize/2+textHeight) + this.drawVisibleText('-'+txtX, this.x2px(-drawX)-4, axisxpx+this.textsize/2+textHeight) + } + } + } + if(this.showygrad) { + for(let y = 0; y < this.axesSteps.y.maxDraw; y += 1) { + let drawY = y*this.axesSteps.y.value + let txtY = this.axesSteps.y.expression.simplify(y).replace(/^\((.+)\)$/, '$1') + textWidth = this._ctx.measureText(txtY).width + this.drawVisibleText(txtY, axisypx-6-textWidth, this.y2px(drawY)+4+(10*(y===0))) + if(y !== 0) + this.drawVisibleText('-'+txtY, axisypx-6-textWidth-txtMinus, this.y2px(-drawY)+4) + } + } + this._ctx.fillStyle = "#FFFFFF" + } + + // + // Public functions + // + + /** + * Draws an horizontal line at x plot coordinate. + * @param {number} x + */ + drawXLine(x) { + if(this.isVisible(x, this.ymax)) { + this.drawLine(this.x2px(x), 0, this.x2px(x), this.height) + } + } + + /** + * Draws an vertical line at y plot coordinate + * @param {number} y + * @private + */ + drawYLine(y) { + if(this.isVisible(this.xmin, y)) { + this.drawLine(0, this.y2px(y), this.width, this.y2px(y)) + } + } + + /** + * Writes multiline text onto the canvas. + * NOTE: The x and y properties here are relative to the canvas, not the plot. + * @param {string} text + * @param {number} x + * @param {number} y + */ + drawVisibleText(text, x, y) { + if(x > 0 && x < this.width && y > 0 && y < this.height) { + text.toString().split("\n").forEach((txt, i) => { + this._ctx.fillText(txt, x, y+(this.textsize*i)) + }) + } + } + + /** + * Draws an image onto the canvas. + * NOTE: The x, y width and height properties here are relative to the canvas, not the plot. + * @param {CanvasImageSource} image + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + */ + drawVisibleImage(image, x, y, width, height) { + this._canvas.markDirty(Qt.rect(x, y, width, height)); + this._ctx.drawImage(image, x, y, width, height) + } + + /** + * Measures the width and height of a multiline text that would be drawn onto the canvas. + * @param {string} text + * @returns {{width: number, height: number}} + */ + measureText(text) { + let theight = 0 + let twidth = 0 + let defaultHeight = this.textsize * 1.2 // Approximate but good enough! + for(let txt of text.split("\n")) { + theight += defaultHeight + if(this._ctx.measureText(txt).width > twidth) twidth = this._ctx.measureText(txt).width + } + return {'width': twidth, 'height': theight} + } + + /** + * Converts an x coordinate to its relative position on the canvas. + * It supports both logarithmic and non-logarithmic scale depending on the currently selected mode. + * @param {number} x + * @returns {number} + */ + x2px(x) { + if(this.logscalex) { + let logxmin = Math.log(this.xmin) + return (Math.log(x)-logxmin)*this.xzoom + } else return (x - this.xmin)*this.xzoom + } + + /** + * Converts an y coordinate to it's relative position on the canvas. + * The y-axis not supporting logarithmic scale, it only supports linear conversion. + * @param {number} y + * @returns {number} + */ + y2px(y) { + return (this.ymax-y)*this.yzoom + } + + /** + * Converts an x px position on the canvas to it's corresponding coordinate on the plot. + * It supports both logarithmic and non-logarithmic scale depending on the currently selected mode. + * @param {number} px + * @returns {number} + */ + px2x(px) { + if(this.logscalex) { + return Math.exp(px/this.xzoom+Math.log(this.xmin)) + } else return (px/this.xzoom+this.xmin) + } + + /** + * Converts an x px position on the canvas to it's corresponding coordinate on the plot. + * It supports both logarithmic and non logarithmic scale depending on the currently selected mode. + * @param {number} px + * @returns {number} + */ + px2y(px) { + return -(px/this.yzoom-this.ymax) + } + + /** + * Checks whether a plot point (x, y) is visible or not on the canvas. + * @param {number} x + * @param {number} y + * @returns {boolean} + */ + isVisible(x, y) { + return (this.x2px(x) >= 0 && this.x2px(x) <= this.width) && (this.y2px(y) >= 0 && this.y2px(y) <= this.height) + } + + /** + * Draws a line from plot point (x1, y1) to plot point (x2, y2). + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + */ + drawLine(x1, y1, x2, y2) { + this._ctx.beginPath(); + this._ctx.moveTo(x1, y1); + this._ctx.lineTo(x2, y2); + this._ctx.stroke(); + } + + /** + * Draws a dashed line from plot point (x1, y1) to plot point (x2, y2). + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} dashPxSize + */ + drawDashedLine(x1, y1, x2, y2, dashPxSize = 6) { + this._ctx.setLineDash([dashPxSize/2, dashPxSize]); + this.drawLine(x1, y1, x2, y2) + this._ctx.setLineDash([]); + } + + /** + * Renders latex markup ltxText to an image and loads it. Returns a dictionary with three values: source, width and height. + * @param {string} ltxText + * @param {string} color + * @param {function({width: number, height: number, source: string})} callback + */ + renderLatexImage(ltxText, color, callback) { + let [ltxSrc, ltxWidth, ltxHeight] = Latex.render(ltxText, this.textsize, color).split(",") + let imgData = { + "source": ltxSrc, + "width": parseFloat(ltxWidth), + "height": parseFloat(ltxHeight) + }; + if(!this._canvas.isImageLoaded(ltxSrc) && !this._canvas.isImageLoading(ltxSrc)){ + // Wait until the image is loaded to callback. + this._canvas.loadImage(ltxSrc) + this._canvas.imageLoaders[ltxSrc] = [callback, imgData] + } else { + // Callback directly + callback(imgData) + } + } + + // + // Context methods + // + + get font() { return this._ctx.font } + set font(value) { return this._ctx.font = value } + + /** + * Draws an act on the canvas centered on a point. + * @param {number} x + * @param {number} y + * @param {number} radius + * @param {number} startAngle + * @param {number} endAngle + * @param {boolean} counterclockwise + */ + arc(x, y, radius, startAngle, endAngle, counterclockwise=false) { + this._ctx.beginPath() + this._ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise) + this._ctx.stroke() + } + + /** + * Draws a filled circle centered on a point. + * @param {number} x + * @param {number} y + * @param {number} radius + */ + disc(x, y, radius) { + this._ctx.beginPath(); + this._ctx.arc(x, y, radius, 0, 2 * Math.PI) + this._ctx.fill(); + } + + /** + * Draws a filled rectangle onto the canvas. + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + */ + fillRect(x, y, w, h) { + this._ctx.fillRect(x, y, w, h) + } +} + +/** @type {CanvasAPI} */ +Modules.Canvas = Modules.Canvas || new CanvasAPI() +export const API = Modules.Canvas \ No newline at end of file diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs index 78476ca..b662098 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs @@ -16,14 +16,14 @@ * along with this program. If not, see . */ -import { RuntimeAPI } from "../runtime.mjs" +import { Module } from "../modules.mjs" import Latex from "../math/latex.mjs" -class HistoryCommonAPI extends RuntimeAPI { +class HistoryCommonAPI extends Module { constructor() { super('History', [ - Runtime.Latex + Modules.Latex ]) // History QML object this.history = null; @@ -31,12 +31,16 @@ class HistoryCommonAPI extends RuntimeAPI { this.imageDepth = 2; this.fontSize = 14; } + + undo() { this.history.undo() } + redo() { this.history.redo() } + addToHistory(action) { this.history.addToHistory(action) } } /** @type {HistoryCommonAPI} */ -Runtime.History = Runtime.History || new HistoryCommonAPI() +Modules.History = Modules.History || new HistoryCommonAPI() -export const API = Runtime.History +export const API = Modules.History export class Action { /** @@ -95,7 +99,7 @@ export class Action { * @returns {string} */ getIconRichText(type) { - return `` + return `` } /** @@ -107,11 +111,11 @@ export class Action { renderLatexAsHtml(latexString) { if(!Latex.enabled) throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.") - let imgDepth = Runtime.History.imageDepth + let imgDepth = Modules.History.imageDepth let [src, width, height] = Latex.render( latexString, - imgDepth * (Runtime.History.fontSize + 2), - Runtime.History.themeTextColor + imgDepth * (Modules.History.fontSize + 2), + Modules.History.themeTextColor ).split(",") return `` } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/expr-eval/integration.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/expr-eval/integration.js index 68a88c1..998ed32 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/expr-eval/integration.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/expr-eval/integration.js @@ -19,9 +19,7 @@ .pragma library .import "expr-eval.js" as ExprEval -.import "../../runtime.mjs" as R - -let RuntimeAPI = R.RuntimeAPI +.import "../../modules.mjs" as M const evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manually @@ -38,11 +36,11 @@ const evalVariables = { "false": false } -class ExprParserAPI extends RuntimeAPI { +class ExprParserAPI extends M.Module { constructor() { super('ExprParser', [ /** @type {ObjectsAPI} */ - Runtime.Objects + Modules.Objects ]) this.currentVars = {} this.Internals = ExprEval @@ -115,7 +113,7 @@ class ExprParserAPI extends RuntimeAPI { } /** @type {ExprParserAPI} */ -Runtime.ExprParser = Runtime.ExprParser || new ExprParserAPI() +Modules.ExprParser = Modules.ExprParser || new ExprParserAPI() diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/qmlpolyfills.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/qmlpolyfills.mjs index a940967..5302aca 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/qmlpolyfills.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/qmlpolyfills.mjs @@ -19,7 +19,7 @@ // Type polyfills for IDEs. // Never directly imported. -Runtime = Runtime || {} +Modules = Modules || {} /** @type {function(string, string): string} */ qsTranslate = qsTranslate || function(category, string) { throw new Error('qsTranslate not implemented.'); } /** @type {function(string): string} */ @@ -29,6 +29,19 @@ QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Er /** @type {function(string|boolean|int): string} */ String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); } +const Qt = { + /** + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + * @returns {{x, width, y, height}} + */ + rect: function(x, y, width, height) { + return {x: x, y: y, width: width, height: height}; + } +} + /** Typehints for Helper. */ const Helper = { /** @type {function(string): boolean} */ diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.mjs index 3a16499..66cd044 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.mjs @@ -25,16 +25,16 @@ import * as Utils from "../utils.mjs" */ export class Expression { constructor(expr) { - if(!Runtime.ExprParser) + if(!Modules.ExprParser) throw new Error('Expression parser not initialized.') - if(!Runtime.Objects) + if(!Modules.Objects) throw new Error('Objects API not initialized.') this.expr = Utils.exponentsToExpression(expr) - this.calc = Runtime.ExprParser.parse(this.expr).simplify() + this.calc = Modules.ExprParser.parse(this.expr).simplify() this.cached = this.isConstant() this.cachedValue = null if(this.cached && this.allRequirementsFullfilled()) - this.cachedValue = this.calc.evaluate(Runtime.Objects.currentObjectsByName) + this.cachedValue = this.calc.evaluate(Modules.Objects.currentObjectsByName) this.latexMarkup = Latex.expression(this.calc.tokens) } @@ -48,26 +48,26 @@ export class Expression { } allRequirementsFullfilled() { - return this.requiredObjects().every(objName => objName in Runtime.Objects.currentObjectsByName) + return this.requiredObjects().every(objName => objName in Modules.Objects.currentObjectsByName) } undefinedVariables() { - return this.requiredObjects().filter(objName => !(objName in Runtime.Objects.currentObjectsByName)) + return this.requiredObjects().filter(objName => !(objName in Modules.Objects.currentObjectsByName)) } recache() { if(this.cached) - this.cachedValue = this.calc.evaluate(Runtime.Objects.currentObjectsByName) + this.cachedValue = this.calc.evaluate(Modules.Objects.currentObjectsByName) } execute(x = 1) { if(this.cached) { if(this.cachedValue == null) - this.cachedValue = this.calc.evaluate(Runtime.Objects.currentObjectsByName) + this.cachedValue = this.calc.evaluate(Modules.Objects.currentObjectsByName) return this.cachedValue } - Runtime.ExprParser.currentVars = Object.assign({'x': x}, Runtime.Objects.currentObjectsByName) - return this.calc.evaluate(Runtime.ExprParser.currentVars) + Modules.ExprParser.currentVars = Object.assign({'x': x}, Modules.Objects.currentObjectsByName) + return this.calc.evaluate(Modules.ExprParser.currentVars) } simplify(x) { diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.mjs index ef1f862..7418b9c 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.mjs @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { RuntimeAPI } from '../runtime.mjs' +import { Module } from '../modules.mjs' const unicodechars = ["α","β","γ","δ","ε","ζ","η", "π","θ","κ","λ","μ","ξ","ρ", @@ -39,13 +39,12 @@ const equivalchars = ["\\alpha","\\beta","\\gamma","\\delta","\\epsilon","\\zeta "{}_{4}","{}_{5}","{}_{6}","{}_{7}","{}_{8}","{}_{9}","{}_{0}", "\\pi", "\\infty"] -console.log(Runtime.ExprParser) - -class LatexAPI extends RuntimeAPI { + +class LatexAPI extends Module { constructor() { super('Latex', [ /** @type {ExprParserAPI} */ - Runtime.ExprParser + Modules.ExprParser ]) /** * true if latex has been enabled by the user, false otherwise. @@ -161,18 +160,18 @@ class LatexAPI extends RuntimeAPI { let type = item.type switch(type) { - case Runtime.ExprParser.Internals.INUMBER: + case Modules.ExprParser.Internals.INUMBER: if(item.value === Infinity) { nstack.push("\\infty") } else if(typeof item.value === 'number' && item.value < 0) { nstack.push(this.par(item.value)); } else if(Array.isArray(item.value)) { - nstack.push('[' + item.value.map(Runtime.ExprParser.Internals.escapeValue).join(', ') + ']'); + nstack.push('[' + item.value.map(Modules.ExprParser.Internals.escapeValue).join(', ') + ']'); } else { - nstack.push(Runtime.ExprParser.Internals.escapeValue(item.value)); + nstack.push(Modules.ExprParser.Internals.escapeValue(item.value)); } break; - case Runtime.ExprParser.Internals.IOP2: + case Modules.ExprParser.Internals.IOP2: n2 = nstack.pop(); n1 = nstack.pop(); f = item.value; @@ -211,7 +210,7 @@ class LatexAPI extends RuntimeAPI { throw new EvalError("Unknown operator " + ope + "."); } break; - case Runtime.ExprParser.Internals.IOP3: // Thirdiary operator + case Modules.ExprParser.Internals.IOP3: // Thirdiary operator n3 = nstack.pop(); n2 = nstack.pop(); n1 = nstack.pop(); @@ -222,11 +221,11 @@ class LatexAPI extends RuntimeAPI { throw new EvalError('Unknown operator ' + ope + '.'); } break; - case Runtime.ExprParser.Internals.IVAR: - case Runtime.ExprParser.Internals.IVARNAME: + case Modules.ExprParser.Internals.IVAR: + case Modules.ExprParser.Internals.IVARNAME: nstack.push(this.variable(item.value.toString())); break; - case Runtime.ExprParser.Internals.IOP1: // Unary operator + case Modules.ExprParser.Internals.IOP1: // Unary operator n1 = nstack.pop(); f = item.value; switch(f) { @@ -242,7 +241,7 @@ class LatexAPI extends RuntimeAPI { break; } break; - case Runtime.ExprParser.Internals.IFUNCALL: + case Modules.ExprParser.Internals.IFUNCALL: argCount = item.value; args = []; while (argCount-- > 0) { @@ -252,14 +251,14 @@ class LatexAPI extends RuntimeAPI { // Handling various functions nstack.push(this.functionToLatex(f, args)) break; - case Runtime.ExprParser.Internals.IFUNDEF: + case Modules.ExprParser.Internals.IFUNDEF: nstack.push(this.par(n1 + '(' + args.join(', ') + ') = ' + n2)); break; - case Runtime.ExprParser.Internals.IMEMBER: + case Modules.ExprParser.Internals.IMEMBER: n1 = nstack.pop(); nstack.push(n1 + '.' + item.value); break; - case Runtime.ExprParser.Internals.IARRAY: + case Modules.ExprParser.Internals.IARRAY: argCount = item.value; args = []; while (argCount-- > 0) { @@ -267,10 +266,10 @@ class LatexAPI extends RuntimeAPI { } nstack.push('[' + args.join(', ') + ']'); break; - case Runtime.ExprParser.Internals.IEXPR: + case Modules.ExprParser.Internals.IEXPR: nstack.push('(' + this.expression(item.value) + ')'); break; - case Runtime.ExprParser.Internals.IENDSTATEMENT: + case Modules.ExprParser.Internals.IENDSTATEMENT: break; default: throw new EvalError('invalid Expression'); @@ -284,6 +283,6 @@ class LatexAPI extends RuntimeAPI { } /** @type {LatexAPI} */ -Runtime.Latex = Runtime.Latex || new LatexAPI() +Modules.Latex = Modules.Latex || new LatexAPI() -export default Runtime.Latex +export default Modules.Latex diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.mjs index 2dafdcd..a1a96cd 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.mjs @@ -33,7 +33,7 @@ export class Sequence extends Expr.Expression { this.latexValues = Object.assign({}, baseValues) for(let n in this.calcValues) if(['string', 'number'].includes(typeof this.calcValues[n])) { - let parsed = Runtime.ExprParser.parse(this.calcValues[n].toString()).simplify() + let parsed = Modules.ExprParser.parse(this.calcValues[n].toString()).simplify() this.latexValues[n] = Latex.expression(parsed.tokens) this.calcValues[n] = parsed.evaluate() } @@ -60,17 +60,17 @@ export class Sequence extends Expr.Expression { cache(n = 1) { let str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString()) - let expr = Runtime.ExprParser.parse(str).simplify() + let expr = Modules.ExprParser.parse(str).simplify() // Cache values required for this one. if(!this.calcValues[n-this.valuePlus] && n-this.valuePlus > 0) this.cache(n-this.valuePlus) // Setting current variables - Runtime.ExprParser.currentVars = Object.assign( + Modules.ExprParser.currentVars = Object.assign( {'n': n-this.valuePlus}, // Just in case, add n (for custom functions) - Runtime.Objects.currentObjectsByName, + Modules.Objects.currentObjectsByName, {[this.name]: this.calcValues} ) - this.calcValues[n] = expr.evaluate(Runtime.ExprParser.currentVars) + this.calcValues[n] = expr.evaluate(Modules.ExprParser.currentVars) } toString(forceSign=false) { diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/runtime.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/modules.mjs similarity index 89% rename from LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/runtime.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/modules.mjs index 0d859aa..1b68b89 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/runtime.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/modules.mjs @@ -19,12 +19,12 @@ /** * Base class for global APIs in runtime. */ -export class RuntimeAPI { +export class Module { /** * * @param {string} name - Name of the API - * @param {(RuntimeAPI|undefined)[]} requires - List of APIs required to initialize this one. + * @param {(Module|undefined)[]} requires - List of APIs required to initialize this one. */ constructor(name, requires = []) { console.log(`Loading module ${name}...`) @@ -33,7 +33,7 @@ export class RuntimeAPI { /** * Checks if all requirements are defined. - * @param {(RuntimeAPI|undefined)[]} requires + * @param {(Module|undefined)[]} requires * @param {string} name */ __check_requirements(requires, name) { diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.mjs index b7a2dae..c45b65e 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.mjs @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -import { RuntimeAPI } from './runtime.mjs' +import { Module } from './modules.mjs' -class ObjectsAPI extends RuntimeAPI { +class ObjectsAPI extends Module { constructor() { super('Objects') @@ -107,6 +107,6 @@ class ObjectsAPI extends RuntimeAPI { } /** @type {ObjectsAPI} */ -Runtime.Objects = Runtime.Objects || new ObjectsAPI() +Modules.Objects = Modules.Objects || new ObjectsAPI() -export default Runtime.Objects +export default Modules.Objects diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/autoload.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/autoload.mjs index c3e8f63..f29710b 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/autoload.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/autoload.mjs @@ -28,7 +28,7 @@ import XCursor from "xcursor.mjs" import Sequence from "sequence.mjs" import RepartitionFunction from "repartition.mjs" -if(Object.keys(Runtime.Objects.types).length === 0) { +if(Object.keys(Modules.Objects.types).length === 0) { ObjectsCommonAPI.registerObject(Point) ObjectsCommonAPI.registerObject(Text) ObjectsCommonAPI.registerObject(Function) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.mjs index 7821037..5fb31ba 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.mjs @@ -19,17 +19,17 @@ import { getRandomColor, textsub } from "../utils.mjs" import Objects from "../objects.mjs" import Latex from "../math/latex.mjs" -import {RuntimeAPI} from "../runtime.mjs" +import {Module} from "../modules.mjs" // This file contains the default data to be imported from all other objects -class ObjectsCommonAPI extends RuntimeAPI { +class ObjectsCommonAPI extends Module { constructor() { super('ObjectsCommon', [ - Runtime.Objects, - Runtime.ExprParser, - Runtime.Latex + Modules.Objects, + Modules.ExprParser, + Modules.Latex ]) } @@ -72,9 +72,9 @@ class ObjectsCommonAPI extends RuntimeAPI { } /** @type {ObjectsCommonAPI} */ -Runtime.ObjectsCommon = Runtime.ObjectsCommon || new ObjectsCommonAPI() +Modules.ObjectsCommon = Modules.ObjectsCommon || new ObjectsCommonAPI() -export const API = Runtime.ObjectsCommon +export const API = Modules.ObjectsCommon /** * Class to extend for every type of object that @@ -269,18 +269,17 @@ export class DrawableObject { for(let toRemove of this.requiredBy) { // Normally, there should be none here, but better leave nothing just in case. Objects.deleteObject(toRemove.name) } - console.log(this.requires) for(let toRemoveFrom of this.requires) { toRemoveFrom.requiredBy = toRemoveFrom.requiredBy.filter(o => o !== this) } } /** - * Abstract method. Draw the object onto the \c canvas with the 2D context \c ctx. - * @param {Canvas} canvas + * Abstract method. Draw the object onto the \c canvas with the. + * @param {CanvasAPI} canvas * @param {CanvasRenderingContext2D} ctx */ - draw(canvas, ctx) {} + draw(canvas) {} /** * Applicates a \c drawFunction with two position arguments depending on @@ -343,7 +342,7 @@ export class DrawableObject { * Then, it's displayed using the \c drawFunctionLatex (x,y,imageData) and * \c drawFunctionText (x,y,text) depending on whether to use latex. * - * @param {Canvas} canvas + * @param {CanvasAPI} canvas * @param {CanvasRenderingContext2D} ctx * @param {string|Enum} labelPosition - Position of the label relative to the marked position * @param {number} posX - Component of the marked position on the x-axis @@ -354,7 +353,7 @@ export class DrawableObject { * @param {function|null} drawFunctionLatex - Function (x,y,imageData) to display the latex image * @param {function|null} drawFunctionText - Function (x,y,text,textSize) to display the text */ - drawLabel(canvas, ctx, labelPosition, posX, posY,forceText = false, + drawLabel(canvas, labelPosition, posX, posY,forceText = false, getLatexFunction = null, getTextFunction = null, drawFunctionLatex = null, drawFunctionText = null) { // Default functions if(getLatexFunction == null) @@ -362,25 +361,25 @@ export class DrawableObject { if(getTextFunction == null) getTextFunction = this.getLabel.bind(this) if(drawFunctionLatex == null) - drawFunctionLatex = (x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, y, ltxImg.width, ltxImg.height) + drawFunctionLatex = (x,y,ltxImg) => canvas.drawVisibleImage(ltxImg.source, x, y, ltxImg.width, ltxImg.height) if(drawFunctionText == null) - drawFunctionText = (x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, y+textSize.height) // Positioned from left bottom + drawFunctionText = (x,y,text,textSize) => canvas.drawVisibleText(text, x, y+textSize.height) // Positioned from left bottom // Drawing the label let offset if(!forceText && Latex.enabled) { // With latex - let drawLblCb = function(canvas, ctx, ltxImg) { - this.drawPositionDivergence(labelPosition, 8+ctx.lineWidth/2, ltxImg, posX, posY, (x,y) => drawFunctionLatex(x,y,ltxImg)) - } + let drawLblCb = ((ltxImg) => { + this.drawPositionDivergence(labelPosition, 8+canvas.linewidth/2, ltxImg, posX, posY, (x,y) => drawFunctionLatex(x,y,ltxImg)) + }).bind(this) let ltxLabel = getLatexFunction(); - if(ltxLabel != "") + if(ltxLabel !== "") canvas.renderLatexImage(ltxLabel, this.color, drawLblCb.bind(this)) } else { // Without latex let text = getTextFunction() - ctx.font = `${canvas.textsize}px sans-serif` - let textSize = canvas.measureText(ctx, text) - this.drawPositionDivergence(labelPosition, 8+ctx.lineWidth/2, textSize, posX, posY, (x,y) => drawFunctionText(x,y,text,textSize)) + canvas.font = `${canvas.textsize}px sans-serif` + let textSize = canvas.measureText(text) + this.drawPositionDivergence(labelPosition, 8+canvas.linewidth/2, textSize, posX, posY, (x,y) => drawFunctionText(x,y,text,textSize)) } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.mjs index 1a52dba..0d68830 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.mjs @@ -104,17 +104,17 @@ export default class Function extends ExecutableObject { return '' } - draw(canvas, ctx) { - Function.drawFunction(canvas, ctx, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines) + draw(canvas) { + Function.drawFunction(canvas, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines) // Label - this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) + this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) } /** * Reusable in other objects. * Drawing small traits every few pixels */ - static drawFunction(canvas, ctx, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) { + static drawFunction(canvas, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) { let pxprecision = 10 let previousX = canvas.px2x(0) let previousY = null; @@ -124,7 +124,7 @@ export default class Function extends ExecutableObject { if(previousX === null) previousX = definitionDomain.next(canvas.px2x(0)) previousY = expr.execute(previousX) if(!drawPoints && !drawDash) return - while(previousX !== null && canvas.x2px(previousX) < canvas.canvasSize.width) { + while(previousX !== null && canvas.x2px(previousX) < canvas.width) { // Reconverted for canvas to fix for logarithmic scales. let currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX)+pxprecision)); let currentY = expr.execute(currentX) @@ -132,10 +132,10 @@ export default class Function extends ExecutableObject { if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) && (destinationDomain.includes(currentY) || destinationDomain.includes(previousY))) { if(drawDash) - canvas.drawDashedLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY)) + canvas.drawDashedLine(canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY)) if(drawPoints) { - ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2) - ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10) + canvas.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2) + canvas.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10) } } previousX = currentX @@ -143,8 +143,8 @@ export default class Function extends ExecutableObject { } if(drawPoints) { // Drawing the last cross - ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2) - ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10) + canvas.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2) + canvas.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10) } } else { // Use max precision if function is trigonometrical on log scale. @@ -154,7 +154,7 @@ export default class Function extends ExecutableObject { // Calculate the previousY at the start of the canvas if(definitionDomain.includes(previousX)) previousY = expr.execute(previousX) - for(let px = pxprecision; px < canvas.canvasSize.width; px += pxprecision) { + for(let px = pxprecision; px < canvas.width; px += pxprecision) { let currentX = canvas.px2x(px) if(!definitionDomain.includes(previousX) && definitionDomain.includes(currentX)) { // Should draw up to currentX, but NOT at previousX. @@ -176,12 +176,12 @@ export default class Function extends ExecutableObject { } while(!definitionDomain.includes(currentX) && currentX !== previousX) } // This max variation is needed for functions with asymptotical vertical lines (e.g. 1/x, tan x...) - let maxvariation = (canvas.px2y(0)-canvas.px2y(canvas.canvasSize.height)) + let maxvariation = (canvas.px2y(0)-canvas.px2y(canvas.height)) if(definitionDomain.includes(previousX) && definitionDomain.includes(currentX)) { let currentY = expr.execute(currentX) if(destinationDomain.includes(currentY)) { if(previousY != null && destinationDomain.includes(previousY) && Math.abs(previousY-currentY) < maxvariation) { - canvas.drawLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY)) + canvas.drawLine(canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY)) } } previousY = currentY diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/gainbode.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/gainbode.mjs index 696084d..fb0e9cd 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/gainbode.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/gainbode.mjs @@ -40,7 +40,7 @@ export default class GainBode extends ExecutableObject { [QT_TRANSLATE_NOOP('prop','labelX')]: 'number', [QT_TRANSLATE_NOOP('prop','omGraduation')]: 'boolean' }} - + constructor(name = null, visible = true, color = null, labelContent = 'name + value', om_0 = '', pass = 'high', gain = '20', labelPosition = 'above', labelX = 1, omGraduation = false) { if(name == null) name = Common.getNewName('G') @@ -52,12 +52,13 @@ export default class GainBode extends ExecutableObject { if(om_0 == null) { // Create new point om_0 = Objects.createNewRegisteredObject('Point', [Common.getNewName('ω'), true, this.color, 'name']) - HistoryAPI.history.addToHistory(new CreateNewObject(om_0.name, 'Point', om_0.export())) + HistoryAPI.addToHistory(new CreateNewObject(om_0.name, 'Point', om_0.export())) om_0.update() labelPosition = 'below' } om_0.requiredBy.push(this) } + /** @type {Point} */ this.om_0 = om_0 this.pass = pass if(typeof gain == 'number' || typeof gain == 'string') gain = new Expression(gain.toString()) @@ -98,7 +99,7 @@ export default class GainBode extends ExecutableObject { simplify(x = 1) { let xval = x if(typeof x == 'string') xval = executeExpression(x) - if((this.pass === 'high' && xval < this.om_0.x) || (this.pass === 'low' && xval > this.om_0.x)) { + if((this.pass === 'high' && xval < this.om_0.x.execute()) || (this.pass === 'low' && xval > this.om_0.x.execute())) { let dbfn = new Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`) return dbfn.simplify(x) } else { @@ -110,29 +111,29 @@ export default class GainBode extends ExecutableObject { return true } - draw(canvas, ctx) { - let base = [canvas.x2px(this.om_0.x), canvas.y2px(this.om_0.y)] + draw(canvas) { + let base = [canvas.x2px(this.om_0.x.execute()), canvas.y2px(this.om_0.y.execute())] let dbfn = new Expression(`${this.gain.execute()}*(log10(x)-log10(${this.om_0.x}))+${this.om_0.y}`) let inDrawDom = new EmptySet() if(this.pass === 'high') { // High pass, linear line from beginning, then constant to the end. - canvas.drawLine(ctx, base[0], base[1], canvas.canvasSize.width, base[1]) + canvas.drawLine(base[0], base[1], canvas.width, base[1]) inDrawDom = parseDomain(`]-inf;${this.om_0.x}[`) } else { // Low pass, constant from the beginning, linear line to the end. - canvas.drawLine(ctx, base[0], base[1], 0, base[1]) + canvas.drawLine(base[0], base[1], 0, base[1]) inDrawDom = parseDomain(`]${this.om_0.x};+inf[`) } - Function.drawFunction(canvas, ctx, dbfn, inDrawDom, Domain.R) + Function.drawFunction(canvas, dbfn, inDrawDom, Domain.R) // Dashed line representing break in function - var xpos = canvas.x2px(this.om_0.x.execute()) - var dashPxSize = 10 - for(var i = 0; i < canvas.canvasSize.height && this.omGraduation; i += dashPxSize*2) - canvas.drawLine(ctx, xpos, i, xpos, i+dashPxSize) + let xpos = canvas.x2px(this.om_0.x.execute()) + let dashPxSize = 10 + for(let i = 0; i < canvas.height && this.omGraduation; i += dashPxSize*2) + canvas.drawLine(xpos, i, xpos, i+dashPxSize) // Label - this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) + this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) } update() { diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/phasebode.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/phasebode.mjs index 14ff351..5b32ec5 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/phasebode.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/phasebode.mjs @@ -57,6 +57,7 @@ export default class PhaseBode extends ExecutableObject { } om_0.requiredBy.push(this) } + /** @type {Point} */ this.om_0 = om_0 this.unit = unit this.labelPosition = labelPosition @@ -100,21 +101,21 @@ export default class PhaseBode extends ExecutableObject { return true } - draw(canvas, ctx) { + draw(canvas) { let baseX = canvas.x2px(this.om_0.x.execute()) let omy = this.om_0.y.execute() let augmt = this.phase.execute() let baseY = canvas.y2px(omy) let augmtY = canvas.y2px(omy+augmt) // Before change line. - canvas.drawLine(ctx, 0, baseY, Math.min(baseX, canvas.canvasSize.height), baseY) + canvas.drawLine(0, baseY, Math.min(baseX, canvas.height), baseY) // Transition line. - canvas.drawLine(ctx, baseX, baseY, baseX, augmtY) + canvas.drawLine(baseX, baseY, baseX, augmtY) // After change line - canvas.drawLine(ctx, Math.max(0, baseX), augmtY, canvas.canvasSize.width, augmtY) + canvas.drawLine(Math.max(0, baseX), augmtY, canvas.width, augmtY) // Label - this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) + this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) } update() { diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/point.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/point.mjs index d03053d..03de816 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/point.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/point.mjs @@ -58,24 +58,22 @@ export default class Point extends DrawableObject { return [this.name, this.visible, this.color.toString(), this.labelContent, this.x.toEditableString(), this.y.toEditableString(), this.labelPosition, this.pointStyle] } - draw(canvas, ctx) { - var [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())] - var pointSize = 8+(ctx.lineWidth*2) + draw(canvas) { + let [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())] + let pointSize = 8+(canvas.linewidth*2) switch(this.pointStyle) { case '●': - ctx.beginPath(); - ctx.ellipse(canvasX-pointSize/2, canvasY-pointSize/2, pointSize, pointSize) - ctx.fill(); + canvas.disc(canvasX, canvasY, pointSize/2) break; case '✕': - canvas.drawLine(ctx, canvasX-pointSize/2, canvasY-pointSize/2, canvasX+pointSize/2, canvasY+pointSize/2) - canvas.drawLine(ctx, canvasX-pointSize/2, canvasY+pointSize/2, canvasX+pointSize/2, canvasY-pointSize/2) + canvas.drawLine(canvasX-pointSize/2, canvasY-pointSize/2, canvasX+pointSize/2, canvasY+pointSize/2) + canvas.drawLine(canvasX-pointSize/2, canvasY+pointSize/2, canvasX+pointSize/2, canvasY-pointSize/2) break; case '+': - ctx.fillRect(canvasX-pointSize/2, canvasY-1, pointSize, 2) - ctx.fillRect(canvasX-1, canvasY-pointSize/2, 2, pointSize) + canvas.fillRect(canvasX-pointSize/2, canvasY-1, pointSize, 2) + canvas.fillRect(canvasX-1, canvasY-pointSize/2, 2, pointSize) break; } - this.drawLabel(canvas, ctx, this.labelPosition, canvasX, canvasY) + this.drawLabel(canvas, this.labelPosition, canvasX, canvasY) } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.mjs index 32105d1..0bee9a0 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.mjs @@ -92,61 +92,51 @@ export default class RepartitionFunction extends ExecutableObject { } } - draw(canvas, ctx) { + draw(canvas) { let currentY = 0; let keys = Object.keys(this.probabilities).map(idx => parseInt(idx)).sort((a,b) => a-b) if(canvas.isVisible(keys[0],this.probabilities[keys[0]].replace(/,/g, '.'))) { - canvas.drawLine(ctx, - 0, - canvas.y2px(0), - canvas.x2px(keys[0]), - canvas.y2px(0) - ) + canvas.drawLine(0, canvas.y2px(0), canvas.x2px(keys[0]), canvas.y2px(0)) if(canvas.isVisible(keys[0],0)) { - ctx.beginPath(); - ctx.arc(canvas.x2px(keys[0])+4,canvas.y2px(0), 4, Math.PI / 2, 3 * Math.PI / 2); - ctx.stroke(); + canvas.arc(canvas.x2px(keys[0])+4,canvas.y2px(0), 4, Math.PI / 2, 3 * Math.PI / 2); } } for(let i = 0; i < keys.length-1; i++) { let idx = keys[i]; currentY += parseFloat(this.probabilities[idx].replace(/,/g, '.')); if(canvas.isVisible(idx,currentY) || canvas.isVisible(keys[i+1],currentY)) { - canvas.drawLine(ctx, + canvas.drawLine( Math.max(0,canvas.x2px(idx)), canvas.y2px(currentY), - Math.min(canvas.canvasSize.width,canvas.x2px(keys[i+1])), + Math.min(canvas.width,canvas.x2px(keys[i+1])), canvas.y2px(currentY) ) if(canvas.isVisible(idx,currentY)) { - ctx.beginPath(); - ctx.arc(canvas.x2px(idx),canvas.y2px(currentY), 4, 0, 2 * Math.PI); - ctx.fill(); + canvas.disc(canvas.x2px(idx), canvas.y2px(currentY), 4) } if(canvas.isVisible(keys[i+1],currentY)) { - ctx.beginPath(); - ctx.arc(canvas.x2px(keys[i+1])+4,canvas.y2px(currentY), 4, Math.PI / 2, 3 * Math.PI / 2); - ctx.stroke(); + canvas.arc(canvas.x2px(keys[i+1])+4,canvas.y2px(currentY), 4, Math.PI / 2, 3 * Math.PI / 2); } } } if(canvas.isVisible(keys[keys.length-1],currentY+parseFloat(this.probabilities[keys[keys.length-1]]))) { - canvas.drawLine(ctx, + canvas.drawLine( Math.max(0,canvas.x2px(keys[keys.length-1])), canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))), - canvas.canvasSize.width, + canvas.width, canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))) ) - ctx.beginPath(); - ctx.arc( + canvas.disc( canvas.x2px(keys[keys.length-1]), - canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))), - 4, 0, 2 * Math.PI); - ctx.fill(); + canvas.y2px( + currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.')) + ), + 4 + ) } // Label - this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) + this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sequence.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sequence.mjs index 4e06b56..a7af26f 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sequence.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sequence.mjs @@ -123,11 +123,11 @@ export default class Sequence extends ExecutableObject { } } - draw(canvas, ctx) { - Function.drawFunction(canvas, ctx, this.sequence, canvas.logscalex ? Domain.NE : Domain.N, Domain.R, this.drawPoints, this.drawDashedLines) + draw(canvas) { + Function.drawFunction(canvas, this.sequence, canvas.logscalex ? Domain.NE : Domain.N, Domain.R, this.drawPoints, this.drawDashedLines) // Label - this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) + this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommegainsbode.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommegainsbode.mjs index e03ca81..947c99f 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommegainsbode.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommegainsbode.mjs @@ -89,7 +89,7 @@ export default class SommeGainsBode extends ExecutableObject { let baseY = 0 let om0xGains = {1000000000: 0} // To draw the last part let om0xPass = {1000000000: 'high'} // To draw the last part - Objects.currentObjects['Gain Bode'].forEach(function(gainObj) { // Sorting by their om_0 position. + for(/** @type {GainBode} */ let gainObj of Objects.currentObjects['Gain Bode']) { // Sorting by their om_0 position. let om0x = gainObj.om_0.x.execute() if(om0xGains[om0x] === undefined) { om0xGains[om0x] = gainObj.gain.execute() @@ -99,7 +99,7 @@ export default class SommeGainsBode extends ExecutableObject { om0xPass[om0x+0.001] = gainObj.pass === 'high' } baseY += gainObj.execute(drawMin) - }) + } // Sorting the om_0x let om0xList = Object.keys(om0xGains).map(x => parseFloat(x)) // THEY WERE CONVERTED TO STRINGS... om0xList.sort((a,b) => a - b) @@ -130,13 +130,13 @@ export default class SommeGainsBode extends ExecutableObject { } } - draw(canvas, ctx) { + draw(canvas) { if(this.cachedParts.length > 0) { for(let [dbfn, inDrawDom] of this.cachedParts) { - Function.drawFunction(canvas, ctx, dbfn, inDrawDom, Domain.R) + Function.drawFunction(canvas, dbfn, inDrawDom, Domain.R) if(inDrawDom.includes(this.labelX)) { // Label - this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) + this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) } } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommephasesbode.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommephasesbode.mjs index 328c069..30ad4b0 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommephasesbode.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommephasesbode.mjs @@ -89,7 +89,7 @@ export default class SommePhasesBode extends ExecutableObject { if(Objects.currentObjects['Phase Bode'] !== undefined) { console.log('Recalculating cache phase') - for(let obj of Objects.currentObjects['Phase Bode']) { + for(/** @type {PhaseBode} */ let obj of Objects.currentObjects['Phase Bode']) { this.om0xList.push(obj.om_0.x.execute()) if(phasesDict[obj.om_0.x.execute()] === undefined) { phasesDict[obj.om_0.x.execute()] = obj.phase.execute() @@ -110,18 +110,18 @@ export default class SommePhasesBode extends ExecutableObject { } } - draw(canvas, ctx) { + draw(canvas) { for(let i = 0; i < this.om0xList.length-1; i++) { let om0xBegin = canvas.x2px(this.om0xList[i]) let om0xEnd = canvas.x2px(this.om0xList[i+1]) let baseY = canvas.y2px(this.phasesList[i]) let nextY = canvas.y2px(this.phasesList[i+1]) - canvas.drawLine(ctx, om0xBegin, baseY, om0xEnd, baseY) - canvas.drawLine(ctx, om0xEnd, baseY, om0xEnd, nextY) + canvas.drawLine(om0xBegin, baseY, om0xEnd, baseY) + canvas.drawLine(om0xEnd, baseY, om0xEnd, nextY) } // Label - this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) + this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.mjs index 040e7bd..e3e2ba2 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.mjs @@ -90,9 +90,9 @@ export default class Text extends DrawableObject { return `\\textsf{${this.latexMarkupText()}}` } - draw(canvas, ctx) { + draw(canvas) { let yOffset = this.disableLatex ? canvas.textsize-4 : 0 - this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())+yOffset, this.disableLatex) + this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())+yOffset, this.disableLatex) } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/xcursor.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/xcursor.mjs index 036c408..236bd4a 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/xcursor.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/xcursor.mjs @@ -91,8 +91,8 @@ export default class XCursor extends DrawableObject { } getTargetValueLatexLabel() { - var t = this.targetElement - var approx = '' + let t = this.targetElement + let approx = '' if(this.approximate) { approx = t.execute(this.x.execute()) approx = approx.toPrecision(this.rounding + Math.round(approx).toString().length) @@ -142,39 +142,35 @@ export default class XCursor extends DrawableObject { } } - draw(canvas, ctx) { + draw(canvas) { let xpos = canvas.x2px(this.x.execute()) switch(this.displayStyle) { case '— — — — — — —': - var dashPxSize = 10 - for(var i = 0; i < canvas.canvasSize.height; i += dashPxSize*2) - canvas.drawLine(ctx, xpos, i, xpos, i+dashPxSize) + canvas.drawDashedLine(xpos, 0, xpos, canvas.height, 20) break; case '⸺⸺⸺⸺⸺⸺': - canvas.drawXLine(ctx, this.x.execute()) + canvas.drawXLine(this.x.execute()) break; case '• • • • • • • • • •': - var pointDistancePx = 10 - var pointSize = 2 - ctx.beginPath(); - for(var i = 0; i < canvas.canvasSize.height; i += pointDistancePx) - ctx.ellipse(xpos-pointSize/2, i-pointSize/2, pointSize, pointSize) - ctx.fill(); + let pointDistancePx = 10 + let pointSize = 2 + for(let i = 0; i < canvas.height; i += pointDistancePx) + canvas.disc(xpos, i, pointSize) break; } // Drawing label at the top of the canvas. - this.drawLabel(canvas, ctx, this.labelPosition, xpos, 0, false, null, null, - (x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, 5, ltxImg.width, ltxImg.height), - (x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, textSize.height+5)) + this.drawLabel(canvas, this.labelPosition, xpos, 0, false, null, null, + (x,y,ltxImg) => canvas.drawVisibleImage(ltxImg.source, x, 5, ltxImg.width, ltxImg.height), + (x,y,text,textSize) => canvas.drawVisibleText(text, x, textSize.height+5)) // Drawing label at the position of the target element. if(this.targetValuePosition === 'Next to target' && this.targetElement != null) { let ypos = canvas.y2px(this.targetElement.execute(this.x.execute())) - this.drawLabel(canvas, ctx, this.labelPosition, xpos, ypos, false, + this.drawLabel(canvas, this.labelPosition, xpos, ypos, false, this.getTargetValueLatexLabel.bind(this), this.getTargetValueLabel.bind(this), - (x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, y, ltxImg.width, ltxImg.height), - (x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, y+textSize.height)) + (x,y,ltxImg) => canvas.drawVisibleImage(ltxImg.source, x, y, ltxImg.width, ltxImg.height), + (x,y,text,textSize) => canvas.drawVisibleText(text, x, y+textSize.height)) } } }