2021-08-14 16:57:29 +00:00
|
|
|
/**
|
2022-03-05 16:49:35 +00:00
|
|
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
2022-01-12 17:47:17 +00:00
|
|
|
* Copyright (C) 2022 Ad5001
|
2021-08-14 16:57:29 +00:00
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
.pragma library
|
|
|
|
|
|
|
|
.import "../utils.js" as Utils
|
|
|
|
.import "../objects.js" as Objects
|
2022-03-06 16:44:31 +00:00
|
|
|
.import "../math/latex.js" as Latex
|
2021-08-14 16:57:29 +00:00
|
|
|
|
|
|
|
// This file contains the default data to be imported from all other objects
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Creates a new name for an object, based on the \c allowedLetters.
|
|
|
|
* If variables with each of the allowedLetters is created, a subscript
|
|
|
|
* number is added to the name.
|
|
|
|
* @return {string} New unused name for a new object.
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
function getNewName(allowedLetters) {
|
|
|
|
// Allows to get a new name, based on the allowed letters,
|
|
|
|
// as well as adding a sub number when needs be.
|
|
|
|
var newid = 0
|
|
|
|
var ret
|
|
|
|
do {
|
|
|
|
var letter = allowedLetters[newid % allowedLetters.length]
|
|
|
|
var num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length)
|
|
|
|
ret = letter + (num > 0 ? Utils.textsub(num-1) : '')
|
|
|
|
newid += 1
|
2022-10-17 18:25:29 +00:00
|
|
|
} while(ret in Objects.currentObjectsByName)
|
2021-08-14 16:57:29 +00:00
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Class to extend for every type of object that
|
|
|
|
* can be drawn on the canvas.
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
class DrawableObject {
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Base name of the object. Needs to be constant over time.
|
|
|
|
* @return {string} Type of the object.
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
static type(){return 'Unknown'}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Translated name of the object to be shown to the user.
|
|
|
|
* @return {string}
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
static displayType(){return 'Unknown'}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Translated name of the object in plural form to be shown to the user.
|
|
|
|
* @return {string}
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
static displayTypeMultiple(){return 'Unknowns'}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* True if this object can be created by the user, false if it can only
|
|
|
|
* be instantiated by other objects
|
|
|
|
* @return {bool}
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
static createable() {return true}
|
2022-03-06 16:44:31 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* List of properties used in the Object Editor.
|
|
|
|
*
|
|
|
|
* Properties are set with key as property name and
|
|
|
|
* value as it's type name (e.g 'Expression', 'string'...),
|
|
|
|
* an Enum for enumerations, an ObjectType for DrawableObjects
|
|
|
|
* with a specific type, a List instance for lists, a
|
|
|
|
* Dictionary instance for dictionaries...
|
|
|
|
*
|
|
|
|
* If the property is to be translated, the key should be passed
|
|
|
|
* through the QT_TRANSLATE_NOOP macro in that form:
|
|
|
|
* [QT_TRANSLATE_NOOP('prop','key')]
|
|
|
|
* Enums that are translated should be indexed in parameters.js and
|
|
|
|
* then be linked directly here.
|
|
|
|
*
|
|
|
|
* @return {Dictionary}
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
static properties() {return {}}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* True if this object can be executed, so that an y value might be computed
|
|
|
|
* for an x value. Only ExecutableObjects have that property set to true.
|
|
|
|
* @return {bool}
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
static executable() {return false}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Base constructor for the object.
|
|
|
|
* @param {string} name - Name of the object
|
|
|
|
* @param {bool} visible - true if the object is visible, false otherwise.
|
|
|
|
* @param {color} color - Color of the object (can be string or QColor)
|
|
|
|
* @param {Enum} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label.
|
|
|
|
* @constructor()
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
constructor(name, visible = true, color = null, labelContent = 'name + value') {
|
|
|
|
if(color == null) color = Utils.getRandomColor()
|
|
|
|
this.type = 'Unknown'
|
|
|
|
this.name = name
|
|
|
|
this.visible = visible
|
|
|
|
this.color = color
|
|
|
|
this.labelContent = labelContent // "null", "name", "name + value"
|
|
|
|
this.requiredBy = []
|
|
|
|
}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Serilizes the object in an array that can be JSON serialized.
|
|
|
|
* These parameters will be re-entered in the constructor when restored.
|
|
|
|
* @return {array}
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
export() {
|
|
|
|
// Should return what will be inputed as arguments when a file is loaded (serializable form)
|
|
|
|
return [this.name, this.visible, this.color.toString(), this.labelContent]
|
|
|
|
}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* String representing the object that will be displayed to the user.
|
|
|
|
* It allows for 2 lines and translated text, but it shouldn't be too long.
|
|
|
|
* @return {string}
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
getReadableString() {
|
|
|
|
return `${this.name} = Unknown`
|
|
|
|
}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Latex markuped version of the readable string.
|
|
|
|
* Every non latin character should be passed as latex symbols and formulas
|
|
|
|
* should be in latex form.
|
|
|
|
* See ../latex.js for helper methods.
|
|
|
|
* @return {string}
|
|
|
|
*/
|
|
|
|
getLatexString() {
|
2022-03-05 19:57:21 +00:00
|
|
|
return this.getReadableString()
|
|
|
|
}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Readable string content of the label depending on the value of the \c latexContent.
|
|
|
|
* @return {string}
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
getLabel() {
|
2022-03-05 23:55:32 +00:00
|
|
|
switch(this.labelContent) {
|
|
|
|
case 'name':
|
|
|
|
return this.name
|
|
|
|
case 'name + value':
|
|
|
|
return this.getReadableString()
|
|
|
|
case 'null':
|
|
|
|
return ''
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Latex markup string content of the label depending on the value of the \c latexContent.
|
|
|
|
* Every non latin character should be passed as latex symbols and formulas
|
|
|
|
* should be in latex form.
|
|
|
|
* See ../latex.js for helper methods.
|
|
|
|
* @return {string}
|
|
|
|
*/
|
2022-03-05 23:55:32 +00:00
|
|
|
getLatexLabel() {
|
2021-08-14 16:57:29 +00:00
|
|
|
switch(this.labelContent) {
|
|
|
|
case 'name':
|
2022-03-06 16:44:31 +00:00
|
|
|
return Latex.variable(this.name)
|
2021-08-14 16:57:29 +00:00
|
|
|
case 'name + value':
|
2022-03-06 16:44:31 +00:00
|
|
|
return this.getLatexString()
|
2021-08-14 16:57:29 +00:00
|
|
|
case 'null':
|
|
|
|
return ''
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Callback method when one of the properties of the object is updated.
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
update() {
|
|
|
|
for(var req of this.requiredBy) {
|
|
|
|
req.update()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Callback method when the object is about to get deleted.
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
delete() {
|
|
|
|
for(var toRemove of this.requiredBy) {
|
|
|
|
toRemove.delete()
|
2021-09-23 14:17:57 +00:00
|
|
|
Objects.currentObjects[toRemove.type] = Objects.currentObjects[toRemove.type].filter(obj => obj.name != toRemove.name)
|
2021-08-14 16:57:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Abstract method. Draw the object onto the \c canvas with the 2D context \c ctx.
|
|
|
|
* @param {Canvas} canvas
|
|
|
|
* @param {Context2D} ctx
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
draw(canvas, ctx) {}
|
2022-01-26 11:02:10 +00:00
|
|
|
|
2022-03-07 01:46:38 +00:00
|
|
|
/**
|
|
|
|
* Applicates a \c drawFunction with two position arguments depending on
|
|
|
|
* both the \c posX and \c posY of where the label should be displayed,
|
|
|
|
* and the \c labelPosition which declares the label should be displayed
|
|
|
|
* relatively to that position.
|
2022-03-07 16:20:24 +00:00
|
|
|
*
|
2022-03-07 01:46:38 +00:00
|
|
|
* @param {string|Enum} labelPosition - Position of the label relative to the marked position
|
|
|
|
* @param {number} offset - Margin between the position and the object to be drawn
|
|
|
|
* @param {Dictionary} size - Size of the label item, containing two properties, "width" and "height"
|
|
|
|
* @param {number} posX - Component of the marked position on the x-axis
|
|
|
|
* @param {number} posY - Component of the marked position on the y-axis
|
|
|
|
* @param {function} drawFunction - Function with two arguments (x, y) that will be called to draw the label
|
|
|
|
*/
|
2022-03-07 16:20:24 +00:00
|
|
|
drawPositionDivergence(labelPosition, offset, size, posX, posY, drawFunction) {
|
2022-03-07 01:46:38 +00:00
|
|
|
switch(labelPosition) {
|
|
|
|
case 'center':
|
|
|
|
drawFunction(posX-size.width/2, posY-size.height/2)
|
|
|
|
break;
|
|
|
|
case 'top':
|
|
|
|
case 'above':
|
|
|
|
drawFunction(posX-size.width/2, posY-size.height-offset)
|
|
|
|
break;
|
|
|
|
case 'bottom':
|
|
|
|
case 'below':
|
|
|
|
drawFunction(posX-size.width/2, posY+offset)
|
|
|
|
break;
|
|
|
|
case 'left':
|
|
|
|
drawFunction(posX-size.width-offset, posY-size.height/2)
|
|
|
|
break;
|
|
|
|
case 'right':
|
|
|
|
drawFunction(posX+offset, posY-size.height/2)
|
|
|
|
break;
|
|
|
|
case 'top-left':
|
|
|
|
case 'above-left':
|
|
|
|
drawFunction(posX-size.width, posY-size.height-offset)
|
|
|
|
break;
|
|
|
|
case 'top-right':
|
|
|
|
case 'above-right':
|
|
|
|
drawFunction(posX+offset, posY-size.height-offset)
|
|
|
|
break;
|
|
|
|
case 'bottom-left':
|
|
|
|
case 'below-left':
|
|
|
|
drawFunction(posX-size.width-offset, posY+offset)
|
|
|
|
break;
|
|
|
|
case 'bottom-right':
|
|
|
|
case 'below-right':
|
|
|
|
drawFunction(posX+offset, posY+offset)
|
|
|
|
break;
|
2022-03-06 16:44:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-07 16:20:24 +00:00
|
|
|
/**
|
|
|
|
* Automaticly draw text (by default the label of the object on the \c canvas with
|
|
|
|
* the 2D context \c ctx depending on user settings.
|
|
|
|
* This method takes into account both the \c posX and \c posY of where the label
|
|
|
|
* should be displayed, including the \c labelPosition relative to it.
|
|
|
|
* The text is get both through the \c getLatexFunction and \c getTextFunction
|
|
|
|
* depending on whether to use latex.
|
|
|
|
* 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 {Context2D} 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
|
|
|
|
* @param {number} posY - Component of the marked position on the y-axis
|
|
|
|
* @param {bool} forceText - Force the rendering of the label as text
|
|
|
|
* @param {function|null} getLatexFunction - Function (no argument) to get the latex markup to be displayed
|
|
|
|
* @param {function|null} getTextFunction - Function (no argument) to get the text to be displayed
|
|
|
|
* @param {function|null} drawFunctionLatex - Function (x,y,imageData) to display the latex image
|
2022-03-07 19:28:50 +00:00
|
|
|
* @param {function|null} drawFunctionText - Function (x,y,text,textSize) to display the text
|
2022-03-07 16:20:24 +00:00
|
|
|
*/
|
|
|
|
drawLabel(canvas, ctx, labelPosition, posX, posY, forceText = false,
|
|
|
|
getLatexFunction = null, getTextFunction = null, drawFunctionLatex = null, drawFunctionText = null) {
|
|
|
|
// Default functions
|
|
|
|
if(getLatexFunction == null)
|
|
|
|
getLatexFunction = this.getLatexLabel.bind(this)
|
|
|
|
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)
|
|
|
|
if(drawFunctionText == null)
|
2022-03-07 19:28:50 +00:00
|
|
|
drawFunctionText = (x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, y+textSize.height) // Positioned from left bottom
|
2022-03-07 16:20:24 +00:00
|
|
|
// Drawing the label
|
|
|
|
let offset
|
2022-03-07 19:28:50 +00:00
|
|
|
if(!forceText && Latex.enabled) { // TODO: Check for user setting with Latex.
|
2022-03-07 16:20:24 +00:00
|
|
|
// With latex
|
|
|
|
let drawLblCb = function(canvas, ctx, ltxImg) {
|
2022-03-07 19:28:50 +00:00
|
|
|
this.drawPositionDivergence(labelPosition, 8+ctx.lineWidth/2, ltxImg, posX, posY, (x,y) => drawFunctionLatex(x,y,ltxImg))
|
2022-03-07 16:20:24 +00:00
|
|
|
}
|
|
|
|
let ltxLabel = getLatexFunction();
|
|
|
|
if(ltxLabel != "")
|
|
|
|
canvas.renderLatexImage(ltxLabel, this.color, drawLblCb.bind(this))
|
|
|
|
} else {
|
|
|
|
// Without latex
|
|
|
|
let text = getTextFunction()
|
|
|
|
ctx.font = `${canvas.textsize}px sans-serif`
|
2022-03-07 19:28:50 +00:00
|
|
|
let textSize = canvas.measureText(ctx, text)
|
|
|
|
this.drawPositionDivergence(labelPosition, 8+ctx.lineWidth/2, textSize, posX, posY, (x,y) => drawFunctionText(x,y,text,textSize))
|
2022-03-07 16:20:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-26 11:02:10 +00:00
|
|
|
toString() {
|
|
|
|
return this.name;
|
|
|
|
}
|
2021-08-14 16:57:29 +00:00
|
|
|
}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Class to be extended for every object on which
|
|
|
|
* an y for a x can be computed with the execute function.
|
|
|
|
* If a value cannot be found during execute, it will
|
|
|
|
* return null. However, theses same x values will
|
|
|
|
* return false when passed to canExecute.
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
class ExecutableObject extends DrawableObject {
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Returns the corresponding y value for an x value.
|
|
|
|
* If the object isn't defined on the given x, then
|
|
|
|
* this function will return null.
|
|
|
|
*
|
|
|
|
* @param {number} x
|
|
|
|
* @returns {number|null}
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
execute(x = 1) {return 0}
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Returns false if the object isn't defined on the given x, true otherwise.
|
|
|
|
*
|
|
|
|
* @param {number} x
|
|
|
|
* @returns {bool}
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
canExecute(x = 1) {return true}
|
2022-03-06 16:44:31 +00:00
|
|
|
/**
|
|
|
|
* Returns the simplified expression string for a given x.
|
|
|
|
*
|
|
|
|
* @param {number} x
|
|
|
|
* @returns {bool}
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
simplify(x = 1) {return '0'}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* True if this object can be executed, so that an y value might be computed
|
|
|
|
* for an x value. Only ExecutableObjects have that property set to true.
|
|
|
|
* @return {bool}
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
static executable() {return true}
|
|
|
|
}
|
|
|
|
|
2022-03-06 16:44:31 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Registers the object \c obj in the object list.
|
|
|
|
* @param {DrawableObject} obj - Object to be registered.
|
|
|
|
*/
|
2021-08-14 16:57:29 +00:00
|
|
|
function registerObject(obj) {
|
|
|
|
// Registers an object to be used in LogarithmPlotter.
|
|
|
|
// This function is called from autoload.js
|
|
|
|
if(obj.prototype instanceof DrawableObject) {
|
|
|
|
Objects.types[obj.type()] = obj
|
|
|
|
} else {
|
|
|
|
console.error("Could not register object " + (obj.type()) + ", as it isn't a DrawableObject.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|