Compare commits
No commits in common. "6a1f01ba1fdb410ec491f68db02a8efd9e548ef9" and "8c8964e75e6de52e99358933b11f40ba716f2812" have entirely different histories.
6a1f01ba1f
...
8c8964e75e
11 changed files with 136 additions and 358 deletions
|
@ -212,11 +212,8 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Modules.History.initialize({
|
Modules.History.history = historyObj
|
||||||
historyObj,
|
Modules.History.themeTextColor = sysPalette.windowText
|
||||||
themeTextColor: sysPalette.windowText.toString(),
|
Modules.History.imageDepth = Screen.devicePixelRatio
|
||||||
imageDepth: Screen.devicePixelRatio,
|
|
||||||
fontSize: 14
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,12 @@ Canvas {
|
||||||
*/
|
*/
|
||||||
property bool showygrad: false
|
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
|
\qmlproperty var LogGraphCanvas::imageLoaders
|
||||||
Dictionary of format {image: [callback.image data]} containing data for defered image loading.
|
Dictionary of format {image: [callback.image data]} containing data for defered image loading.
|
||||||
|
@ -133,14 +139,14 @@ Canvas {
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
imageLoaders = {}
|
imageLoaders = {}
|
||||||
Modules.Canvas.initialize({ canvas, drawingErrorDialog })
|
Modules.Canvas.initialize(canvas, drawingErrorDialog)
|
||||||
}
|
}
|
||||||
|
|
||||||
Native.MessageDialog {
|
Native.MessageDialog {
|
||||||
id: drawingErrorDialog
|
id: drawingErrorDialog
|
||||||
title: qsTranslate("expression", "LogarithmPlotter - Drawing error")
|
title: qsTranslate("expression", "LogarithmPlotter - Drawing error")
|
||||||
text: ""
|
text: ""
|
||||||
function show(objType, objName, error) {
|
function showDialog(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)
|
text = qsTranslate("error", "Error while attempting to draw %1 %2:\n%3\n\nUndoing last change.").arg(objType).arg(objName).arg(error)
|
||||||
open()
|
open()
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ ScrollView {
|
||||||
property string saveFilename: ""
|
property string saveFilename: ""
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Modules.IO.initialize({ root, settings, alert })
|
Modules.IO.initialize(root, settings, alert)
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
|
|
@ -17,28 +17,30 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Module } from "./common.mjs"
|
import { Module } from "./common.mjs"
|
||||||
import { FUNCTION, Interface, CanvasInterface, DialogInterface } from "./interface.mjs"
|
|
||||||
import { textsup } from "../utils.mjs"
|
import { textsup } from "../utils.mjs"
|
||||||
import { Expression } from "../math/index.mjs"
|
import { Expression } from "../math/index.mjs"
|
||||||
import Latex from "./latex.mjs"
|
import Latex from "./latex.mjs"
|
||||||
import Objects from "./objects.mjs"
|
import Objects from "./objects.mjs"
|
||||||
import History from "./history.mjs"
|
import History from "./history.mjs"
|
||||||
|
|
||||||
class CanvasAPI extends Module {
|
|
||||||
constructor() {
|
|
||||||
super("Canvas", {
|
|
||||||
canvas: CanvasInterface,
|
|
||||||
drawingErrorDialog: DialogInterface
|
|
||||||
})
|
|
||||||
|
|
||||||
/** @type {CanvasInterface} */
|
class CanvasAPI extends Module {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('Canvas', [
|
||||||
|
Modules.Objects,
|
||||||
|
Modules.History
|
||||||
|
])
|
||||||
|
|
||||||
|
/** @type {HTMLCanvasElement} */
|
||||||
this._canvas = null
|
this._canvas = null
|
||||||
|
|
||||||
/** @type {CanvasRenderingContext2D} */
|
/** @type {CanvasRenderingContext2D} */
|
||||||
this._ctx = null
|
this._ctx = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {{show(string, string, string)}}
|
* @type {Object}
|
||||||
|
* @property {function(string, string, string)} showDialog
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this._drawingErrorDialog = null
|
this._drawingErrorDialog = null
|
||||||
|
@ -60,132 +62,86 @@ class CanvasAPI extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
initialize(canvasObject, drawingErrorDialog) {
|
||||||
* Initialize the module.
|
this._canvas = canvasObject
|
||||||
* @param {CanvasInterface} canvas
|
|
||||||
* @param {{show(string, string, string)}} drawingErrorDialog
|
|
||||||
*/
|
|
||||||
initialize({ canvas, drawingErrorDialog }) {
|
|
||||||
super.initialize({ canvas, drawingErrorDialog })
|
|
||||||
this._canvas = canvas
|
|
||||||
this._drawingErrorDialog = drawingErrorDialog
|
this._drawingErrorDialog = drawingErrorDialog
|
||||||
}
|
}
|
||||||
|
|
||||||
get width() {
|
get width() { return this._canvas.width }
|
||||||
if(!this.initialized) throw new Error("Attempting width before initialize!")
|
|
||||||
return this._canvas.width
|
|
||||||
}
|
|
||||||
|
|
||||||
get height() {
|
get height() { return this._canvas.height }
|
||||||
if(!this.initialized) throw new Error("Attempting height before initialize!")
|
|
||||||
return this._canvas.height
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimum x of the diagram, provided from settings.
|
* Minimum x of the diagram, provided from settings.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
get xmin() {
|
get xmin() { return this._canvas.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.
|
* Zoom on the x-axis of the diagram, provided from settings.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
get xzoom() {
|
get xzoom() { return this._canvas.xzoom }
|
||||||
if(!this.initialized) throw new Error("Attempting xzoom before initialize!")
|
|
||||||
return this._canvas.xzoom
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum y of the diagram, provided from settings.
|
* Maximum y of the diagram, provided from settings.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
get ymax() {
|
get ymax() { return this._canvas.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.
|
* Zoom on the y-axis of the diagram, provided from settings.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
get yzoom() {
|
get yzoom() { return this._canvas.yzoom }
|
||||||
if(!this.initialized) throw new Error("Attempting yzoom before initialize!")
|
|
||||||
return this._canvas.yzoom
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Label used on the x-axis, provided from settings.
|
* Label used on the x-axis, provided from settings.
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
get xlabel() {
|
get xlabel() { return this._canvas.xlabel }
|
||||||
if(!this.initialized) throw new Error("Attempting xlabel before initialize!")
|
|
||||||
return this._canvas.xlabel
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Label used on the y-axis, provided from settings.
|
* Label used on the y-axis, provided from settings.
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
get ylabel() {
|
get ylabel() { return this._canvas.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.
|
* Width of lines that will be drawn into the canvas, provided from settings.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
get linewidth() {
|
get linewidth() { return this._canvas.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.
|
* Font size of the text that will be drawn into the canvas, provided from settings.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
get textsize() {
|
get textsize() { return this._canvas.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.
|
* True if the canvas should be in logarithmic mode, false otherwise.
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
get logscalex() {
|
get logscalex() { return this._canvas.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.
|
* True if the x graduation should be shown, false otherwise.
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
get showxgrad() {
|
get showxgrad() { return this._canvas.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.
|
* True if the y graduation should be shown, false otherwise.
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
get showygrad() {
|
get showygrad() { return this._canvas.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.
|
* Max power of the logarithmic scaled on the x axis in logarithmic mode.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
get maxgradx() {
|
get maxgradx() {
|
||||||
if(!this.initialized) throw new Error("Attempting maxgradx before initialize!")
|
|
||||||
return Math.min(
|
return Math.min(
|
||||||
309, // 10e309 = Infinity (beyond this land be dragons)
|
309, // 10e309 = Infinity (beyond this land be dragons)
|
||||||
Math.max(
|
Math.max(
|
||||||
|
@ -200,7 +156,6 @@ class CanvasAPI extends Module {
|
||||||
//
|
//
|
||||||
|
|
||||||
requestPaint() {
|
requestPaint() {
|
||||||
if(!this.initialized) throw new Error("Attempting requestPaint before initialize!")
|
|
||||||
this._canvas.requestPaint()
|
this._canvas.requestPaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +163,6 @@ class CanvasAPI extends Module {
|
||||||
* Redraws the entire canvas
|
* Redraws the entire canvas
|
||||||
*/
|
*/
|
||||||
redraw() {
|
redraw() {
|
||||||
if(!this.initialized) throw new Error("Attempting redraw before initialize!")
|
|
||||||
this._ctx = this._canvas.getContext("2d")
|
this._ctx = this._canvas.getContext("2d")
|
||||||
this._computeAxes()
|
this._computeAxes()
|
||||||
this._reset()
|
this._reset()
|
||||||
|
@ -328,7 +282,7 @@ class CanvasAPI extends Module {
|
||||||
// Axis graduation labels
|
// 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.showxgrad) {
|
||||||
if(this.logscalex) {
|
if(this.logscalex) {
|
||||||
for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow+=1) {
|
for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow+=1) {
|
||||||
|
@ -339,21 +293,21 @@ class CanvasAPI extends Module {
|
||||||
} else {
|
} else {
|
||||||
for(let x = 1; x < this.axesSteps.x.maxDraw; x += 1) {
|
for(let x = 1; x < this.axesSteps.x.maxDraw; x += 1) {
|
||||||
let drawX = x*this.axesSteps.x.value
|
let drawX = x*this.axesSteps.x.value
|
||||||
let txtX = this.axesSteps.x.expression.simplify(x).toString().replace(/^\((.+)\)$/, "$1")
|
let txtX = this.axesSteps.x.expression.simplify(x).toString().replace(/^\((.+)\)$/, '$1')
|
||||||
let textHeight = this.measureText(txtX).height
|
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) {
|
if(this.showygrad) {
|
||||||
for(let y = 0; y < this.axesSteps.y.maxDraw; y += 1) {
|
for(let y = 0; y < this.axesSteps.y.maxDraw; y += 1) {
|
||||||
let drawY = y*this.axesSteps.y.value
|
let drawY = y*this.axesSteps.y.value
|
||||||
let txtY = this.axesSteps.y.expression.simplify(y).toString().replace(/^\((.+)\)$/, "$1")
|
let txtY = this.axesSteps.y.expression.simplify(y).toString().replace(/^\((.+)\)$/, '$1')
|
||||||
textWidth = this._ctx.measureText(txtY).width
|
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)
|
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"
|
this._ctx.fillStyle = "#FFFFFF"
|
||||||
|
@ -409,7 +363,7 @@ class CanvasAPI extends Module {
|
||||||
* @param {number} height
|
* @param {number} height
|
||||||
*/
|
*/
|
||||||
drawVisibleImage(image, x, y, width, 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)
|
this._ctx.drawImage(image, x, y, width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,7 +380,7 @@ class CanvasAPI extends Module {
|
||||||
theight += defaultHeight
|
theight += defaultHeight
|
||||||
if(this._ctx.measureText(txt).width > twidth) twidth = this._ctx.measureText(txt).width
|
if(this._ctx.measureText(txt).width > twidth) twidth = this._ctx.measureText(txt).width
|
||||||
}
|
}
|
||||||
return { "width": twidth, "height": theight }
|
return {'width': twidth, 'height': theight}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -494,10 +448,10 @@ class CanvasAPI extends Module {
|
||||||
* @param {number} y2
|
* @param {number} y2
|
||||||
*/
|
*/
|
||||||
drawLine(x1, y1, x2, y2) {
|
drawLine(x1, y1, x2, y2) {
|
||||||
this._ctx.beginPath()
|
this._ctx.beginPath();
|
||||||
this._ctx.moveTo(x1, y1)
|
this._ctx.moveTo(x1, y1);
|
||||||
this._ctx.lineTo(x2, y2)
|
this._ctx.lineTo(x2, y2);
|
||||||
this._ctx.stroke()
|
this._ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -509,9 +463,9 @@ class CanvasAPI extends Module {
|
||||||
* @param {number} dashPxSize
|
* @param {number} dashPxSize
|
||||||
*/
|
*/
|
||||||
drawDashedLine(x1, y1, x2, y2, dashPxSize = 6) {
|
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.drawLine(x1, y1, x2, y2)
|
||||||
this._ctx.setLineDash([])
|
this._ctx.setLineDash([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -542,13 +496,8 @@ class CanvasAPI extends Module {
|
||||||
// Context methods
|
// Context methods
|
||||||
//
|
//
|
||||||
|
|
||||||
get font() {
|
get font() { return this._ctx.font }
|
||||||
return this._ctx.font
|
set font(value) { return this._ctx.font = value }
|
||||||
}
|
|
||||||
|
|
||||||
set font(value) {
|
|
||||||
return this._ctx.font = value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws an act on the canvas centered on a point.
|
* Draws an act on the canvas centered on a point.
|
||||||
|
@ -572,9 +521,9 @@ class CanvasAPI extends Module {
|
||||||
* @param {number} radius
|
* @param {number} radius
|
||||||
*/
|
*/
|
||||||
disc(x, y, radius) {
|
disc(x, y, radius) {
|
||||||
this._ctx.beginPath()
|
this._ctx.beginPath();
|
||||||
this._ctx.arc(x, y, radius, 0, 2 * Math.PI)
|
this._ctx.arc(x, y, radius, 0, 2 * Math.PI)
|
||||||
this._ctx.fill()
|
this._ctx.fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Interface } from "./interface.mjs"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for global APIs in runtime.
|
* Base class for global APIs in runtime.
|
||||||
*/
|
*/
|
||||||
|
@ -26,30 +24,22 @@ export class Module {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string} name - Name of the API
|
* @param {string} name - Name of the API
|
||||||
* @param {Object.<string, (Interface|string)>} initializationParameters - List of parameters for the initialize function.
|
* @param {(Module|undefined)[]} requires - List of APIs required to initialize this one.
|
||||||
*/
|
*/
|
||||||
constructor(name, initializationParameters = {}) {
|
constructor(name, requires = []) {
|
||||||
console.log(`Loading module ${name}...`)
|
console.log(`Loading module ${name}...`)
|
||||||
this.__name = name
|
this.__check_requirements(requires, name)
|
||||||
this.__initializationParameters = initializationParameters
|
|
||||||
this.initialized = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if all requirements are defined.
|
* Checks if all requirements are defined.
|
||||||
* @param {Object.<string, any>} options
|
* @param {(Module|undefined)[]} requires
|
||||||
|
* @param {string} name
|
||||||
*/
|
*/
|
||||||
initialize(options) {
|
__check_requirements(requires, name) {
|
||||||
if(this.initialized)
|
for(let requirement of requires) {
|
||||||
throw new Error(`Cannot reinitialize module ${this.__name}.`)
|
if(requirement === undefined)
|
||||||
for(const [name, value] of Object.entries(this.__initializationParameters)) {
|
throw new Error(`Requirement ${requires.indexOf(requirement)} of ${name} has not been initialized.`)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -36,7 +36,10 @@ const evalVariables = {
|
||||||
|
|
||||||
export class ExprParserAPI extends Module {
|
export class ExprParserAPI extends Module {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("ExprParser")
|
super("ExprParser", [
|
||||||
|
/** @type {ObjectsAPI} */
|
||||||
|
Modules.Objects
|
||||||
|
])
|
||||||
this.currentVars = {}
|
this.currentVars = {}
|
||||||
this._parser = new Parser()
|
this._parser = new Parser()
|
||||||
|
|
||||||
|
|
|
@ -18,62 +18,26 @@
|
||||||
|
|
||||||
import { Module } from "./common.mjs"
|
import { Module } from "./common.mjs"
|
||||||
import Latex from "./latex.mjs"
|
import Latex from "./latex.mjs"
|
||||||
import { HistoryInterface, NUMBER, STRING } from "./interface.mjs"
|
|
||||||
|
|
||||||
|
|
||||||
class HistoryAPI extends Module {
|
class HistoryAPI extends Module {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("History", {
|
super('History', [
|
||||||
historyObj: HistoryInterface,
|
Modules.Latex
|
||||||
themeTextColor: STRING,
|
])
|
||||||
imageDepth: NUMBER,
|
|
||||||
fontSize: NUMBER
|
|
||||||
})
|
|
||||||
// History QML object
|
// History QML object
|
||||||
this.history = null
|
this.history = null;
|
||||||
this.themeTextColor = "#FF0000"
|
this.themeTextColor = "#ff0000";
|
||||||
this.imageDepth = 2
|
this.imageDepth = 2;
|
||||||
this.fontSize = 28
|
this.fontSize = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize({ historyObj, themeTextColor, imageDepth, fontSize }) {
|
undo() { this.history.undo() }
|
||||||
super.initialize({ historyObj, themeTextColor, imageDepth, fontSize })
|
redo() { this.history.redo() }
|
||||||
console.log("Initializing history...")
|
clear() { this.history.clear() }
|
||||||
this.history = historyObj
|
addToHistory(action) { this.history.addToHistory(action) }
|
||||||
this.themeTextColor = themeTextColor
|
unserialize(...data) { this.history.unserialize(...data) }
|
||||||
this.imageDepth = imageDepth
|
serialize() { return this.history.serialize() }
|
||||||
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} */
|
/** @type {HistoryAPI} */
|
||||||
|
|
|
@ -1,130 +0,0 @@
|
||||||
/**
|
|
||||||
* 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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,17 +20,14 @@ import { Module } from "./common.mjs"
|
||||||
import Objects from "./objects.mjs"
|
import Objects from "./objects.mjs"
|
||||||
import History from "./history.mjs"
|
import History from "./history.mjs"
|
||||||
import Canvas from "./canvas.mjs"
|
import Canvas from "./canvas.mjs"
|
||||||
import { DialogInterface, FUNCTION, Interface, RootInterface, SettingsInterface } from "./interface.mjs"
|
|
||||||
|
|
||||||
|
|
||||||
class IOAPI extends Module {
|
class IOAPI extends Module {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("IO", {
|
super("IO", [
|
||||||
alert: DialogInterface,
|
Modules.Objects,
|
||||||
root: RootInterface,
|
Modules.History
|
||||||
settings: SettingsInterface
|
])
|
||||||
})
|
|
||||||
/**
|
/**
|
||||||
* Path of the currently opened file. Empty if no file is opened.
|
* Path of the currently opened file. Empty if no file is opened.
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
@ -40,13 +37,12 @@ class IOAPI extends Module {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes module with QML elements.
|
* Initializes module with QML elements.
|
||||||
* @param {RootInterface} root
|
* @param {{width: number, height: number, updateObjectsLists: function()}} rootElement
|
||||||
* @param {SettingsInterface} settings
|
* @param {Settings} settings
|
||||||
* @param {{show: function(string)}} alert
|
* @param {{show: function(string)}} alert
|
||||||
*/
|
*/
|
||||||
initialize({ root, settings, alert }) {
|
initialize(rootElement, settings, alert) {
|
||||||
super.initialize({ root, settings, alert })
|
this.rootElement = rootElement
|
||||||
this.rootElement = root
|
|
||||||
this.settings = settings
|
this.settings = settings
|
||||||
this.alert = alert
|
this.alert = alert
|
||||||
}
|
}
|
||||||
|
@ -56,7 +52,6 @@ class IOAPI extends Module {
|
||||||
* @param {string} filename
|
* @param {string} filename
|
||||||
*/
|
*/
|
||||||
saveDiagram(filename) {
|
saveDiagram(filename) {
|
||||||
if(!this.initialized) throw new Error("Attempting saveDiagram before initialize!")
|
|
||||||
// Add extension if necessary
|
// Add extension if necessary
|
||||||
if(["lpf"].indexOf(filename.split(".")[filename.split(".").length - 1]) === -1)
|
if(["lpf"].indexOf(filename.split(".")[filename.split(".").length - 1]) === -1)
|
||||||
filename += ".lpf"
|
filename += ".lpf"
|
||||||
|
@ -98,13 +93,11 @@ class IOAPI extends Module {
|
||||||
* @param {string} filename
|
* @param {string} filename
|
||||||
*/
|
*/
|
||||||
loadDiagram(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()
|
let basename = filename.split("/").pop()
|
||||||
this.alert.show(qsTranslate("io", "Loading file '%1'.").arg(basename))
|
this.alert.show(qsTranslate("io", "Loading file '%1'.").arg(basename))
|
||||||
let data = JSON.parse(Helper.load(filename))
|
let data = JSON.parse(Helper.load(filename))
|
||||||
let error = ""
|
let error = ""
|
||||||
if(data.hasOwnProperty("type") && data["type"] === "logplotv1") {
|
if(Object.keys(data).includes("type") && data["type"] === "logplotv1") {
|
||||||
History.clear()
|
History.clear()
|
||||||
// Importing settings
|
// Importing settings
|
||||||
this.settings.saveFilename = filename
|
this.settings.saveFilename = filename
|
||||||
|
@ -136,7 +129,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.
|
// 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"]) {
|
for(let objType in data["objects"]) {
|
||||||
if(Object.keys(Objects.types).includes(objType)) {
|
if(Object.keys(Objects.types).indexOf(objType) > -1) {
|
||||||
Objects.currentObjects[objType] = []
|
Objects.currentObjects[objType] = []
|
||||||
for(let objData of data["objects"][objType]) {
|
for(let objData of data["objects"][objType]) {
|
||||||
/** @type {DrawableObject} */
|
/** @type {DrawableObject} */
|
||||||
|
@ -164,7 +157,7 @@ class IOAPI extends Module {
|
||||||
}
|
}
|
||||||
if(error !== "") {
|
if(error !== "") {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
this.alert.show(qsTranslate("io", "Could not load file: ") + error)
|
this.alert.show(qsTranslate("io", "Could not save file: ") + error)
|
||||||
// TODO: Error handling
|
// TODO: Error handling
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,10 @@ class LatexRenderResult {
|
||||||
|
|
||||||
class LatexAPI extends Module {
|
class LatexAPI extends Module {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("Latex")
|
super("Latex", [
|
||||||
|
/** @type {ExprParserAPI} */
|
||||||
|
Modules.ExprParser
|
||||||
|
])
|
||||||
/**
|
/**
|
||||||
* true if latex has been enabled by the user, false otherwise.
|
* true if latex has been enabled by the user, false otherwise.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -22,7 +22,10 @@ import DefaultGraph from "../preferences/default.mjs"
|
||||||
|
|
||||||
class PreferencesAPI extends Module {
|
class PreferencesAPI extends Module {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('Preferences')
|
super('Preferences', [
|
||||||
|
Modules.Canvas,
|
||||||
|
Modules.Latex
|
||||||
|
])
|
||||||
|
|
||||||
this.categories = {
|
this.categories = {
|
||||||
[QT_TRANSLATE_NOOP('settingCategory', 'general')]: General,
|
[QT_TRANSLATE_NOOP('settingCategory', 'general')]: General,
|
||||||
|
|
Loading…
Reference in a new issue