diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/History.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/History.qml
index e1e527d..85abbc7 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/History.qml
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/History.qml
@@ -212,8 +212,11 @@ Item {
}
Component.onCompleted: {
- Modules.History.history = historyObj
- Modules.History.themeTextColor = sysPalette.windowText
- Modules.History.imageDepth = Screen.devicePixelRatio
+ Modules.History.initialize({
+ historyObj,
+ themeTextColor: sysPalette.windowText.toString(),
+ imageDepth: Screen.devicePixelRatio,
+ fontSize: 14
+ })
}
}
diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml
index 4159497..e54781f 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml
@@ -120,12 +120,6 @@ Canvas {
*/
property bool showygrad: false
- /*!
- \qmlproperty int LogGraphCanvas::maxgradx
- Max power of the logarithmic scaled on the x axis in logarithmic mode.
- */
- property int maxgradx: 90
-
/*!
\qmlproperty var LogGraphCanvas::imageLoaders
Dictionary of format {image: [callback.image data]} containing data for defered image loading.
@@ -139,14 +133,14 @@ Canvas {
Component.onCompleted: {
imageLoaders = {}
- Modules.Canvas.initialize(canvas, drawingErrorDialog)
+ Modules.Canvas.initialize({ canvas, drawingErrorDialog })
}
Native.MessageDialog {
id: drawingErrorDialog
title: qsTranslate("expression", "LogarithmPlotter - Drawing error")
text: ""
- function showDialog(objType, objName, error) {
+ function show(objType, objName, error) {
text = qsTranslate("error", "Error while attempting to draw %1 %2:\n%3\n\nUndoing last change.").arg(objType).arg(objName).arg(error)
open()
}
diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml
index c94bef1..3c6e994 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml
@@ -128,7 +128,7 @@ ScrollView {
property string saveFilename: ""
Component.onCompleted: {
- Modules.IO.initialize(root, settings, alert)
+ Modules.IO.initialize({ root, settings, alert })
}
Column {
diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/canvas.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/canvas.mjs
index 020ff88..04abf68 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/canvas.mjs
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/canvas.mjs
@@ -17,30 +17,28 @@
*/
import { Module } from "./common.mjs"
+import { FUNCTION, Interface, CanvasInterface, DialogInterface } from "./interface.mjs"
import { textsup } from "../utils.mjs"
import { Expression } from "../math/index.mjs"
import Latex from "./latex.mjs"
import Objects from "./objects.mjs"
import History from "./history.mjs"
-
class CanvasAPI extends Module {
-
constructor() {
- super('Canvas', [
- Modules.Objects,
- Modules.History
- ])
+ super("Canvas", {
+ canvas: CanvasInterface,
+ drawingErrorDialog: DialogInterface
+ })
- /** @type {HTMLCanvasElement} */
+ /** @type {CanvasInterface} */
this._canvas = null
/** @type {CanvasRenderingContext2D} */
this._ctx = null
/**
- * @type {Object}
- * @property {function(string, string, string)} showDialog
+ * @type {{show(string, string, string)}}
* @private
*/
this._drawingErrorDialog = null
@@ -62,86 +60,132 @@ class CanvasAPI extends Module {
}
}
- initialize(canvasObject, drawingErrorDialog) {
- this._canvas = canvasObject
+ /**
+ * Initialize the module.
+ * @param {CanvasInterface} canvas
+ * @param {{show(string, string, string)}} drawingErrorDialog
+ */
+ initialize({ canvas, drawingErrorDialog }) {
+ super.initialize({ canvas, drawingErrorDialog })
+ this._canvas = canvas
this._drawingErrorDialog = drawingErrorDialog
}
- get width() { return this._canvas.width }
+ get width() {
+ if(!this.initialized) throw new Error("Attempting width before initialize!")
+ return this._canvas.width
+ }
- get height() { return this._canvas.height }
+ get height() {
+ if(!this.initialized) throw new Error("Attempting height before initialize!")
+ return this._canvas.height
+ }
/**
* Minimum x of the diagram, provided from settings.
* @returns {number}
*/
- get xmin() { return this._canvas.xmin }
+ get xmin() {
+ if(!this.initialized) throw new Error("Attempting xmin before initialize!")
+ return this._canvas.xmin
+ }
/**
* Zoom on the x-axis of the diagram, provided from settings.
* @returns {number}
*/
- get xzoom() { return this._canvas.xzoom }
+ get xzoom() {
+ if(!this.initialized) throw new Error("Attempting xzoom before initialize!")
+ return this._canvas.xzoom
+ }
/**
* Maximum y of the diagram, provided from settings.
* @returns {number}
*/
- get ymax() { return this._canvas.ymax }
+ get ymax() {
+ if(!this.initialized) throw new Error("Attempting ymax before initialize!")
+ return this._canvas.ymax
+ }
/**
* Zoom on the y-axis of the diagram, provided from settings.
* @returns {number}
*/
- get yzoom() { return this._canvas.yzoom }
+ get yzoom() {
+ if(!this.initialized) throw new Error("Attempting yzoom before initialize!")
+ return this._canvas.yzoom
+ }
/**
* Label used on the x-axis, provided from settings.
* @returns {string}
*/
- get xlabel() { return this._canvas.xlabel }
+ get xlabel() {
+ if(!this.initialized) throw new Error("Attempting xlabel before initialize!")
+ return this._canvas.xlabel
+ }
/**
* Label used on the y-axis, provided from settings.
* @returns {string}
*/
- get ylabel() { return this._canvas.ylabel }
+ get ylabel() {
+ if(!this.initialized) throw new Error("Attempting ylabel before initialize!")
+ 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 }
+ get linewidth() {
+ if(!this.initialized) throw new Error("Attempting linewidth before initialize!")
+ 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 }
+ get textsize() {
+ if(!this.initialized) throw new Error("Attempting textsize before initialize!")
+ return this._canvas.textsize
+ }
/**
* True if the canvas should be in logarithmic mode, false otherwise.
* @returns {boolean}
*/
- get logscalex() { return this._canvas.logscalex }
+ get logscalex() {
+ if(!this.initialized) throw new Error("Attempting logscalex before initialize!")
+ return this._canvas.logscalex
+ }
/**
* True if the x graduation should be shown, false otherwise.
* @returns {boolean}
*/
- get showxgrad() { return this._canvas.showxgrad }
+ get showxgrad() {
+ if(!this.initialized) throw new Error("Attempting showxgrad before initialize!")
+ return this._canvas.showxgrad
+ }
/**
* True if the y graduation should be shown, false otherwise.
* @returns {boolean}
*/
- get showygrad() { return this._canvas.showygrad }
+ get showygrad() {
+ if(!this.initialized) throw new Error("Attempting showygrad before initialize!")
+ return this._canvas.showygrad
+ }
/**
* Max power of the logarithmic scaled on the x axis in logarithmic mode.
* @returns {number}
*/
get maxgradx() {
+ if(!this.initialized) throw new Error("Attempting maxgradx before initialize!")
return Math.min(
309, // 10e309 = Infinity (beyond this land be dragons)
Math.max(
@@ -156,6 +200,7 @@ class CanvasAPI extends Module {
//
requestPaint() {
+ if(!this.initialized) throw new Error("Attempting requestPaint before initialize!")
this._canvas.requestPaint()
}
@@ -163,6 +208,7 @@ class CanvasAPI extends Module {
* Redraws the entire canvas
*/
redraw() {
+ if(!this.initialized) throw new Error("Attempting redraw before initialize!")
this._ctx = this._canvas.getContext("2d")
this._computeAxes()
this._reset()
@@ -171,7 +217,7 @@ class CanvasAPI extends Module {
this._drawLabels()
this._ctx.lineWidth = this.linewidth
for(let objType in Objects.currentObjects) {
- for(let obj of Objects.currentObjects[objType]){
+ for(let obj of Objects.currentObjects[objType]) {
this._ctx.strokeStyle = obj.color
this._ctx.fillStyle = obj.color
if(obj.visible)
@@ -202,12 +248,12 @@ class CanvasAPI extends Module {
x: {
expression: exprX,
value: x1,
- maxDraw: Math.ceil(Math.max(Math.abs(this.xmin), Math.abs(this.px2x(this.width)))/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)
+ maxDraw: Math.ceil(Math.max(Math.abs(this.ymax), Math.abs(this.px2y(this.height))) / y1)
}
}
}
@@ -216,14 +262,14 @@ class CanvasAPI extends Module {
* Resets the canvas to a blank one with default setting.
* @private
*/
- _reset(){
+ _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)
+ this._ctx.fillRect(0, 0, this.width, this.height)
}
-
+
/**
* Draws the grid.
* @private
@@ -233,18 +279,18 @@ class CanvasAPI extends Module {
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)
+ 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 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)
+ 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)
}
}
@@ -260,10 +306,10 @@ class CanvasAPI extends Module {
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)
+ 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)
}
/**
@@ -276,38 +322,38 @@ class CanvasAPI extends Module {
// Labels
this._ctx.fillStyle = "#000000"
this._ctx.font = `${this.textsize}px sans-serif`
- this._ctx.fillText(this.ylabel, axisypx+10, 24)
+ 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)
+ this._ctx.fillText(this.xlabel, this.width - 14 - textWidth, axisxpx - 5)
// Axis graduation labels
- this._ctx.font = `${this.textsize-4}px sans-serif`
+ this._ctx.font = `${this.textsize - 4}px sans-serif`
- let txtMinus = this._ctx.measureText('-').width
+ 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
+ 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)))
+ 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).toString().replace(/^\((.+)\)$/, '$1')
+ let drawX = x * this.axesSteps.x.value
+ let txtX = this.axesSteps.x.expression.simplify(x).toString().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)
+ 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).toString().replace(/^\((.+)\)$/, '$1')
+ let drawY = y * this.axesSteps.y.value
+ let txtY = this.axesSteps.y.expression.simplify(y).toString().replace(/^\((.+)\)$/, "$1")
textWidth = this._ctx.measureText(txtY).width
- this.drawVisibleText(txtY, axisypx-6-textWidth, this.y2px(drawY)+4+(10*(y===0)))
+ 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.drawVisibleText("-" + txtY, axisypx - 6 - textWidth - txtMinus, this.y2px(-drawY) + 4)
}
}
this._ctx.fillStyle = "#FFFFFF"
@@ -348,7 +394,7 @@ class CanvasAPI extends Module {
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))
+ this._ctx.fillText(txt, x, y + (this.textsize * i))
})
}
}
@@ -363,7 +409,7 @@ class CanvasAPI extends Module {
* @param {number} height
*/
drawVisibleImage(image, x, y, width, height) {
- this._canvas.markDirty(Qt.rect(x, y, width, height));
+ this._canvas.markDirty(Qt.rect(x, y, width, height))
this._ctx.drawImage(image, x, y, width, height)
}
@@ -380,7 +426,7 @@ class CanvasAPI extends Module {
theight += defaultHeight
if(this._ctx.measureText(txt).width > twidth) twidth = this._ctx.measureText(txt).width
}
- return {'width': twidth, 'height': theight}
+ return { "width": twidth, "height": theight }
}
/**
@@ -392,9 +438,9 @@ class CanvasAPI extends Module {
x2px(x) {
if(this.logscalex) {
const logxmin = Math.log(this.xmin)
- return (Math.log(x)-logxmin)*this.xzoom
+ return (Math.log(x) - logxmin) * this.xzoom
} else
- return (x - this.xmin)*this.xzoom
+ return (x - this.xmin) * this.xzoom
}
/**
@@ -415,9 +461,9 @@ class CanvasAPI extends Module {
*/
px2x(px) {
if(this.logscalex) {
- return Math.exp(px/this.xzoom+Math.log(this.xmin))
+ return Math.exp(px / this.xzoom + Math.log(this.xmin))
} else
- return (px/this.xzoom+this.xmin)
+ return (px / this.xzoom + this.xmin)
}
/**
@@ -427,7 +473,7 @@ class CanvasAPI extends Module {
* @returns {number}
*/
px2y(px) {
- return -(px/this.yzoom-this.ymax)
+ return -(px / this.yzoom - this.ymax)
}
/**
@@ -448,10 +494,10 @@ class CanvasAPI extends Module {
* @param {number} y2
*/
drawLine(x1, y1, x2, y2) {
- this._ctx.beginPath();
- this._ctx.moveTo(x1, y1);
- this._ctx.lineTo(x2, y2);
- this._ctx.stroke();
+ this._ctx.beginPath()
+ this._ctx.moveTo(x1, y1)
+ this._ctx.lineTo(x2, y2)
+ this._ctx.stroke()
}
/**
@@ -463,9 +509,9 @@ class CanvasAPI extends Module {
* @param {number} dashPxSize
*/
drawDashedLine(x1, y1, x2, y2, dashPxSize = 6) {
- this._ctx.setLineDash([dashPxSize/2, dashPxSize]);
+ this._ctx.setLineDash([dashPxSize / 2, dashPxSize])
this.drawLine(x1, y1, x2, y2)
- this._ctx.setLineDash([]);
+ this._ctx.setLineDash([])
}
/**
@@ -476,7 +522,7 @@ class CanvasAPI extends Module {
*/
renderLatexImage(ltxText, color, callback) {
const onRendered = (imgData) => {
- if(!this._canvas.isImageLoaded(imgData.source) && !this._canvas.isImageLoading(imgData.source)){
+ if(!this._canvas.isImageLoaded(imgData.source) && !this._canvas.isImageLoading(imgData.source)) {
// Wait until the image is loaded to callback.
this._canvas.loadImage(imgData.source)
this._canvas.imageLoaders[imgData.source] = [callback, imgData]
@@ -496,8 +542,13 @@ class CanvasAPI extends Module {
// Context methods
//
- get font() { return this._ctx.font }
- set font(value) { return this._ctx.font = value }
+ get font() {
+ return this._ctx.font
+ }
+
+ set font(value) {
+ return this._ctx.font = value
+ }
/**
* Draws an act on the canvas centered on a point.
@@ -508,7 +559,7 @@ class CanvasAPI extends Module {
* @param {number} endAngle
* @param {boolean} counterclockwise
*/
- arc(x, y, radius, startAngle, endAngle, counterclockwise=false) {
+ arc(x, y, radius, startAngle, endAngle, counterclockwise = false) {
this._ctx.beginPath()
this._ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise)
this._ctx.stroke()
@@ -521,9 +572,9 @@ class CanvasAPI extends Module {
* @param {number} radius
*/
disc(x, y, radius) {
- this._ctx.beginPath();
+ this._ctx.beginPath()
this._ctx.arc(x, y, radius, 0, 2 * Math.PI)
- this._ctx.fill();
+ this._ctx.fill()
}
/**
diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/common.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/common.mjs
index 1b68b89..6902745 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/common.mjs
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/common.mjs
@@ -16,6 +16,8 @@
* along with this program. If not, see .
*/
+import { Interface } from "./interface.mjs"
+
/**
* Base class for global APIs in runtime.
*/
@@ -24,22 +26,30 @@ export class Module {
/**
*
* @param {string} name - Name of the API
- * @param {(Module|undefined)[]} requires - List of APIs required to initialize this one.
+ * @param {Object.} initializationParameters - List of parameters for the initialize function.
*/
- constructor(name, requires = []) {
+ constructor(name, initializationParameters = {}) {
console.log(`Loading module ${name}...`)
- this.__check_requirements(requires, name)
+ this.__name = name
+ this.__initializationParameters = initializationParameters
+ this.initialized = false
}
/**
* Checks if all requirements are defined.
- * @param {(Module|undefined)[]} requires
- * @param {string} name
+ * @param {Object.} options
*/
- __check_requirements(requires, name) {
- for(let requirement of requires) {
- if(requirement === undefined)
- throw new Error(`Requirement ${requires.indexOf(requirement)} of ${name} has not been initialized.`)
+ initialize(options) {
+ if(this.initialized)
+ throw new Error(`Cannot reinitialize module ${this.__name}.`)
+ for(const [name, value] of Object.entries(this.__initializationParameters)) {
+ if(!options.hasOwnProperty(name))
+ throw new Error(`Option '${name}' of initialize of module ${this.__name} does not exist.`)
+ if(typeof value === "function" && value.prototype instanceof Interface)
+ Interface.check_implementation(value, options[name])
+ else if(typeof value !== typeof options[name])
+ throw new Error(`Option '${name}' of initialize of module ${this.__name} is not a '${value}' (${typeof options[name]}).`)
}
+ this.initialized = true
}
}
\ No newline at end of file
diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/expreval.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/expreval.mjs
index 5ac849e..cc61434 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/expreval.mjs
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/expreval.mjs
@@ -36,10 +36,7 @@ const evalVariables = {
export class ExprParserAPI extends Module {
constructor() {
- super("ExprParser", [
- /** @type {ObjectsAPI} */
- Modules.Objects
- ])
+ super("ExprParser")
this.currentVars = {}
this._parser = new Parser()
diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/history.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/history.mjs
index 27eedcb..09b45d6 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/history.mjs
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/history.mjs
@@ -18,29 +18,65 @@
import { Module } from "./common.mjs"
import Latex from "./latex.mjs"
+import { HistoryInterface, NUMBER, STRING } from "./interface.mjs"
class HistoryAPI extends Module {
constructor() {
- super('History', [
- Modules.Latex
- ])
+ super("History", {
+ historyObj: HistoryInterface,
+ themeTextColor: STRING,
+ imageDepth: NUMBER,
+ fontSize: NUMBER
+ })
// History QML object
- this.history = null;
- this.themeTextColor = "#ff0000";
- this.imageDepth = 2;
- this.fontSize = 14;
+ this.history = null
+ this.themeTextColor = "#FF0000"
+ this.imageDepth = 2
+ this.fontSize = 28
}
- undo() { this.history.undo() }
- redo() { this.history.redo() }
- clear() { this.history.clear() }
- addToHistory(action) { this.history.addToHistory(action) }
- unserialize(...data) { this.history.unserialize(...data) }
- serialize() { return this.history.serialize() }
+ initialize({ historyObj, themeTextColor, imageDepth, fontSize }) {
+ super.initialize({ historyObj, themeTextColor, imageDepth, fontSize })
+ console.log("Initializing history...")
+ this.history = historyObj
+ this.themeTextColor = themeTextColor
+ this.imageDepth = imageDepth
+ this.fontSize = fontSize
+ }
+
+ undo() {
+ if(!this.initialized) throw new Error("Attempting undo before initialize!")
+ this.history.undo()
+ }
+
+ redo() {
+ if(!this.initialized) throw new Error("Attempting redo before initialize!")
+ this.history.redo()
+ }
+
+ clear() {
+ if(!this.initialized) throw new Error("Attempting clear before initialize!")
+ this.history.clear()
+ }
+
+ addToHistory(action) {
+ if(!this.initialized) throw new Error("Attempting addToHistory before initialize!")
+ this.history.addToHistory(action)
+ }
+
+ unserialize(...data) {
+ if(!this.initialized) throw new Error("Attempting unserialize before initialize!")
+ this.history.unserialize(...data)
+ }
+
+ serialize() {
+ if(!this.initialized) throw new Error("Attempting serialize before initialize!")
+ return this.history.serialize()
+ }
}
/** @type {HistoryAPI} */
Modules.History = Modules.History || new HistoryAPI()
-export default Modules.History
\ No newline at end of file
+export default Modules.History
diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/interface.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/interface.mjs
new file mode 100644
index 0000000..9da3f4d
--- /dev/null
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/interface.mjs
@@ -0,0 +1,130 @@
+/**
+ * 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 .
+ */
+
+export const NUMBER = 0
+export const STRING = "string"
+export const BOOLEAN = true
+export const OBJECT = {}
+export const FUNCTION = () => {
+}
+
+
+export class Interface {
+ /**
+ * Checks if the class to check implements the given interface.
+ * Throws an error if the implementation does not conform to the interface.
+ * @param {typeof Interface} interface_
+ * @param {object} classToCheck
+ * @return {boolean}
+ */
+ static check_implementation(interface_, classToCheck) {
+ const properties = new interface_()
+ const interfaceName = interface_.name
+ const toCheckName = classToCheck.constructor.name
+ for(const [property, value] of Object.entries(properties))
+ if(property !== "implement") {
+ console.log(classToCheck[property], value)
+ if(!classToCheck.hasOwnProperty(property))
+ // Check if the property exist
+ throw new Error(`Property '${property}' (${typeof value}) is present in interface ${interfaceName}, but not in implementation ${toCheckName}.`)
+ else if((typeof value) !== (typeof classToCheck[property]))
+ // Compare the types
+ throw new Error(`Property '${property}' of ${interfaceName} implementation ${toCheckName} is a '${typeof classToCheck[property]}' and not a '${typeof value}'.`)
+ else if((typeof value) === "object")
+ // Test type of object.
+ if(value instanceof Interface)
+ Interface.check_implementation(value, classToCheck[property])
+ else if(value.prototype && !(classToCheck[property] instanceof value))
+ throw new Error(`Property '${property}' of ${interfaceName} implementation ${toCheckName} is not '${value.constructor.name}'.`)
+ }
+ }
+
+ /**
+ * Decorator to automatically check if a class conforms to the current interface.
+ * @param {object} class_
+ */
+ implement(class_) {
+ Interface.check_implementation(this, class_)
+ return class_
+ }
+}
+
+
+export class SettingsInterface extends Interface {
+ constructor() {
+ super()
+ this.width = NUMBER
+ this.height = NUMBER
+ this.xmin = NUMBER
+ this.ymax = NUMBER
+ this.xzoom = NUMBER
+ this.yzoom = NUMBER
+ this.xaxisstep = STRING
+ this.yaxisstep = STRING
+ this.xlabel = STRING
+ this.ylabel = STRING
+ this.linewidth = NUMBER
+ this.textsize = NUMBER
+ this.logscalex = BOOLEAN
+ this.showxgrad = BOOLEAN
+ this.showygrad = BOOLEAN
+ }
+}
+
+export class CanvasInterface extends SettingsInterface {
+ constructor() {
+ super()
+ this.imageLoaders = OBJECT
+ /** @type {function(string): CanvasRenderingContext2D} */
+ this.getContext = FUNCTION
+ /** @type {function(rect)} */
+ this.markDirty = FUNCTION
+ /** @type {function(string)} */
+ this.loadImage = FUNCTION
+ /** @type {function()} */
+ this.requestPaint = FUNCTION
+ }
+}
+
+export class RootInterface extends Interface {
+ constructor() {
+ super()
+ this.width = NUMBER
+ this.height = NUMBER
+ this.updateObjectsLists = FUNCTION
+ }
+}
+
+export class DialogInterface extends Interface {
+ constructor() {
+ super()
+ this.show = FUNCTION
+ }
+}
+
+export class HistoryInterface extends Interface {
+ constructor() {
+ super()
+ this.undo = FUNCTION
+ this.redo = FUNCTION
+ this.clear = FUNCTION
+ this.addToHistory = FUNCTION
+ this.unserialize = FUNCTION
+ this.serialize = FUNCTION
+ }
+}
\ No newline at end of file
diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/io.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/io.mjs
index bc9a1db..2a540a7 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/io.mjs
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/io.mjs
@@ -20,14 +20,17 @@ import { Module } from "./common.mjs"
import Objects from "./objects.mjs"
import History from "./history.mjs"
import Canvas from "./canvas.mjs"
+import { DialogInterface, FUNCTION, Interface, RootInterface, SettingsInterface } from "./interface.mjs"
+
class IOAPI extends Module {
constructor() {
- super("IO", [
- Modules.Objects,
- Modules.History
- ])
+ super("IO", {
+ alert: DialogInterface,
+ root: RootInterface,
+ settings: SettingsInterface
+ })
/**
* Path of the currently opened file. Empty if no file is opened.
* @type {string}
@@ -37,12 +40,13 @@ class IOAPI extends Module {
/**
* Initializes module with QML elements.
- * @param {{width: number, height: number, updateObjectsLists: function()}} rootElement
- * @param {Settings} settings
+ * @param {RootInterface} root
+ * @param {SettingsInterface} settings
* @param {{show: function(string)}} alert
*/
- initialize(rootElement, settings, alert) {
- this.rootElement = rootElement
+ initialize({ root, settings, alert }) {
+ super.initialize({ root, settings, alert })
+ this.rootElement = root
this.settings = settings
this.alert = alert
}
@@ -52,6 +56,7 @@ class IOAPI extends Module {
* @param {string} filename
*/
saveDiagram(filename) {
+ if(!this.initialized) throw new Error("Attempting saveDiagram before initialize!")
// Add extension if necessary
if(["lpf"].indexOf(filename.split(".")[filename.split(".").length - 1]) === -1)
filename += ".lpf"
@@ -93,11 +98,13 @@ class IOAPI extends Module {
* @param {string} filename
*/
loadDiagram(filename) {
+ if(!this.initialized) throw new Error("Attempting loadDiagram before initialize!")
+ if(!History.initialized) throw new Error("Attempting loadDiagram before history is initialized!")
let basename = filename.split("/").pop()
this.alert.show(qsTranslate("io", "Loading file '%1'.").arg(basename))
let data = JSON.parse(Helper.load(filename))
let error = ""
- if(Object.keys(data).includes("type") && data["type"] === "logplotv1") {
+ if(data.hasOwnProperty("type") && data["type"] === "logplotv1") {
History.clear()
// Importing settings
this.settings.saveFilename = filename
@@ -129,7 +136,7 @@ class IOAPI extends Module {
// 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(Objects.types).indexOf(objType) > -1) {
+ if(Object.keys(Objects.types).includes(objType)) {
Objects.currentObjects[objType] = []
for(let objData of data["objects"][objType]) {
/** @type {DrawableObject} */
@@ -157,7 +164,7 @@ class IOAPI extends Module {
}
if(error !== "") {
console.log(error)
- this.alert.show(qsTranslate("io", "Could not save file: ") + error)
+ this.alert.show(qsTranslate("io", "Could not load file: ") + error)
// TODO: Error handling
return
}
diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/latex.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/latex.mjs
index 32f048b..f2e9b0b 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/latex.mjs
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/latex.mjs
@@ -58,10 +58,7 @@ class LatexRenderResult {
class LatexAPI extends Module {
constructor() {
- super("Latex", [
- /** @type {ExprParserAPI} */
- Modules.ExprParser
- ])
+ super("Latex")
/**
* true if latex has been enabled by the user, false otherwise.
*/
diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/preferences.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/preferences.mjs
index e76596e..389a5ba 100644
--- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/preferences.mjs
+++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/module/preferences.mjs
@@ -22,10 +22,7 @@ import DefaultGraph from "../preferences/default.mjs"
class PreferencesAPI extends Module {
constructor() {
- super('Preferences', [
- Modules.Canvas,
- Modules.Latex
- ])
+ super('Preferences')
this.categories = {
[QT_TRANSLATE_NOOP('settingCategory', 'general')]: General,