Compare commits

..

3 commits

Author SHA1 Message Date
c32d70e9ed
Reformatting JS files
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-27 02:54:20 +02:00
8cefc56ac7
Adding JS polyfills to avoid issues because core-js does not deal well with the environment it's given. 2024-09-27 02:34:42 +02:00
850d076268
Using more modern ECMAScript 2024-09-26 23:16:58 +02:00
49 changed files with 1583 additions and 1435 deletions

View file

@ -538,7 +538,7 @@ Item {
try { try {
expr = new JS.MathLib.Expression(value.toString()) expr = new JS.MathLib.Expression(value.toString())
// Check if the expression is valid, throws error otherwise. // Check if the expression is valid, throws error otherwise.
if(!expr.allRequirementsFullfilled()) { if(!expr.allRequirementsFulfilled()) {
let undefVars = expr.undefinedVariables() let undefVars = expr.undefinedVariables()
if(undefVars.length > 1) if(undefVars.length > 1)
throw new Error(qsTranslate('error', 'No object found with names %1.').arg(undefVars.join(', '))) throw new Error(qsTranslate('error', 'No object found with names %1.').arg(undefVars.join(', ')))

View file

@ -16,6 +16,8 @@
* 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 js from "./lib/polyfills/js.mjs"
// Loading modules in order // Loading modules in order
import * as Objects from "./module/objects.mjs" import * as Objects from "./module/objects.mjs"
import * as ExprParser from "./module/expreval.mjs" import * as ExprParser from "./module/expreval.mjs"

View file

@ -21,10 +21,13 @@ import Objects from "../module/objects.mjs"
export default class ColorChanged extends EditedProperty { export default class ColorChanged extends EditedProperty {
// Action used everytime when an object's color is changed // Action used everytime when an object's color is changed
type(){return 'ColorChanged'} type() {
return "ColorChanged"
icon(){return 'appearance'} }
icon() {
return "appearance"
}
constructor(targetName = "", targetType = "Point", oldColor = "black", newColor = "white") { constructor(targetName = "", targetType = "Point", oldColor = "black", newColor = "white") {
super(targetName, targetType, "color", oldColor, newColor) super(targetName, targetType, "color", oldColor, newColor)
@ -34,7 +37,9 @@ export default class ColorChanged extends EditedProperty {
return [this.targetName, this.targetType, this.previousValue, this.newValue] return [this.targetName, this.targetType, this.previousValue, this.newValue]
} }
color(darkVer=false){return darkVer ? 'purple' : 'plum'} color(darkVer = false) {
return darkVer ? "purple" : "plum"
}
getReadableString() { getReadableString() {
return qsTranslate("color", "%1 %2's color changed from %3 to %4.") return qsTranslate("color", "%1 %2's color changed from %3 to %4.")
@ -49,7 +54,7 @@ export default class ColorChanged extends EditedProperty {
getHTMLString() { getHTMLString() {
return qsTranslate("color", "%1 %2's color changed from %3 to %4.") return qsTranslate("color", "%1 %2's color changed from %3 to %4.")
.arg(Objects.types[this.targetType].displayType()) .arg(Objects.types[this.targetType].displayType())
.arg('<b style="font-size: 15px;">&nbsp;' + this.targetName + "&nbsp;</b>") .arg("<b style=\"font-size: 15px;\">&nbsp;" + this.targetName + "&nbsp;</b>")
.arg(this.formatColor(this.previousValue)).arg(this.formatColor(this.newValue)) .arg(this.formatColor(this.previousValue)).arg(this.formatColor(this.newValue))
} }
} }

View file

@ -25,14 +25,18 @@ export class Action {
* *
* @returns {string} * @returns {string}
*/ */
type(){return 'Unknown'} type() {
return "Unknown"
}
/** /**
* Icon associated with the action. * Icon associated with the action.
* *
* @returns {string} * @returns {string}
*/ */
icon(){return 'position'} icon() {
return "position"
}
// TargetName is the name of the object that's targeted by the event. // TargetName is the name of the object that's targeted by the event.
constructor(targetName = "", targetType = "Point") { constructor(targetName = "", targetType = "Point") {
@ -43,12 +47,14 @@ export class Action {
/** /**
* Undoes the action. * Undoes the action.
*/ */
undo() {} undo() {
}
/** /**
* Redoes the action. * Redoes the action.
*/ */
redo() {} redo() {
}
/** /**
* Export the action to a serializable format. * Export the action to a serializable format.
@ -61,12 +67,12 @@ export class Action {
} }
/** /**
* Returns a string with the human readable description of the action. * Returns a string with the human-readable description of the action.
* *
* @returns {string} * @returns {string}
*/ */
getReadableString() { getReadableString() {
return 'Unknown action' return "Unknown action"
} }
/** /**
@ -76,7 +82,7 @@ export class Action {
* @returns {string} * @returns {string}
*/ */
getIconRichText(type) { getIconRichText(type) {
return `<img source="../icons/objects/${type}.svg" style="color: ${History.themeTextColor};" width=18 height=18></img>` return `<img src="../icons/objects/${type}.svg" style="color: ${History.themeTextColor};" width=18 height=18></img>`
} }
/** /**
@ -85,20 +91,16 @@ export class Action {
* @param {string} latexString - Source string of the latex. * @param {string} latexString - Source string of the latex.
* @returns {Promise<string>} * @returns {Promise<string>}
*/ */
renderLatexAsHtml(latexString) { async renderLatexAsHtml(latexString) {
if(!Latex.enabled) if(!Latex.enabled)
throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.") throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.")
return new Promise(resolve => { const imgDepth = History.imageDepth
let imgDepth = History.imageDepth const { source, width, height } = await Latex.requestAsyncRender(
Latex.requestAsyncRender(
latexString, latexString,
imgDepth * (History.fontSize + 2), imgDepth * (History.fontSize + 2),
History.themeTextColor History.themeTextColor
).then((imgData) => { )
const { source, width, height } = imgData return `<img src="${source}" width="${width / imgDepth}" height="${height / imgDepth}" style="vertical-align: middle"/>`
resolve(`<img src="${source}" width="${width/imgDepth}" height="${height/imgDepth}" style="vertical-align: middle"/>`)
})
})
} }
/** /**

View file

@ -19,13 +19,21 @@
import Objects from "../module/objects.mjs" import Objects from "../module/objects.mjs"
import { Action } from "./common.mjs" import { Action } from "./common.mjs"
/**
* Action used for the creation of an object.
*/
export default class CreateNewObject extends Action { export default class CreateNewObject extends Action {
// Action used for the creation of an object type() {
type(){return 'CreateNewObject'} return "CreateNewObject"
}
icon(){return 'create'} icon() {
return "create"
}
color(darkVer=false){return darkVer ? 'green' : 'lime'} color(darkVer = false) {
return darkVer ? "green" : "lime"
}
constructor(targetName = "", targetType = "Point", properties = []) { constructor(targetName = "", targetType = "Point", properties = []) {
super(targetName, targetType) super(targetName, targetType)
@ -55,7 +63,7 @@ export default class CreateNewObject extends Action {
getHTMLString() { getHTMLString() {
return qsTranslate("create", "New %1 %2 created.") return qsTranslate("create", "New %1 %2 created.")
.arg(Objects.types[this.targetType].displayType()) .arg(Objects.types[this.targetType].displayType())
.arg('<b style="font-size: 15px;">' + this.targetName + "</b>") .arg("<b style=\"font-size: 15px;\">" + this.targetName + "</b>")
} }
} }

View file

@ -20,10 +20,10 @@ import Objects from "../module/objects.mjs"
import CreateNewObject from "./create.mjs" import CreateNewObject from "./create.mjs"
export default class DeleteObject extends CreateNewObject { /**
/**
* Action used at the deletion of an object. Basically the same thing as creating a new object, except Redo & Undo are reversed. * Action used at the deletion of an object. Basically the same thing as creating a new object, except Redo & Undo are reversed.
*/ */
export default class DeleteObject extends CreateNewObject {
type(){return 'DeleteObject'} type(){return 'DeleteObject'}
icon(){return 'delete'} icon(){return 'delete'}

View file

@ -22,14 +22,20 @@ import * as MathLib from "../math/index.mjs"
import { Action } from "./common.mjs" import { Action } from "./common.mjs"
import { DrawableObject } from "../objs/common.mjs" import { DrawableObject } from "../objs/common.mjs"
/**
* Action used everytime an object's property has been changed.
*/
export default class EditedProperty extends Action { export default class EditedProperty extends Action {
// Action used everytime an object's property has been changed type() {
type(){return 'EditedProperty'} return "EditedProperty"
}
icon(){return 'modify'} icon() {
return "modify"
}
color(darkVer=false){ color(darkVer = false) {
return darkVer ? 'darkslateblue' : 'cyan'; return darkVer ? "darkslateblue" : "cyan"
} }
/** /**
@ -49,12 +55,12 @@ export default class EditedProperty extends Action {
this.newValue = newValue this.newValue = newValue
this.propertyType = Objects.types[targetType].properties()[targetProperty] this.propertyType = Objects.types[targetType].properties()[targetProperty]
if(valueIsExpressionNeedingImport) { if(valueIsExpressionNeedingImport) {
if(typeof this.propertyType == 'object' && this.propertyType.type === "Expression") { if(typeof this.propertyType == "object" && this.propertyType.type === "Expression") {
this.previousValue = new MathLib.Expression(this.previousValue); this.previousValue = new MathLib.Expression(this.previousValue)
this.newValue = new MathLib.Expression(this.newValue); this.newValue = new MathLib.Expression(this.newValue)
} else if(this.propertyType === "Domain") { } else if(this.propertyType === "Domain") {
this.previousValue = MathLib.parseDomain(this.previousValue); this.previousValue = MathLib.parseDomain(this.previousValue)
this.newValue = MathLib.parseDomain(this.newValue); this.newValue = MathLib.parseDomain(this.newValue)
} else { } else {
// Objects // Objects
this.previousValue = Objects.currentObjectsByName[this.previousValue] // Objects.getObjectByName(this.previousValue); this.previousValue = Objects.currentObjectsByName[this.previousValue] // Objects.getObjectByName(this.previousValue);
@ -93,42 +99,42 @@ export default class EditedProperty extends Action {
case "Enum": case "Enum":
this.prevString = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.previousValue)] this.prevString = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.previousValue)]
this.nextString = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.newValue)] this.nextString = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.newValue)]
break; break
case "ObjectType": case "ObjectType":
this.prevString = this.previousValue == null ? "null" : this.previousValue.name this.prevString = this.previousValue == null ? "null" : this.previousValue.name
this.nextString = this.newValue == null ? "null" : this.newValue.name this.nextString = this.newValue == null ? "null" : this.newValue.name
break; break
case "List": case "List":
this.prevString = this.previousValue.join(",") this.prevString = this.previousValue.join(",")
this.nextString = this.newValue.name.join(",") this.nextString = this.newValue.name.join(",")
break; break
case "Dict": case "Dict":
this.prevString = JSON.stringify(this.previousValue) this.prevString = JSON.stringify(this.previousValue)
this.nextString = JSON.stringify(this.newValue) this.nextString = JSON.stringify(this.newValue)
break; break
case "Expression": case "Expression":
this.prevString = this.previousValue == null ? "null" : this.previousValue.toString() this.prevString = this.previousValue == null ? "null" : this.previousValue.toString()
this.nextString = this.newValue == null ? "null" : this.newValue.toString() this.nextString = this.newValue == null ? "null" : this.newValue.toString()
break; break
} }
} else { } else {
this.prevString = this.previousValue == null ? "null" : this.previousValue.toString() this.prevString = this.previousValue == null ? "null" : this.previousValue.toString()
this.nextString = this.newValue == null ? "null" : this.newValue.toString() this.nextString = this.newValue == null ? "null" : this.newValue.toString()
} }
// HTML // HTML
this.prevHTML = '<tt style="background: rgba(128,128,128,0.1);">&nbsp;'+this.prevString+'&nbsp;</tt>' this.prevHTML = "<tt style=\"background: rgba(128,128,128,0.1);\">&nbsp;" + this.prevString + "&nbsp;</tt>"
this.nextHTML = '<tt style="background: rgba(128,128,128,0.1);">&nbsp;'+this.nextString+'&nbsp;</tt>' this.nextHTML = "<tt style=\"background: rgba(128,128,128,0.1);\">&nbsp;" + this.nextString + "&nbsp;</tt>"
if(Latex.enabled && typeof this.propertyType == 'object' && this.propertyType.type === "Expression") { if(Latex.enabled && typeof this.propertyType == "object" && this.propertyType.type === "Expression") {
// Store promises so that querying can wait for them to finish. // Store promises so that querying can wait for them to finish.
this._renderPromises = [ this._renderPromises = [
this.renderLatexAsHtml(this.previousValue.latexMarkup).then(prev => this.prevHTML = prev), this.renderLatexAsHtml(this.previousValue.latexMarkup).then(prev => this.prevHTML = prev),
this.renderLatexAsHtml(this.newValue.latexMarkup).then(next => this.nextHTML = prev) this.renderLatexAsHtml(this.newValue.latexMarkup).then(next => this.nextHTML = next)
] ]
} }
} }
getReadableString() { getReadableString() {
return qsTranslate("editproperty", '%1 of %2 %3 changed from "%4" to "%5".') return qsTranslate("editproperty", "%1 of %2 %3 changed from \"%4\" to \"%5\".")
.arg(this.targetPropertyReadable) .arg(this.targetPropertyReadable)
.arg(Objects.types[this.targetType].displayType()) .arg(Objects.types[this.targetType].displayType())
.arg(this.targetName).arg(this.prevString).arg(this.nextString) .arg(this.targetName).arg(this.prevString).arg(this.nextString)
@ -138,22 +144,16 @@ export default class EditedProperty extends Action {
* *
* @return {Promise<string>|string} * @return {Promise<string>|string}
*/ */
getHTMLString() { async getHTMLString() {
return new Promise(resolve => { const translation = qsTranslate("editproperty", "%1 of %2 changed from %3 to %4.")
const translation = qsTranslate("editproperty", '%1 of %2 changed from %3 to %4.')
.arg(this.targetPropertyReadable) .arg(this.targetPropertyReadable)
.arg('<b style="font-size: 15px;">&nbsp;' + this.targetName + '&nbsp;</b>') .arg("<b style=\"font-size: 15px;\">&nbsp;" + this.targetName + "&nbsp;</b>")
// Check if we need to wait for LaTeX HTML to be rendered. // Check if we need to wait for LaTeX HTML to be rendered.
if(this.prevHTML !== undefined && this.nextHTML !== undefined) if(this.prevHTML === undefined || this.nextHTML === undefined) {
resolve(translation.arg(this.prevHTML).arg(this.nextHTML)) const [prevHTML, nextHTML] = await Promise.all(this._renderPromises)
else this.prevHTML = this.prevHTML ?? prevHTML
Promise.all(this._renderPromises).then((rendered) => { this.nextHTML = this.nextHTML ?? nextHTML
// Rendered are (potentially) two HTML strings which are defined during rendering }
this.prevHTML = this.prevHTML ?? rendered[0] return translation.arg(this.prevHTML).arg(this.nextHTML)
this.nextHTML = this.prevHTML ?? rendered[1]
resolve(translation.arg(this.prevHTML).arg(this.nextHTML))
})
})
} }
} }

View file

@ -19,9 +19,10 @@
import EditedProperty from "./editproperty.mjs" import EditedProperty from "./editproperty.mjs"
import Objects from "../module/objects.mjs" import Objects from "../module/objects.mjs"
/**
* Action used everytime an object's name has been changed.
*/
export default class NameChanged extends EditedProperty { export default class NameChanged extends EditedProperty {
// Action used everytime an object's property has been changed
type(){return 'NameChanged'} type(){return 'NameChanged'}
icon(){return 'name'} icon(){return 'name'}

View file

@ -21,28 +21,33 @@ import Latex from "../module/latex.mjs"
import * as MathLib from "../math/index.mjs" import * as MathLib from "../math/index.mjs"
import { escapeHTML } from "../utils.mjs" import { escapeHTML } from "../utils.mjs"
import { Action } from "./common.mjs" import { Action } from "./common.mjs"
import { DrawableObject } from "../objs/common.mjs"
/**
* Action used for objects that have a X and Y expression properties (points, texts...)
*/
export default class EditedPosition extends Action { export default class EditedPosition extends Action {
// Action used for objects that have a X and Y expression properties (points, texts...) type() {
type(){return 'EditedPosition'} return "EditedPosition"
}
icon(){return 'position'} icon() {
return "position"
}
color(darkVer=false){ color(darkVer = false) {
return darkVer ? 'seagreen' : 'lightseagreen'; return darkVer ? "seagreen" : "lightseagreen"
} }
constructor(targetName = "", targetType = "Point", previousXValue = "", newXValue = "", previousYValue = "", newYValue = "") { constructor(targetName = "", targetType = "Point", previousXValue = "", newXValue = "", previousYValue = "", newYValue = "") {
super(targetName, targetType) super(targetName, targetType)
let imports = { let imports = {
'previousXValue': previousXValue, "previousXValue": previousXValue,
'previousYValue': previousYValue, "previousYValue": previousYValue,
'newXValue': newXValue, "newXValue": newXValue,
'newYValue': newYValue "newYValue": newYValue
} }
for(let name in imports) for(let name in imports)
this[name] = (typeof imports[name]) == 'string' ? new MathLib.Expression(imports[name]) : imports[name] this[name] = (typeof imports[name]) == "string" ? new MathLib.Expression(imports[name]) : imports[name]
this.setReadableValues() this.setReadableValues()
} }
@ -71,8 +76,8 @@ export default class EditedPosition extends Action {
this.renderLatexAsHtml(nextMarkup) this.renderLatexAsHtml(nextMarkup)
] ]
} else { } else {
this.prevHTML = '<tt style="background: rgba(128,128,128,0.1);">&nbsp;'+escapeHTML(this.prevString)+'&nbsp;</tt>' this.prevHTML = "<tt style=\"background: rgba(128,128,128,0.1);\">&nbsp;" + escapeHTML(this.prevString) + "&nbsp;</tt>"
this.nextHTML = '<tt style="background: rgba(128,128,128,0.1);">&nbsp;'+escapeHTML(this.nextString)+'&nbsp;</tt>' this.nextHTML = "<tt style=\"background: rgba(128,128,128,0.1);\">&nbsp;" + escapeHTML(this.nextString) + "&nbsp;</tt>"
} }
} }
@ -84,26 +89,21 @@ export default class EditedPosition extends Action {
} }
getReadableString() { getReadableString() {
return qsTranslate("position", 'Position of %1 %2 set from "%3" to "%4".') return qsTranslate("position", "Position of %1 %2 set from \"%3\" to \"%4\".")
.arg(Objects.types[this.targetType].displayType()) .arg(Objects.types[this.targetType].displayType())
.arg(this.targetName).arg(this.prevString).arg(this.nextString) .arg(this.targetName).arg(this.prevString).arg(this.nextString)
} }
getHTMLString() { async getHTMLString() {
return new Promise(resolve => { const translation = qsTranslate("position", "Position of %1 set from %2 to %3.")
const translation = qsTranslate("position", 'Position of %1 set from %2 to %3.') .arg("<b style=\"font-size: 15px;\">&nbsp;" + this.targetName + "&nbsp;</b>")
.arg('<b style="font-size: 15px;">&nbsp;' + this.targetName + '&nbsp;</b>')
// Check if we need to wait for LaTeX HTML to be rendered. // Check if we need to wait for LaTeX HTML to be rendered.
if(this.prevHTML !== undefined && this.nextHTML !== undefined) if(this.prevHTML === undefined || this.nextHTML === undefined) {
resolve(translation.arg(this.prevHTML).arg(this.nextHTML)) const [prevHTML, nextHTML] = await Promise.all(this._renderPromises)
else this.prevHTML = this.prevHTML ?? prevHTML
Promise.all(this._renderPromises).then((rendered) => { this.nextHTML = this.nextHTML ?? nextHTML
// Rendered are (potentially) two HTML strings which are defined during rendering }
this.prevHTML = this.prevHTML ?? rendered[0] return translation.arg(this.prevHTML).arg(this.nextHTML)
this.nextHTML = this.nextHTML ?? rendered[1]
resolve(translation.arg(this.prevHTML).arg(this.nextHTML))
})
})
} }
} }

View file

@ -20,16 +20,22 @@ import EditedProperty from "./editproperty.mjs"
import Objects from "../module/objects.mjs" import Objects from "../module/objects.mjs"
/**
* Action used when an object's shown or hidden.
*/
export default class EditedVisibility extends EditedProperty { export default class EditedVisibility extends EditedProperty {
// Action used when an object's shown or hidden. type() {
type(){return 'EditedVisibility'} return "EditedVisibility"
}
icon(){return 'visibility'} icon() {
return "visibility"
}
color(darkVer=false){ color(darkVer = false) {
return this.newValue ? return this.newValue ?
(darkVer ? 'darkgray' : 'whitesmoke') : (darkVer ? "darkgray" : "whitesmoke") :
(darkVer ? 'dimgray' : 'lightgray') (darkVer ? "dimgray" : "lightgray")
} }
constructor(targetName = "", targetType = "Point", newValue = true) { constructor(targetName = "", targetType = "Point", newValue = true) {
@ -41,15 +47,15 @@ export default class EditedVisibility extends EditedProperty {
} }
getReadableString() { getReadableString() {
return (this.newValue ? qsTranslate('visibility', '%1 %2 shown.') : qsTranslate('visibility', '%1 %2 hidden.')) return (this.newValue ? qsTranslate("visibility", "%1 %2 shown.") : qsTranslate("visibility", "%1 %2 hidden."))
.arg(Objects.types[this.targetType].displayType()) .arg(Objects.types[this.targetType].displayType())
.arg(this.targetName) .arg(this.targetName)
} }
getHTMLString() { getHTMLString() {
return (this.newValue ? qsTranslate('visibility', '%1 %2 shown.') : qsTranslate('visibility', '%1 %2 hidden.')) return (this.newValue ? qsTranslate("visibility", "%1 %2 shown.") : qsTranslate("visibility", "%1 %2 hidden."))
.arg(Objects.types[this.targetType].displayType()) .arg(Objects.types[this.targetType].displayType())
.arg('<b style="font-size: 15px;">' + this.targetName + "</b>") .arg("<b style=\"font-size: 15px;\">" + this.targetName + "</b>")
} }
} }

View file

@ -482,7 +482,7 @@ export class ExprEvalExpression {
/** /**
* Calculates the value of the expression by giving all variables and their corresponding values. * Calculates the value of the expression by giving all variables and their corresponding values.
* @param {Object<string, number>} values * @param {Object<string, number|DrawableObject>} values
* @returns {number} * @returns {number}
*/ */
evaluate(values) { evaluate(values) {
@ -512,8 +512,7 @@ export class ExprEvalExpression {
* as constants or functions. * as constants or functions.
* @returns {string[]} * @returns {string[]}
*/ */
variables(options) { variables(options = {}) {
options = options || {}
const vars = [] const vars = []
getSymbols(this.tokens, vars, options) getSymbols(this.tokens, vars, options)
const functions = this.functions const functions = this.functions

View file

@ -0,0 +1,126 @@
/**
* 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/>.
*/
// JS polyfills to add because they're not implemented in the QML Scripting engine.
// CoreJS does not work well with it (as well as doubles the compiled size), so this is preferable.
function notPolyfilled(name) {
return function() { throw new Error(`${name} not polyfilled`) }
}
/**
* @param {number} depth
* @this {Array}
* @returns {Array}
*/
function arrayFlat(depth = 1) {
const newArray = []
for(const element of this) {
if(element instanceof Array)
newArray.push(...(depth > 1 ? element.flat(depth - 1) : element))
else
newArray.push(element)
}
return newArray
}
/**
* @param {function(any, number, Array): any} callbackFn
* @param {object} thisArg
* @this {Array}
* @returns {Array}
*/
function arrayFlatMap(callbackFn, thisArg) {
const newArray = []
for(let i = 0; i < this.length; i++) {
const value = callbackFn.call(thisArg ?? this, this[i], i, this)
if(value instanceof Array)
newArray.push(...value)
else
newArray.push(value)
}
return newArray
}
/**
* Replaces all instances of from by to.
* @param {string} from
* @param {string} to
* @this {string}
* @return {String}
*/
function stringReplaceAll(from, to) {
let str = this
while(str.includes(from))
str = str.replace(from, to)
return str
}
const polyfills = {
2017: [
[Object, "entries", notPolyfilled("Object.entries")],
[Object, "values", notPolyfilled("Object.values")],
[Object, "getOwnPropertyDescriptors", notPolyfilled("Object.getOwnPropertyDescriptors")],
[String.prototype, "padStart", notPolyfilled("String.prototype.padStart")],
[String.prototype, "padEnd", notPolyfilled("String.prototype.padEnd")]
],
2018: [
[Promise.prototype, "finally", notPolyfilled("Object.entries")]
],
2019: [
[String.prototype, "trimStart", notPolyfilled("String.prototype.trimStart")],
[String.prototype, "trimEnd", notPolyfilled("String.prototype.trimEnd")],
[Object, "fromEntries", notPolyfilled("Object.fromEntries")],
[Array.prototype, "flat", arrayFlat],
[Array.prototype, "flatMap", arrayFlatMap]
],
2020: [
[String.prototype, "matchAll", notPolyfilled("String.prototype.matchAll")],
[Promise, "allSettled", notPolyfilled("Promise.allSettled")]
],
2021: [
[Promise, "any", notPolyfilled("Promise.any")],
[String.prototype, "replaceAll", stringReplaceAll]
],
2022: [
[Array.prototype, "at", notPolyfilled("Array.prototype.at")],
[String.prototype, "at", notPolyfilled("String.prototype.at")],
[Object, "hasOwn", notPolyfilled("Object.hasOwn")]
],
2023: [
[Array.prototype, "findLast", notPolyfilled("Array.prototype.findLast")],
[Array.prototype, "toReversed", notPolyfilled("Array.prototype.toReversed")],
[Array.prototype, "toSorted", notPolyfilled("Array.prototype.toSorted")],
[Array.prototype, "toSpliced", notPolyfilled("Array.prototype.toSpliced")],
[Array.prototype, "with", notPolyfilled("Array.prototype.with")]
],
2024: [
[Object, "groupBy", notPolyfilled("Object.groupBy")],
[Map, "groupBy", notPolyfilled("Map.groupBy")]
]
}
// Fulfill polyfill.
for(const [year, entries] of Object.entries(polyfills)) {
const defined = entries.filter(x => x[0][x[1]] !== undefined)
console.info(`ES${year} support: ${defined.length === entries.length} (${defined.length}/${entries.length})`)
// Apply polyfills
for(const [context, functionName, polyfill] of entries.filter(x => x[0][x[1]] === undefined)) {
context[functionName] = polyfill
}
}

View file

@ -18,6 +18,7 @@
// Type polyfills for IDEs. // Type polyfills for IDEs.
// Never directly imported. // Never directly imported.
// Might need to be reimplemented in other implemententations.
Modules = Modules || {} Modules = Modules || {}
/** @type {function(string, string): string} */ /** @type {function(string, string): string} */
@ -25,7 +26,7 @@ qsTranslate = qsTranslate || function(category, string) { throw new Error('qsTra
/** @type {function(string): string} */ /** @type {function(string): string} */
qsTr = qsTr || function(string) { throw new Error('qsTr not implemented.'); } qsTr = qsTr || function(string) { throw new Error('qsTr not implemented.'); }
/** @type {function(string, string): string} */ /** @type {function(string, string): string} */
QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); } QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(category, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); }
/** @type {function(string): string} */ /** @type {function(string): string} */
QT_TR_NOOP = QT_TR_NOOP || function(string) { throw new Error('QT_TR_NOOP not implemented.'); } QT_TR_NOOP = QT_TR_NOOP || function(string) { throw new Error('QT_TR_NOOP not implemented.'); }
/** @type {function(string|boolean|int): string} */ /** @type {function(string|boolean|int): string} */

View file

@ -23,34 +23,43 @@ import { Expression, executeExpression } from "./expression.mjs"
* It doesn't represent any kind of domain and is meant to be extended. * It doesn't represent any kind of domain and is meant to be extended.
*/ */
export class Domain { export class Domain {
constructor() {} constructor() {
}
/** /**
* Checks whether x is included in the domain. * Checks whether x is included in the domain.
* @param {number} x - The x value. * @param {number} x - The x value.
* @return {boolean} true if included, false otherwise. * @return {boolean} true if included, false otherwise.
*/ */
includes(x) { return false } includes(x) {
return false
}
/** /**
* Returns a string representation of the domain. * Returns a string representation of the domain.
* @return {string} String representation of the domain. * @return {string} String representation of the domain.
*/ */
toString() { return '???' } toString() {
return "???"
}
/** /**
* Returns a new domain that is the union between this domain and another. * Returns a new domain that is the union between this domain and another.
* @param {Domain} domain - Domain to unionise with this. * @param {Domain} domain - Domain to unionise with this.
* @return {Domain} newly created domain. * @return {Domain} newly created domain.
*/ */
union(domain) { return domain } union(domain) {
return domain
}
/** /**
* Returns a new domain that is the intersection between this domain and another. * Returns a new domain that is the intersection between this domain and another.
* @param {Domain} domain - Domain to get the interscection with this. * @param {Domain} domain - Domain to get the interscection with this.
* @return {Domain} newly created domain. * @return {Domain} newly created domain.
*/ */
intersection(domain) { return this } intersection(domain) {
return this
}
/** /**
* Imports a domain from a string. * Imports a domain from a string.
@ -61,24 +70,20 @@ export class Domain {
case "R": case "R":
case "": case "":
return Domain.R return Domain.R
break;
case "RE": case "RE":
case "R*": case "R*":
case "*": case "*":
return Domain.RE return Domain.RE
break;
case "RP": case "RP":
case "R+": case "R+":
case "ℝ⁺": case "ℝ⁺":
case "+": case "+":
return Domain.RP return Domain.RP
break;
case "RM": case "RM":
case "R-": case "R-":
case "ℝ⁻": case "ℝ⁻":
case "-": case "-":
return Domain.RM return Domain.RM
break;
case "RPE": case "RPE":
case "REP": case "REP":
case "R+*": case "R+*":
@ -88,7 +93,6 @@ export class Domain {
case "*+": case "*+":
case "+*": case "+*":
return Domain.RPE return Domain.RPE
break;
case "RME": case "RME":
case "REM": case "REM":
case "R-*": case "R-*":
@ -98,7 +102,6 @@ export class Domain {
case "-*": case "-*":
case "*-": case "*-":
return Domain.RME return Domain.RME
break;
case "": case "":
case "N": case "N":
case "ZP": case "ZP":
@ -106,12 +109,10 @@ export class Domain {
case "ℤ⁺": case "ℤ⁺":
case "+": case "+":
return Domain.N return Domain.N
break;
case "NLOG": case "NLOG":
case "ℕˡᵒᵍ": case "ℕˡᵒᵍ":
case "LOG": case "LOG":
return Domain.NLog return Domain.NLog
break;
case "NE": case "NE":
case "NP": case "NP":
case "N*": case "N*":
@ -128,17 +129,14 @@ export class Domain {
case "+*": case "+*":
case "*+": case "*+":
return Domain.NE return Domain.NE
break;
case "Z": case "Z":
case "": case "":
return Domain.Z return Domain.Z
break;
case "ZM": case "ZM":
case "Z-": case "Z-":
case "ℤ⁻": case "ℤ⁻":
case "-": case "-":
return Domain.ZM return Domain.ZM
break;
case "ZME": case "ZME":
case "ZEM": case "ZEM":
case "Z-*": case "Z-*":
@ -148,15 +146,12 @@ export class Domain {
case "-*": case "-*":
case "*-": case "*-":
return Domain.ZME return Domain.ZME
break;
case "ZE": case "ZE":
case "Z*": case "Z*":
case "*": case "*":
return Domain.ZE return Domain.ZE
break;
default: default:
return Domain.EmptySet return Domain.EmptySet
break;
} }
} }
} }
@ -171,15 +166,25 @@ export class EmptySet extends Domain {
this.latexMarkup = "\\emptyset" this.latexMarkup = "\\emptyset"
} }
includes(x) { return false } includes(x) {
return false
}
toString() { return this.displayName } toString() {
return this.displayName
}
union(domain) { return domain } union(domain) {
return domain
}
intersection(domain) { return this } intersection(domain) {
return this
}
static import(frm) { return new EmptySet() } static import(frm) {
return new EmptySet()
}
} }
Domain.EmptySet = new EmptySet() // To prevent use prior to declaration. Domain.EmptySet = new EmptySet() // To prevent use prior to declaration.
@ -190,9 +195,9 @@ Domain.EmptySet = new EmptySet() // To prevent use prior to declaration.
export class Range extends Domain { export class Range extends Domain {
constructor(begin, end, openBegin, openEnd) { constructor(begin, end, openBegin, openEnd) {
super() super()
if(typeof begin == 'number' || typeof begin == 'string') begin = new Expression(begin.toString()) if(typeof begin == "number" || typeof begin == "string") begin = new Expression(begin.toString())
this.begin = begin this.begin = begin
if(typeof end == 'number' || typeof end == 'string') end = new Expression(end.toString()) if(typeof end == "number" || typeof end == "string") end = new Expression(end.toString())
this.end = end this.end = end
this.openBegin = openBegin this.openBegin = openBegin
this.openEnd = openEnd this.openEnd = openEnd
@ -202,7 +207,7 @@ export class Range extends Domain {
includes(x) { includes(x) {
if(x instanceof Expression) x = x.execute() if(x instanceof Expression) x = x.execute()
if(typeof x == 'string') x = executeExpression(x) if(typeof x == "string") x = executeExpression(x)
return ((this.openBegin && x > this.begin.execute()) || (!this.openBegin && x >= this.begin.execute())) && return ((this.openBegin && x > this.begin.execute()) || (!this.openBegin && x >= this.begin.execute())) &&
((this.openEnd && x < this.end.execute()) || (!this.openEnd && x <= this.end.execute())) ((this.openEnd && x < this.end.execute()) || (!this.openEnd && x <= this.end.execute()))
} }
@ -231,8 +236,8 @@ export class Range extends Domain {
static import(frm) { static import(frm) {
let openBegin = frm.trim().charAt(0) === "]" let openBegin = frm.trim().charAt(0) === "]"
let openEnd = frm.trim().charAt(frm.length -1) === "[" let openEnd = frm.trim().charAt(frm.length - 1) === "["
let [begin, end] = frm.substr(1, frm.length-2).split(";") let [begin, end] = frm.substring(1, frm.length - 1).split(";")
return new Range(begin.trim(), end.trim(), openBegin, openEnd) return new Range(begin.trim(), end.trim(), openBegin, openEnd)
} }
} }
@ -261,19 +266,19 @@ export class SpecialDomain extends Domain {
includes(x) { includes(x) {
if(x instanceof Expression) x = x.execute() if(x instanceof Expression) x = x.execute()
if(typeof x == 'string') x = executeExpression(x) if(typeof x == "string") x = executeExpression(x)
return this.isValid(x) return this.isValid(x)
} }
next(x) { next(x) {
if(x instanceof Expression) x = x.execute() if(x instanceof Expression) x = x.execute()
if(typeof x == 'string') x = executeExpression(x) if(typeof x == "string") x = executeExpression(x)
return this.nextValue(x) return this.nextValue(x)
} }
previous(x) { previous(x) {
if(x instanceof Expression) x = x.execute() if(x instanceof Expression) x = x.execute()
if(typeof x == 'string') x = executeExpression(x) if(typeof x == "string") x = executeExpression(x)
return this.prevValue(x) return this.prevValue(x)
} }
@ -305,7 +310,7 @@ export class SpecialDomain extends Domain {
*/ */
export class DomainSet extends SpecialDomain { export class DomainSet extends SpecialDomain {
constructor(values) { constructor(values) {
super('', x => true, x => x, true) super("", () => true, x => x, true)
let newVals = {} let newVals = {}
this.executedValues = [] this.executedValues = []
for(let value of values) { for(let value of values) {
@ -314,7 +319,7 @@ export class DomainSet extends SpecialDomain {
newVals[ex] = expr newVals[ex] = expr
this.executedValues.push(ex) this.executedValues.push(ex)
} }
this.executedValues.sort((a,b) => a-b) this.executedValues.sort((a, b) => a - b)
this.values = this.executedValues.map(val => newVals[val]) this.values = this.executedValues.map(val => newVals[val])
this.displayName = "{" + this.values.join(";") + "}" this.displayName = "{" + this.values.join(";") + "}"
this.latexMarkup = `\\{${this.values.join(";")}\\}` this.latexMarkup = `\\{${this.values.join(";")}\\}`
@ -322,7 +327,7 @@ export class DomainSet extends SpecialDomain {
includes(x) { includes(x) {
if(x instanceof Expression) x = x.execute() if(x instanceof Expression) x = x.execute()
if(typeof x == 'string') x = executeExpression(x) if(typeof x == "string") x = executeExpression(x)
for(let value of this.values) for(let value of this.values)
if(x === value.execute()) return true if(x === value.execute()) return true
return false return false
@ -330,10 +335,10 @@ export class DomainSet extends SpecialDomain {
next(x) { next(x) {
if(x instanceof Expression) x = x.execute() if(x instanceof Expression) x = x.execute()
if(typeof x == 'string') x = executeExpression(x) if(typeof x == "string") x = executeExpression(x)
if(x < this.executedValues[0]) return this.executedValues[0] if(x < this.executedValues[0]) return this.executedValues[0]
for(let i = 1; i < this.values.length; i++) { for(let i = 1; i < this.values.length; i++) {
let prevValue = this.executedValues[i-1] let prevValue = this.executedValues[i - 1]
let value = this.executedValues[i] let value = this.executedValues[i]
if(x >= prevValue && x < value) return value if(x >= prevValue && x < value) return value
} }
@ -342,11 +347,11 @@ export class DomainSet extends SpecialDomain {
previous(x) { previous(x) {
if(x instanceof Expression) x = x.execute() if(x instanceof Expression) x = x.execute()
if(typeof x == 'string') x = executeExpression(x) if(typeof x == "string") x = executeExpression(x)
if(x > this.executedValues[this.executedValues.length-1]) if(x > this.executedValues[this.executedValues.length - 1])
return this.executedValues[this.executedValues.length-1] return this.executedValues[this.executedValues.length - 1]
for(let i = 1; i < this.values.length; i++) { for(let i = 1; i < this.values.length; i++) {
let prevValue = this.executedValues[i-1] let prevValue = this.executedValues[i - 1]
let value = this.executedValues[i] let value = this.executedValues[i]
if(x > prevValue && x <= value) return prevValue if(x > prevValue && x <= value) return prevValue
} }
@ -361,7 +366,7 @@ export class DomainSet extends SpecialDomain {
if(domain instanceof EmptySet) return this if(domain instanceof EmptySet) return this
if(domain instanceof DomainSet) { if(domain instanceof DomainSet) {
let newValues = [] let newValues = []
let values = this.values.concat(domain.values).filter(function(val){ let values = this.values.concat(domain.values).filter(function(val) {
newValues.push(val.execute()) newValues.push(val.execute())
return newValues.indexOf(val.execute()) === newValues.length - 1 return newValues.indexOf(val.execute()) === newValues.length - 1
}) })
@ -389,7 +394,7 @@ export class DomainSet extends SpecialDomain {
if(domain instanceof EmptySet) return domain if(domain instanceof EmptySet) return domain
if(domain instanceof DomainSet) { if(domain instanceof DomainSet) {
let domValues = domain.values.map(expr => expr.execute()) let domValues = domain.values.map(expr => expr.execute())
this.values = this.values.filter(function(val){ this.values = this.values.filter(function(val) {
return domValues.indexOf(val.execute()) >= 0 return domValues.indexOf(val.execute()) >= 0
}) })
return this return this
@ -414,7 +419,7 @@ export class DomainSet extends SpecialDomain {
} }
static import(frm) { static import(frm) {
return new DomainSet(frm.substr(1, frm.length-2).split(";")) return new DomainSet(frm.substring(1, frm.length - 1).split(";"))
} }
} }
@ -459,7 +464,7 @@ export class UnionDomain extends Domain {
let domains = frm.trim().split("") let domains = frm.trim().split("")
if(domains.length === 1) domains = frm.trim().split("U") // Fallback if(domains.length === 1) domains = frm.trim().split("U") // Fallback
let dom2 = parseDomain(domains.pop()) let dom2 = parseDomain(domains.pop())
let dom1 = parseDomain(domains.join('')) let dom1 = parseDomain(domains.join(""))
return dom1.union(dom2) return dom1.union(dom2)
} }
} }
@ -504,7 +509,7 @@ export class IntersectionDomain extends Domain {
static import(frm) { static import(frm) {
let domains = frm.trim().split("∩") let domains = frm.trim().split("∩")
let dom1 = parseDomain(domains.pop()) let dom1 = parseDomain(domains.pop())
let dom2 = parseDomain(domains.join('∩')) let dom2 = parseDomain(domains.join("∩"))
return dom1.intersection(dom2) return dom1.intersection(dom2)
} }
} }
@ -533,7 +538,7 @@ export class MinusDomain extends Domain {
let domains = frm.trim().split("") let domains = frm.trim().split("")
if(domains.length === 1) domains = frm.trim().split("\\") // Fallback if(domains.length === 1) domains = frm.trim().split("\\") // Fallback
let dom1 = parseDomain(domains.shift()) let dom1 = parseDomain(domains.shift())
let dom2 = parseDomain(domains.join('')) let dom2 = parseDomain(domains.join(""))
return new MinusDomain(dom1, dom2) return new MinusDomain(dom1, dom2)
} }
} }
@ -542,52 +547,52 @@ Domain.RE = new MinusDomain("R", "{0}")
Domain.RE.displayName = "*" Domain.RE.displayName = "*"
Domain.RE.latexMarkup = "\\mathbb{R}^{*}" Domain.RE.latexMarkup = "\\mathbb{R}^{*}"
Domain.R = new Range(-Infinity,Infinity,true,true) Domain.R = new Range(-Infinity, Infinity, true, true)
Domain.R.displayName = "" Domain.R.displayName = ""
Domain.R.latexMarkup = "\\mathbb{R}" Domain.R.latexMarkup = "\\mathbb{R}"
Domain.RP = new Range(0,Infinity,true,false) Domain.RP = new Range(0, Infinity, true, false)
Domain.RP.displayName = "ℝ⁺" Domain.RP.displayName = "ℝ⁺"
Domain.RP.latexMarkup = "\\mathbb{R}^{+}" Domain.RP.latexMarkup = "\\mathbb{R}^{+}"
Domain.RM = new Range(-Infinity,0,true,false) Domain.RM = new Range(-Infinity, 0, true, false)
Domain.RM.displayName = "ℝ⁻" Domain.RM.displayName = "ℝ⁻"
Domain.RM.latexMarkup = "\\mathbb{R}^{-}" Domain.RM.latexMarkup = "\\mathbb{R}^{-}"
Domain.RPE = new Range(0,Infinity,true,true) Domain.RPE = new Range(0, Infinity, true, true)
Domain.RPE.displayName = "ℝ⁺*" Domain.RPE.displayName = "ℝ⁺*"
Domain.RPE.latexMarkup = "\\mathbb{R}^{+*}" Domain.RPE.latexMarkup = "\\mathbb{R}^{+*}"
Domain.RME = new Range(-Infinity,0,true,true) Domain.RME = new Range(-Infinity, 0, true, true)
Domain.RME.displayName = "ℝ⁻*" Domain.RME.displayName = "ℝ⁻*"
Domain.RME.latexMarkup = "\\mathbb{R}^{+*}" Domain.RME.latexMarkup = "\\mathbb{R}^{+*}"
Domain.N = new SpecialDomain('', x => x%1===0 && x >= 0, Domain.N = new SpecialDomain("", x => x % 1 === 0 && x >= 0,
x => Math.max(Math.floor(x)+1, 0), x => Math.max(Math.floor(x) + 1, 0),
x => Math.max(Math.ceil(x)-1, 0)) x => Math.max(Math.ceil(x) - 1, 0))
Domain.N.latexMarkup = "\\mathbb{N}" Domain.N.latexMarkup = "\\mathbb{N}"
Domain.NE = new SpecialDomain('*', x => x%1===0 && x > 0, Domain.NE = new SpecialDomain("*", x => x % 1 === 0 && x > 0,
x => Math.max(Math.floor(x)+1, 1), x => Math.max(Math.floor(x) + 1, 1),
x => Math.max(Math.ceil(x)-1, 1)) x => Math.max(Math.ceil(x) - 1, 1))
Domain.NE.latexMarkup = "\\mathbb{N}^{*}" Domain.NE.latexMarkup = "\\mathbb{N}^{*}"
Domain.Z = new SpecialDomain('', x => x%1===0, x => Math.floor(x)+1, x => Math.ceil(x)-1) Domain.Z = new SpecialDomain("", x => x % 1 === 0, x => Math.floor(x) + 1, x => Math.ceil(x) - 1)
Domain.Z.latexMarkup = "\\mathbb{Z}" Domain.Z.latexMarkup = "\\mathbb{Z}"
Domain.ZE = new SpecialDomain('*', x => x%1===0 && x !== 0, Domain.ZE = new SpecialDomain("*", x => x % 1 === 0 && x !== 0,
x => Math.floor(x)+1 === 0 ? Math.floor(x)+2 : Math.floor(x)+1, x => Math.floor(x) + 1 === 0 ? Math.floor(x) + 2 : Math.floor(x) + 1,
x => Math.ceil(x)-1 === 0 ? Math.ceil(x)-2 : Math.ceil(x)-1) x => Math.ceil(x) - 1 === 0 ? Math.ceil(x) - 2 : Math.ceil(x) - 1)
Domain.ZE.latexMarkup = "\\mathbb{Z}^{*}" Domain.ZE.latexMarkup = "\\mathbb{Z}^{*}"
Domain.ZM = new SpecialDomain('ℤ⁻', x => x%1===0 && x <= 0, Domain.ZM = new SpecialDomain("ℤ⁻", x => x % 1 === 0 && x <= 0,
x => Math.min(Math.floor(x)+1, 0), x => Math.min(Math.floor(x) + 1, 0),
x => Math.min(Math.ceil(x)-1, 0)) x => Math.min(Math.ceil(x) - 1, 0))
Domain.ZM.latexMarkup = "\\mathbb{Z}^{-}" Domain.ZM.latexMarkup = "\\mathbb{Z}^{-}"
Domain.ZME = new SpecialDomain('ℤ⁻*', x => x%1===0 && x < 0, Domain.ZME = new SpecialDomain("ℤ⁻*", x => x % 1 === 0 && x < 0,
x => Math.min(Math.floor(x)+1, -1), x => Math.min(Math.floor(x) + 1, -1),
x => Math.min(Math.ceil(x)-1, -1)) x => Math.min(Math.ceil(x) - 1, -1))
Domain.ZME.latexMarkup = "\\mathbb{Z}^{-*}" Domain.ZME.latexMarkup = "\\mathbb{Z}^{-*}"
Domain.NLog = new SpecialDomain('ℕˡᵒᵍ', Domain.NLog = new SpecialDomain("ℕˡᵒᵍ",
x => x/Math.pow(10, x.toString().length-1) % 1 === 0 && x > 0, x => x / Math.pow(10, x.toString().length - 1) % 1 === 0 && x > 0,
function(x) { function(x) {
let x10pow = Math.pow(10, x.toString().length-1) let x10pow = Math.pow(10, x.toString().length - 1)
return Math.max(1, (Math.floor(x/x10pow)+1)*x10pow) return Math.max(1, (Math.floor(x / x10pow) + 1) * x10pow)
}, },
function(x) { function(x) {
let x10pow = Math.pow(10, x.toString().length-1) let x10pow = Math.pow(10, x.toString().length - 1)
return Math.max(1, (Math.ceil(x/x10pow)-1)*x10pow) return Math.max(1, (Math.ceil(x / x10pow) - 1) * x10pow)
}) })
Domain.NLog.latexMarkup = "\\mathbb{N}^{log}" Domain.NLog.latexMarkup = "\\mathbb{N}^{log}"
@ -600,11 +605,11 @@ let refedDomains = []
* @returns {Domain} Parsed domain. * @returns {Domain} Parsed domain.
*/ */
export function parseDomain(domain) { export function parseDomain(domain) {
if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain) if(!domain.includes(")") && !domain.includes("(")) return parseDomainSimple(domain)
let domStr let domStr
while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) { while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) {
let dom = parseDomainSimple(domStr[1].trim()); let dom = parseDomainSimple(domStr[1].trim())
domain = domain.replace(domStr[0], 'D' + refedDomains.length) domain = domain.replace(domStr[0], "D" + refedDomains.length)
refedDomains.push(dom) refedDomains.push(dom)
} }
return parseDomainSimple(domain) return parseDomainSimple(domain)
@ -621,10 +626,10 @@ export function parseDomainSimple(domain) {
if(domain.includes("U") || domain.includes("")) return UnionDomain.import(domain) if(domain.includes("U") || domain.includes("")) return UnionDomain.import(domain)
if(domain.includes("∩")) return IntersectionDomain.import(domain) if(domain.includes("∩")) return IntersectionDomain.import(domain)
if(domain.includes("") || domain.includes("\\")) return MinusDomain.import(domain) if(domain.includes("") || domain.includes("\\")) return MinusDomain.import(domain)
if(domain.charAt(0) === "{" && domain.charAt(domain.length -1) === "}") return DomainSet.import(domain) if(domain.charAt(0) === "{" && domain.charAt(domain.length - 1) === "}") return DomainSet.import(domain)
if(domain.includes("]") || domain.includes("[")) return Range.import(domain) if(domain.includes("]") || domain.includes("[")) return Range.import(domain)
if(["R", "", "N", "", "Z", ""].some(str => domain.toUpperCase().includes(str))) if(["R", "", "N", "", "Z", ""].some(str => domain.toUpperCase().includes(str)))
return Domain.import(domain) return Domain.import(domain)
if(domain[0] === 'D') return refedDomains[parseInt(domain.substr(1))] if(domain[0] === "D") return refedDomains[parseInt(domain.substring(1))]
return new EmptySet() return new EmptySet()
} }

View file

@ -37,28 +37,48 @@ export class Expression {
} }
this.cached = this.isConstant() this.cached = this.isConstant()
this.cachedValue = null this.cachedValue = null
if(this.cached && this.allRequirementsFullfilled()) if(this.cached && this.allRequirementsFulfilled())
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName) this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
this.latexMarkup = Latex.expression(this.calc.tokens) this.latexMarkup = Latex.expression(this.calc.tokens)
} }
/**
* Return all the variables used in calc
* @return {string[]}
*/
variables() { variables() {
return this.calc.variables() return this.calc.variables()
} }
/**
* Checks if the current expression is constant (does not depend on a variable, be it x or n).
* @return {boolean}
*/
isConstant() { isConstant() {
let vars = this.calc.variables() let vars = this.calc.variables()
return !vars.includes("x") && !vars.includes("n") return !vars.includes("x") && !vars.includes("n")
} }
/**
* Returns the list of object names this expression is dependant on.
* @return {string[]}
*/
requiredObjects() { requiredObjects() {
return this.calc.variables().filter(objName => objName !== "x" && objName !== "n") return this.calc.variables().filter(objName => objName !== "x" && objName !== "n")
} }
allRequirementsFullfilled() { /**
* Checks if all the objects required for this expression are defined.
* @return {boolean}
*/
allRequirementsFulfilled() {
return this.requiredObjects().every(objName => objName in Objects.currentObjectsByName) return this.requiredObjects().every(objName => objName in Objects.currentObjectsByName)
} }
/**
* Returns a list of names whose corresponding objects this expression is dependant on and are missing.
* @return {boolean}
*/
undefinedVariables() { undefinedVariables() {
return this.requiredObjects().filter(objName => !(objName in Objects.currentObjectsByName)) return this.requiredObjects().filter(objName => !(objName in Objects.currentObjectsByName))
} }
@ -74,13 +94,13 @@ export class Expression {
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName) this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
return this.cachedValue return this.cachedValue
} }
ExprParser.currentVars = Object.assign({'x': x}, Objects.currentObjectsByName) ExprParser.currentVars = Object.assign({ "x": x }, Objects.currentObjectsByName)
return this.calc.evaluate(ExprParser.currentVars) return this.calc.evaluate(ExprParser.currentVars)
} }
simplify(x) { simplify(x) {
let expr = this.calc.substitute('x', x).simplify() let expr = this.calc.substitute("x", x).simplify()
if(expr.evaluate() === 0) expr = '0' if(expr.evaluate() === 0) expr = "0"
return new Expression(expr) return new Expression(expr)
} }
@ -88,19 +108,19 @@ export class Expression {
return this.calc.toString() return this.calc.toString()
} }
toString(forceSign=false) { toString(forceSign = false) {
let str = Utils.makeExpressionReadable(this.calc.toString()) let str = Utils.makeExpressionReadable(this.calc.toString())
if(str !== undefined && str.match(/^\d*\.\d+$/)) { if(str !== undefined && str.match(/^\d*\.\d+$/)) {
if(str.split('.')[1].split('0').length > 7) { if(str.split(".")[1].split("0").length > 7) {
// Likely rounding error // Likely rounding error
str = parseFloat(str.substring(0, str.length-1)).toString(); str = parseFloat(str.substring(0, str.length - 1)).toString()
} }
} }
if(str[0] !== '-' && forceSign) str = '+' + str if(str[0] !== "-" && forceSign) str = "+" + str
return str return str
} }
} }
export function executeExpression(expr){ export function executeExpression(expr) {
return (new Expression(expr.toString())).execute() return (new Expression(expr.toString())).execute()
} }

View file

@ -17,7 +17,7 @@
*/ */
import { Module } from "./common.mjs" import { Module } from "./common.mjs"
import { FUNCTION, Interface, CanvasInterface, DialogInterface } from "./interface.mjs" import { 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"
@ -227,7 +227,7 @@ class CanvasAPI extends Module {
// Drawing throws an error. Generally, it's due to a new modification (or the opening of a file) // Drawing throws an error. Generally, it's due to a new modification (or the opening of a file)
console.error(e) console.error(e)
console.log(e.stack) console.log(e.stack)
this._drawingErrorDialog.showDialog(objType, obj.name, e.message) this._drawingErrorDialog.show(objType, obj.name, e.message)
History.undo() History.undo()
} }
} }

View file

@ -26,7 +26,7 @@ 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 {Object.<string, (Interface|string|number|boolean)>} initializationParameters - List of parameters for the initialize function.
*/ */
constructor(name, initializationParameters = {}) { constructor(name, initializationParameters = {}) {
console.log(`Loading module ${name}...`) console.log(`Loading module ${name}...`)

View file

@ -55,7 +55,7 @@ class ExprParserAPI extends Module {
* @return {function} JS function to call. * @return {function} JS function to call.
*/ */
parseArgumentsForFunction(args, usage1, usage2) { parseArgumentsForFunction(args, usage1, usage2) {
let f, target, variable let f, variable
if(args.length === 1) { if(args.length === 1) {
// Parse object // Parse object
f = args[0] f = args[0]

View file

@ -17,7 +17,6 @@
*/ */
import { Module } from "./common.mjs" import { Module } from "./common.mjs"
import Latex from "./latex.mjs"
import { HistoryInterface, NUMBER, STRING } from "./interface.mjs" import { HistoryInterface, NUMBER, STRING } from "./interface.mjs"

View file

@ -69,65 +69,54 @@ export class Interface {
export class SettingsInterface extends Interface { export class SettingsInterface extends Interface {
constructor() { width = NUMBER
super() height = NUMBER
this.width = NUMBER xmin = NUMBER
this.height = NUMBER ymax = NUMBER
this.xmin = NUMBER xzoom = NUMBER
this.ymax = NUMBER yzoom = NUMBER
this.xzoom = NUMBER xaxisstep = STRING
this.yzoom = NUMBER yaxisstep = STRING
this.xaxisstep = STRING xlabel = STRING
this.yaxisstep = STRING ylabel = STRING
this.xlabel = STRING linewidth = NUMBER
this.ylabel = STRING textsize = NUMBER
this.linewidth = NUMBER logscalex = BOOLEAN
this.textsize = NUMBER showxgrad = BOOLEAN
this.logscalex = BOOLEAN showygrad = BOOLEAN
this.showxgrad = BOOLEAN
this.showygrad = BOOLEAN
}
} }
export class CanvasInterface extends SettingsInterface { export class CanvasInterface extends SettingsInterface {
constructor() { imageLoaders = OBJECT
super()
this.imageLoaders = OBJECT
/** @type {function(string): CanvasRenderingContext2D} */ /** @type {function(string): CanvasRenderingContext2D} */
this.getContext = FUNCTION getContext = FUNCTION
/** @type {function(rect)} */ /** @type {function(rect)} */
this.markDirty = FUNCTION markDirty = FUNCTION
/** @type {function(string)} */ /** @type {function(string)} */
this.loadImage = FUNCTION loadImage = FUNCTION
/** @type {function(string)} */
isImageLoading = FUNCTION
/** @type {function(string)} */
isImageLoaded = FUNCTION
/** @type {function()} */ /** @type {function()} */
this.requestPaint = FUNCTION requestPaint = FUNCTION
}
} }
export class RootInterface extends Interface { export class RootInterface extends Interface {
constructor() { width = NUMBER
super() height = NUMBER
this.width = NUMBER updateObjectsLists = FUNCTION
this.height = NUMBER
this.updateObjectsLists = FUNCTION
}
} }
export class DialogInterface extends Interface { export class DialogInterface extends Interface {
constructor() { show = FUNCTION
super()
this.show = FUNCTION
}
} }
export class HistoryInterface extends Interface { export class HistoryInterface extends Interface {
constructor() { undo = FUNCTION
super() redo = FUNCTION
this.undo = FUNCTION clear = FUNCTION
this.redo = FUNCTION addToHistory = FUNCTION
this.clear = FUNCTION unserialize = FUNCTION
this.addToHistory = FUNCTION serialize = FUNCTION
this.unserialize = FUNCTION
this.serialize = FUNCTION
}
} }

View file

@ -20,7 +20,7 @@ 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" import { DialogInterface, RootInterface, SettingsInterface } from "./interface.mjs"
class IOAPI extends Module { class IOAPI extends Module {
@ -97,7 +97,7 @@ class IOAPI extends Module {
* Loads the diagram from a certain \c filename. * Loads the diagram from a certain \c filename.
* @param {string} filename * @param {string} filename
*/ */
loadDiagram(filename) { async loadDiagram(filename) {
if(!this.initialized) throw new Error("Attempting loadDiagram before initialize!") if(!this.initialized) throw new Error("Attempting loadDiagram before initialize!")
if(!History.initialized) throw new Error("Attempting loadDiagram before history is initialized!") if(!History.initialized) throw new Error("Attempting loadDiagram before history is initialized!")
let basename = filename.split("/").pop() let basename = filename.split("/").pop()

View file

@ -20,7 +20,8 @@ import { Module } from "./common.mjs"
import * as Instruction from "../lib/expr-eval/instruction.mjs" import * as Instruction from "../lib/expr-eval/instruction.mjs"
import { escapeValue } from "../lib/expr-eval/expression.mjs" import { escapeValue } from "../lib/expr-eval/expression.mjs"
const unicodechars = ["α", "β", "γ", "δ", "ε", "ζ", "η", const unicodechars = [
"α", "β", "γ", "δ", "ε", "ζ", "η",
"π", "θ", "κ", "λ", "μ", "ξ", "ρ", "π", "θ", "κ", "λ", "μ", "ξ", "ρ",
"ς", "σ", "τ", "φ", "χ", "ψ", "ω", "ς", "σ", "τ", "φ", "χ", "ψ", "ω",
"Γ", "Δ", "Θ", "Λ", "Ξ", "Π", "Σ", "Γ", "Δ", "Θ", "Λ", "Ξ", "Π", "Σ",
@ -30,7 +31,8 @@ const unicodechars = ["α", "β", "γ", "δ", "ε", "ζ", "η",
"⁷", "⁸", "⁹", "⁰", "₁", "₂", "₃", "⁷", "⁸", "⁹", "⁰", "₁", "₂", "₃",
"₄", "₅", "₆", "₇", "₈", "₉", "₀", "₄", "₅", "₆", "₇", "₈", "₉", "₀",
"pi", "∞"] "pi", "∞"]
const equivalchars = ["\\alpha", "\\beta", "\\gamma", "\\delta", "\\epsilon", "\\zeta", "\\eta", const equivalchars = [
"\\alpha", "\\beta", "\\gamma", "\\delta", "\\epsilon", "\\zeta", "\\eta",
"\\pi", "\\theta", "\\kappa", "\\lambda", "\\mu", "\\xi", "\\rho", "\\pi", "\\theta", "\\kappa", "\\lambda", "\\mu", "\\xi", "\\rho",
"\\sigma", "\\sigma", "\\tau", "\\phi", "\\chi", "\\psi", "\\omega", "\\sigma", "\\sigma", "\\tau", "\\phi", "\\chi", "\\psi", "\\omega",
"\\Gamma", "\\Delta", "\\Theta", "\\Lambda", "\\Xi", "\\Pi", "\\Sigma", "\\Gamma", "\\Delta", "\\Theta", "\\Lambda", "\\Xi", "\\Pi", "\\Sigma",
@ -65,26 +67,13 @@ class LatexAPI extends Module {
this.enabled = Helper.getSettingBool("enable_latex") this.enabled = Helper.getSettingBool("enable_latex")
} }
/**
* Prepares and renders a latex string into a png file.
*
* @param {string} markup - LaTeX markup to render.
* @param {number} fontSize - Font size (in pt) to render.
* @param {color} color - Color of the text to render.
* @returns {LatexRenderResult}
*/
renderSync(markup, fontSize, color) {
let args = Latex.render(markup, fontSize, color).split(",")
return new LatexRenderResult(...args)
}
/** /**
* Checks if the given markup (with given font size and color) has already been * Checks if the given markup (with given font size and color) has already been
* rendered, and if so, returns its data. Otherwise, returns null. * rendered, and if so, returns its data. Otherwise, returns null.
* *
* @param {string} markup - LaTeX markup to render. * @param {string} markup - LaTeX markup to render.
* @param {number} fontSize - Font size (in pt) to render. * @param {number} fontSize - Font size (in pt) to render.
* @param {color} color - Color of the text to render. * @param {string} color - Color of the text to render.
* @returns {LatexRenderResult|null} * @returns {LatexRenderResult|null}
*/ */
findPrerendered(markup, fontSize, color) { findPrerendered(markup, fontSize, color) {
@ -100,13 +89,12 @@ class LatexAPI extends Module {
* *
* @param {string} markup - LaTeX markup to render. * @param {string} markup - LaTeX markup to render.
* @param {number} fontSize - Font size (in pt) to render. * @param {number} fontSize - Font size (in pt) to render.
* @param {color} color - Color of the text to render. * @param {string} color - Color of the text to render.
* @returns {Promise<LatexRenderResult>} * @returns {Promise<LatexRenderResult>}
*/ */
requestAsyncRender(markup, fontSize, color) { async requestAsyncRender(markup, fontSize, color) {
return new Promise(resolve => { let args = Latex.render(markup, fontSize, color).split(",")
resolve(this.renderSync(markup, fontSize, color)) return new LatexRenderResult(...args)
})
} }
/** /**
@ -133,7 +121,7 @@ class LatexAPI extends Module {
if(elem[0] !== "(" && elem[elem.length - 1] !== ")" && contents.some(x => elem.indexOf(x) > 0)) if(elem[0] !== "(" && elem[elem.length - 1] !== ")" && contents.some(x => elem.indexOf(x) > 0))
return this.par(elem) return this.par(elem)
if(elem[0] === "(" && elem[elem.length - 1] === ")") if(elem[0] === "(" && elem[elem.length - 1] === ")")
return elem.substr(1, elem.length - 2) return elem.removeEnclosure()
return elem return elem
} }
@ -148,24 +136,24 @@ class LatexAPI extends Module {
switch(f) { switch(f) {
case "derivative": case "derivative":
if(args.length === 3) if(args.length === 3)
return "\\frac{d" + args[0].substr(1, args[0].length - 2).replace(new RegExp(args[1].substr(1, args[1].length - 2), "g"), "x") + "}{dx}" return `\\frac{d${args[0].removeEnclosure().replaceAll(args[1].removeEnclosure(), "x")}}{dx}`
else else
return "\\frac{d" + args[0] + "}{dx}(x)" return `\\frac{d${args[0]}}{dx}(x)`
case "integral": case "integral":
if(args.length === 4) if(args.length === 4)
return "\\int\\limits_{" + args[0] + "}^{" + args[1] + "}" + args[2].substr(1, args[2].length - 2) + " d" + args[3].substr(1, args[3].length - 2) return `\\int\\limits_{${args[0]}}^{${args[1]}}${args[2].removeEnclosure()} d${args[3].removeEnclosure()}`
else else
return "\\int\\limits_{" + args[0] + "}^{" + args[1] + "}" + args[2] + "(t) dt" return `\\int\\limits_{${args[0]}}^{${args[1]}}${args[2]}(t) dt`
case "sqrt": case "sqrt":
return "\\sqrt\\left(" + args.join(", ") + "\\right)" return `\\sqrt\\left(${args.join(", ")}\\right)`
case "abs": case "abs":
return "\\left|" + args.join(", ") + "\\right|" return `\\left|${args.join(", ")}\\right|`
case "floor": case "floor":
return "\\left\\lfloor" + args.join(", ") + "\\right\\rfloor" return `\\left\\lfloor${args.join(", ")}\\right\\rfloor`
case "ceil": case "ceil":
return "\\left\\lceil" + args.join(", ") + "\\right\\rceil" return `\\left\\lceil${args.join(", ")}\\right\\rceil`
default: default:
return "\\mathrm{" + f + "}\\left(" + args.join(", ") + "\\right)" return `\\mathrm{${f}}\\left(${args.join(", ")}\\right)`
} }
} }
@ -180,12 +168,12 @@ class LatexAPI extends Module {
if(wrapIn$) if(wrapIn$)
for(let i = 0; i < unicodechars.length; i++) { for(let i = 0; i < unicodechars.length; i++) {
if(vari.includes(unicodechars[i])) if(vari.includes(unicodechars[i]))
vari = vari.replace(new RegExp(unicodechars[i], "g"), "$" + equivalchars[i] + "$") vari = vari.replaceAll(unicodechars[i], "$" + equivalchars[i] + "$")
} }
else else
for(let i = 0; i < unicodechars.length; i++) { for(let i = 0; i < unicodechars.length; i++) {
if(vari.includes(unicodechars[i])) if(vari.includes(unicodechars[i]))
vari = vari.replace(new RegExp(unicodechars[i], "g"), equivalchars[i]) vari = vari.replaceAll(unicodechars[i], equivalchars[i])
} }
return vari return vari
} }

View file

@ -16,13 +16,13 @@
* 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 { Module } from './common.mjs' import { Module } from "./common.mjs"
import { textsub } from '../utils.mjs' import { textsub } from "../utils.mjs"
class ObjectsAPI extends Module { class ObjectsAPI extends Module {
constructor() { constructor() {
super('Objects') super("Objects")
this.types = {} this.types = {}
/** /**
@ -45,7 +45,7 @@ class ObjectsAPI extends Module {
* @param {string} prefix - Prefix to the name. * @param {string} prefix - Prefix to the name.
* @return {string} New unused name for a new object. * @return {string} New unused name for a new object.
*/ */
getNewName(allowedLetters, prefix='') { getNewName(allowedLetters, prefix = "") {
// Allows to get a new name, based on the allowed letters, // Allows to get a new name, based on the allowed letters,
// as well as adding a sub number when needs be. // as well as adding a sub number when needs be.
let newid = 0 let newid = 0
@ -53,7 +53,7 @@ class ObjectsAPI extends Module {
do { do {
let letter = allowedLetters[newid % allowedLetters.length] let letter = allowedLetters[newid % allowedLetters.length]
let num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length) let num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length)
ret = prefix + letter + (num > 0 ? textsub(num-1) : '') ret = prefix + letter + (num > 0 ? textsub(num - 1) : "")
newid += 1 newid += 1
} while(ret in this.currentObjectsByName) } while(ret in this.currentObjectsByName)
return ret return ret
@ -78,7 +78,7 @@ class ObjectsAPI extends Module {
deleteObject(objName) { deleteObject(objName) {
let obj = this.currentObjectsByName[objName] let obj = this.currentObjectsByName[objName]
if(obj !== undefined) { if(obj !== undefined) {
this.currentObjects[obj.type].splice(this.currentObjects[obj.type].indexOf(obj),1) this.currentObjects[obj.type].splice(this.currentObjects[obj.type].indexOf(obj), 1)
obj.delete() obj.delete()
delete this.currentObjectsByName[objName] delete this.currentObjectsByName[objName]
} }
@ -90,15 +90,10 @@ class ObjectsAPI extends Module {
* @returns {string[]} List of names of the objects. * @returns {string[]} List of names of the objects.
*/ */
getObjectsName(objType) { getObjectsName(objType) {
if(objType === "ExecutableObject") { if(objType === "ExecutableObject")
// NOTE: QMLJS does not support flatMap. return this.getExecutableTypes().flatMap(
// return getExecutableTypes().flatMap(elemType => currentObjects[elemType].map(obj => obj.name)) elemType => this.currentObjects[elemType].map(obj => obj.name)
let types = this.getExecutableTypes() )
let elementNames = ['']
for(let elemType of types)
elementNames = elementNames.concat(this.currentObjects[elemType].map(obj => obj.name))
return elementNames
}
if(this.currentObjects[objType] === undefined) return [] if(this.currentObjects[objType] === undefined) return []
return this.currentObjects[objType].map(obj => obj.name) return this.currentObjects[objType].map(obj => obj.name)
} }
@ -117,9 +112,9 @@ class ObjectsAPI extends Module {
* @param {string[]} args - List of arguments for the objects (can be empty). * @param {string[]} args - List of arguments for the objects (can be empty).
* @return {DrawableObject<objType>} Newly created object. * @return {DrawableObject<objType>} Newly created object.
*/ */
createNewRegisteredObject(objType, args= []) { createNewRegisteredObject(objType, args = []) {
if(Object.keys(this.types).indexOf(objType) === -1) return null // Object type does not exist. if(Object.keys(this.types).indexOf(objType) === -1) return null // Object type does not exist.
let newobj = new this.types[objType](...args) const newobj = new this.types[objType](...args)
if(Object.keys(this.currentObjects).indexOf(objType) === -1) { if(Object.keys(this.currentObjects).indexOf(objType) === -1) {
this.currentObjects[objType] = [] this.currentObjects[objType] = []
} }

View file

@ -15,19 +15,19 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* 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 {Module} from "./common.mjs" import { Module } from "./common.mjs"
import General from "../preferences/general.mjs" import General from "../preferences/general.mjs"
import Editor from "../preferences/expression.mjs" import Editor from "../preferences/expression.mjs"
import DefaultGraph from "../preferences/default.mjs" import DefaultGraph from "../preferences/default.mjs"
class PreferencesAPI extends Module { class PreferencesAPI extends Module {
constructor() { constructor() {
super('Preferences') super("Preferences")
this.categories = { this.categories = {
[QT_TRANSLATE_NOOP('settingCategory', 'general')]: General, [QT_TRANSLATE_NOOP("settingCategory", "general")]: General,
[QT_TRANSLATE_NOOP('settingCategory', 'editor')]: Editor, [QT_TRANSLATE_NOOP("settingCategory", "editor")]: Editor,
[QT_TRANSLATE_NOOP('settingCategory', 'default')]: DefaultGraph, [QT_TRANSLATE_NOOP("settingCategory", "default")]: DefaultGraph
} }
} }
} }

View file

@ -27,39 +27,50 @@ import { ExecutableObject } from "./common.mjs"
import Function from "./function.mjs" import Function from "./function.mjs"
export default class BodeMagnitude extends ExecutableObject { export default class BodeMagnitude extends ExecutableObject {
static type(){return 'Gain Bode'} static type() {
static displayType(){return qsTranslate("bodemagnitude", 'Bode Magnitude')} return "Gain Bode"
static displayTypeMultiple(){return qsTranslate("bodemagnitude", 'Bode Magnitudes')} }
static properties() {return {
[QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'),
[QT_TRANSLATE_NOOP('prop','pass')]: P.Enum.BodePass,
[QT_TRANSLATE_NOOP('prop','gain')]: new P.Expression(),
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
[QT_TRANSLATE_NOOP('prop','omGraduation')]: 'boolean'
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value', static displayType() {
om_0 = '', pass = 'high', gain = '20', labelPosition = 'above', labelX = 1, omGraduation = false) { return qsTranslate("bodemagnitude", "Bode Magnitude")
if(name == null) name = Objects.getNewName('G') }
if(name === 'G') name = 'G₀' // G is reserved for sum of BODE magnitudes (Somme gains Bode).
static displayTypeMultiple() {
return qsTranslate("bodemagnitude", "Bode Magnitudes")
}
static properties() {
return {
[QT_TRANSLATE_NOOP("prop", "om_0")]: new P.ObjectType("Point"),
[QT_TRANSLATE_NOOP("prop", "pass")]: P.Enum.BodePass,
[QT_TRANSLATE_NOOP("prop", "gain")]: new P.Expression(),
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
[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 = Objects.getNewName("G")
if(name === "G") name = "G₀" // G is reserved for sum of BODE magnitudes (Somme gains Bode).
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
if(typeof om_0 == "string") { if(typeof om_0 == "string") {
// Point name or create one // Point name or create one
om_0 = Objects.currentObjectsByName[om_0] om_0 = Objects.currentObjectsByName[om_0]
if(om_0 == null) { if(om_0 == null) {
// Create new point // Create new point
om_0 = Objects.createNewRegisteredObject('Point', [Objects.getNewName('ω'), true, this.color, 'name']) om_0 = Objects.createNewRegisteredObject("Point", [Objects.getNewName("ω"), true, this.color, "name"])
History.addToHistory(new CreateNewObject(om_0.name, 'Point', om_0.export())) History.addToHistory(new CreateNewObject(om_0.name, "Point", om_0.export()))
om_0.update() om_0.update()
labelPosition = 'below' labelPosition = "below"
} }
om_0.requiredBy.push(this) om_0.requiredBy.push(this)
} }
/** @type {Point} */ /** @type {Point} */
this.om_0 = om_0 this.om_0 = om_0
this.pass = pass this.pass = pass
if(typeof gain == 'number' || typeof gain == 'string') gain = new Expression(gain.toString()) if(typeof gain == "number" || typeof gain == "string") gain = new Expression(gain.toString())
this.gain = gain this.gain = gain
this.labelPosition = labelPosition this.labelPosition = labelPosition
this.labelX = labelX this.labelX = labelX
@ -67,21 +78,21 @@ export default class BodeMagnitude extends ExecutableObject {
} }
getReadableString() { getReadableString() {
let pass = this.pass === "low" ? qsTranslate("bodemagnitude", "low-pass") : qsTranslate("bodemagnitude", "high-pass"); let pass = this.pass === "low" ? qsTranslate("bodemagnitude", "low-pass") : qsTranslate("bodemagnitude", "high-pass")
return `${this.name}: ${pass}; ${this.om_0.name} = ${this.om_0.x}\n ${' '.repeat(this.name.length)}${this.gain.toString(true)} dB/dec` return `${this.name}: ${pass}; ${this.om_0.name} = ${this.om_0.x}\n ${" ".repeat(this.name.length)}${this.gain.toString(true)} dB/dec`
} }
getLatexString() { getLatexString() {
let pass = this.pass === "low" ? qsTranslate("bodemagnitude", "low-pass") : qsTranslate("bodemagnitude", "high-pass"); let pass = this.pass === "low" ? qsTranslate("bodemagnitude", "low-pass") : qsTranslate("bodemagnitude", "high-pass")
return `\\mathrm{${Latex.variable(this.name)}:}\\begin{array}{l} return `\\mathrm{${Latex.variable(this.name)}:}\\begin{array}{l}
\\textsf{${pass}};${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup} \\\\ \\textsf{${pass}};${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup} \\\\
${this.gain.latexMarkup}\\textsf{ dB/dec} ${this.gain.latexMarkup}\\textsf{ dB/dec}
\\end{array}` \\end{array}`
} }
execute(x=1) { execute(x = 1) {
if(typeof x == 'string') x = executeExpression(x) if(typeof x == "string") x = executeExpression(x)
if((this.pass === 'high' && x < this.om_0.x) || (this.pass === 'low' && x > this.om_0.x)) { if((this.pass === "high" && x < this.om_0.x) || (this.pass === "low" && x > this.om_0.x)) {
let dbfn = new Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`) let dbfn = new Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
return dbfn.execute(x) return dbfn.execute(x)
} else { } else {
@ -91,8 +102,8 @@ export default class BodeMagnitude extends ExecutableObject {
simplify(x = 1) { simplify(x = 1) {
let xval = x let xval = x
if(typeof x == 'string') xval = executeExpression(x) if(typeof x == "string") xval = executeExpression(x)
if((this.pass === 'high' && xval < this.om_0.x.execute()) || (this.pass === 'low' && xval > this.om_0.x.execute())) { 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}`) let dbfn = new Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
return dbfn.simplify(x) return dbfn.simplify(x)
} else { } else {
@ -107,9 +118,9 @@ export default class BodeMagnitude extends ExecutableObject {
draw(canvas) { draw(canvas) {
let base = [canvas.x2px(this.om_0.x.execute()), canvas.y2px(this.om_0.y.execute())] 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 dbfn = new Expression(`${this.gain.execute()}*(log10(x)-log10(${this.om_0.x}))+${this.om_0.y}`)
let inDrawDom = new EmptySet() let inDrawDom
if(this.pass === 'high') { if(this.pass === "high") {
// High pass, linear line from beginning, then constant to the end. // High pass, linear line from beginning, then constant to the end.
canvas.drawLine(base[0], base[1], canvas.width, base[1]) canvas.drawLine(base[0], base[1], canvas.width, base[1])
inDrawDom = parseDomain(`]-inf;${this.om_0.x}[`) inDrawDom = parseDomain(`]-inf;${this.om_0.x}[`)
@ -122,8 +133,8 @@ export default class BodeMagnitude extends ExecutableObject {
// Dashed line representing break in function // Dashed line representing break in function
let xpos = canvas.x2px(this.om_0.x.execute()) let xpos = canvas.x2px(this.om_0.x.execute())
let dashPxSize = 10 let dashPxSize = 10
for(let i = 0; i < canvas.height && this.omGraduation; i += dashPxSize*2) for(let i = 0; i < canvas.height && this.omGraduation; i += dashPxSize * 2)
canvas.drawLine(xpos, i, xpos, i+dashPxSize) canvas.drawLine(xpos, i, xpos, i + dashPxSize)
// Label // Label
this.drawLabel(canvas, 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)))
@ -131,17 +142,19 @@ export default class BodeMagnitude extends ExecutableObject {
update() { update() {
super.update() super.update()
let sumObjs = Objects.currentObjects['Somme gains Bode'] /** @type {BodeMagnitudeSum[]} */
let sumObjs = Objects.currentObjects["Somme gains Bode"]
if(sumObjs !== undefined && sumObjs.length > 0) { if(sumObjs !== undefined && sumObjs.length > 0) {
sumObjs[0].recalculateCache() sumObjs[0].recalculateCache()
} else { } else {
Objects.createNewRegisteredObject('Somme gains Bode') Objects.createNewRegisteredObject("Somme gains Bode")
} }
} }
delete() { delete() {
super.delete() super.delete()
let sumObjs = Objects.currentObjects['Somme gains Bode'] /** @type {BodeMagnitudeSum[]} */
let sumObjs = Objects.currentObjects["Somme gains Bode"]
if(sumObjs !== undefined && sumObjs.length > 0) { if(sumObjs !== undefined && sumObjs.length > 0) {
sumObjs[0].recalculateCache() sumObjs[0].recalculateCache()
} }

View file

@ -26,18 +26,32 @@ import Function from "./function.mjs"
export default class BodeMagnitudeSum extends ExecutableObject { export default class BodeMagnitudeSum extends ExecutableObject {
static type(){return 'Somme gains Bode'} static type() {
static displayType(){return qsTranslate("bodemagnitudesum", 'Bode Magnitudes Sum')} return "Somme gains Bode"
static displayTypeMultiple(){return qsTranslate("bodemagnitudesum", 'Bode Magnitudes Sum')} }
static createable() {return false}
static properties() {return {
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value', static displayType() {
labelPosition = 'above', labelX = 1) { return qsTranslate("bodemagnitudesum", "Bode Magnitudes Sum")
if(name == null) name = 'G' }
static displayTypeMultiple() {
return qsTranslate("bodemagnitudesum", "Bode Magnitudes Sum")
}
static createable() {
return false
}
static properties() {
return {
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
[QT_TRANSLATE_NOOP("prop", "labelX")]: "number"
}
}
constructor(name = null, visible = true, color = null, labelContent = "name + value",
labelPosition = "above", labelX = 1) {
if(name == null) name = "G"
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
this.labelPosition = labelPosition this.labelPosition = labelPosition
this.labelX = labelX this.labelX = labelX
@ -45,11 +59,11 @@ export default class BodeMagnitudeSum extends ExecutableObject {
} }
getReadableString() { getReadableString() {
return `${this.name} = ${Objects.getObjectsName('Gain Bode').join(' + ')}` return `${this.name} = ${Objects.getObjectsName("Gain Bode").join(" + ")}`
} }
getLatexString() { getLatexString() {
return `${Latex.variable(this.name)} = ${Objects.getObjectsName('Gain Bode').map(name => Latex.variable(name)).join(' + ')}` return `${Latex.variable(this.name)} = ${Objects.getObjectsName("Gain Bode").map(name => Latex.variable(name)).join(" + ")}`
} }
execute(x = 0) { execute(x = 0) {
@ -71,17 +85,17 @@ export default class BodeMagnitudeSum extends ExecutableObject {
return limitedDrawFunction.simplify(x) return limitedDrawFunction.simplify(x)
} }
} }
return '' return ""
} }
recalculateCache() { recalculateCache() {
this.cachedParts = [] this.cachedParts = []
// Calculating this is fairly resource expansive so it's cached. // Calculating this is fairly resource expansive so it's cached.
let magnitudeObjects = Objects.currentObjects['Gain Bode'] let magnitudeObjects = Objects.currentObjects["Gain Bode"]
if(magnitudeObjects === undefined || magnitudeObjects.length < 1) { if(magnitudeObjects === undefined || magnitudeObjects.length < 1) {
Objects.deleteObject(this.name) Objects.deleteObject(this.name)
} else { } else {
console.log('Recalculating cache gain') console.log("Recalculating cache gain")
// Minimum to draw (can be expended if needed, just not infinite or it'll cause issues. // Minimum to draw (can be expended if needed, just not infinite or it'll cause issues.
const MIN_DRAW = 1e-20 const MIN_DRAW = 1e-20
// Format: [[x value of where the filter transitions, magnitude, high-pass (bool)]] // Format: [[x value of where the filter transitions, magnitude, high-pass (bool)]]
@ -92,18 +106,18 @@ export default class BodeMagnitudeSum extends ExecutableObject {
magnitudes.push([Number.MAX_VALUE, 0, true]) // Draw the ending section magnitudes.push([Number.MAX_VALUE, 0, true]) // Draw the ending section
// Collect data from current magnitude (or gain in French) objects. // Collect data from current magnitude (or gain in French) objects.
let baseY = 0 let baseY = 0
for(/** @type {Bodemagnitude} */ let magnitudeObj of magnitudeObjects) { // Sorting by their om_0 position. for(/** @type {BodeMagnitude} */ let magnitudeObj of magnitudeObjects) { // Sorting by their om_0 position.
const om0x = magnitudeObj.om_0.x.execute() const om0x = magnitudeObj.om_0.x.execute()
magnitudes.push([om0x, magnitudeObj.gain.execute(), magnitudeObj.pass === 'high']) magnitudes.push([om0x, magnitudeObj.gain.execute(), magnitudeObj.pass === "high"])
baseY += magnitudeObj.execute(MIN_DRAW) baseY += magnitudeObj.execute(MIN_DRAW)
} }
// Sorting the data by their x transitions value // Sorting the data by their x transitions value
magnitudes.sort((a,b) => a[XVALUE] - b[XVALUE]) magnitudes.sort((a, b) => a[XVALUE] - b[XVALUE])
// Adding the total gains. // Adding the total gains.
let magnitudesBeforeTransition = [] let magnitudesBeforeTransition = []
let magnitudesAfterTransition = [] let magnitudesAfterTransition = []
let totalMagnitudeAtStart = 0 // Magnitude at the lowest x value (sum of all high-pass magnitudes) let totalMagnitudeAtStart = 0 // Magnitude at the lowest x value (sum of all high-pass magnitudes)
for(let [om0x, magnitude, highpass] of magnitudes){ for(let [om0x, magnitude, highpass] of magnitudes) {
if(highpass) { if(highpass) {
magnitudesBeforeTransition.push(magnitude) magnitudesBeforeTransition.push(magnitude)
magnitudesAfterTransition.push(0) magnitudesAfterTransition.push(0)

View file

@ -27,33 +27,44 @@ import { ExecutableObject } from "./common.mjs"
export default class BodePhase extends ExecutableObject { export default class BodePhase extends ExecutableObject {
static type(){return 'Phase Bode'} static type() {
static displayType(){return qsTranslate("bodephase", 'Bode Phase')} return "Phase Bode"
static displayTypeMultiple(){return qsTranslate("bodephase", 'Bode Phases')} }
static properties() {return {
[QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'),
[QT_TRANSLATE_NOOP('prop','phase')]: new P.Expression(),
[QT_TRANSLATE_NOOP('prop','unit')]: new P.Enum('°', 'deg', 'rad'),
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number'
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value', static displayType() {
om_0 = '', phase = 90, unit = '°', labelPosition = 'above', labelX = 1) { return qsTranslate("bodephase", "Bode Phase")
if(name == null) name = Objects.getNewName('φ') }
if(name === 'φ') name = 'φ₀' // φ is reserved for sum of Bode phases.
static displayTypeMultiple() {
return qsTranslate("bodephase", "Bode Phases")
}
static properties() {
return {
[QT_TRANSLATE_NOOP("prop", "om_0")]: new P.ObjectType("Point"),
[QT_TRANSLATE_NOOP("prop", "phase")]: new P.Expression(),
[QT_TRANSLATE_NOOP("prop", "unit")]: new P.Enum("°", "deg", "rad"),
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
[QT_TRANSLATE_NOOP("prop", "labelX")]: "number"
}
}
constructor(name = null, visible = true, color = null, labelContent = "name + value",
om_0 = "", phase = 90, unit = "°", labelPosition = "above", labelX = 1) {
if(name == null) name = Objects.getNewName("φ")
if(name === "φ") name = "φ₀" // φ is reserved for sum of Bode phases.
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
if(typeof phase == 'number' || typeof phase == 'string') phase = new Expression(phase.toString()) if(typeof phase == "number" || typeof phase == "string") phase = new Expression(phase.toString())
this.phase = phase this.phase = phase
if(typeof om_0 == "string") { if(typeof om_0 == "string") {
// Point name or create one // Point name or create one
om_0 = Objects.currentObjectsByName[om_0] om_0 = Objects.currentObjectsByName[om_0]
if(om_0 == null) { if(om_0 == null) {
// Create new point // Create new point
om_0 = Objects.createNewRegisteredObject('Point', [Objects.getNewName('ω'), this.color, 'name']) om_0 = Objects.createNewRegisteredObject("Point", [Objects.getNewName("ω"), this.color, "name"])
om_0.labelPosition = this.phase.execute() >= 0 ? 'above' : 'below' om_0.labelPosition = this.phase.execute() >= 0 ? "above" : "below"
History.history.addToHistory(new CreateNewObject(om_0.name, 'Point', om_0.export())) History.history.addToHistory(new CreateNewObject(om_0.name, "Point", om_0.export()))
labelPosition = 'below' labelPosition = "below"
} }
om_0.requiredBy.push(this) om_0.requiredBy.push(this)
} }
@ -72,8 +83,8 @@ export default class BodePhase extends ExecutableObject {
return `${Latex.variable(this.name)}: ${this.phase.latexMarkup}\\textsf{${this.unit} at }${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup}` return `${Latex.variable(this.name)}: ${this.phase.latexMarkup}\\textsf{${this.unit} at }${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup}`
} }
execute(x=1) { execute(x = 1) {
if(typeof x == 'string') x = executeExpression(x) if(typeof x == "string") x = executeExpression(x)
if(x < this.om_0.x) { if(x < this.om_0.x) {
return this.om_0.y.execute() return this.om_0.y.execute()
} else { } else {
@ -83,11 +94,11 @@ export default class BodePhase extends ExecutableObject {
simplify(x = 1) { simplify(x = 1) {
let xval = x let xval = x
if(typeof x == 'string') xval = executeExpression(x) if(typeof x == "string") xval = executeExpression(x)
if(xval < this.om_0.x) { if(xval < this.om_0.x) {
return this.om_0.y.toString() return this.om_0.y.toString()
} else { } else {
let newExp = this.om_0.y.toEditableString() + ' + ' + this.phase.toEditableString() let newExp = this.om_0.y.toEditableString() + " + " + this.phase.toEditableString()
return new Expression(newExp) return new Expression(newExp)
} }
} }
@ -101,7 +112,7 @@ export default class BodePhase extends ExecutableObject {
let omy = this.om_0.y.execute() let omy = this.om_0.y.execute()
let augmt = this.phase.execute() let augmt = this.phase.execute()
let baseY = canvas.y2px(omy) let baseY = canvas.y2px(omy)
let augmtY = canvas.y2px(omy+augmt) let augmtY = canvas.y2px(omy + augmt)
// Before change line. // Before change line.
canvas.drawLine(0, baseY, Math.min(baseX, canvas.height), baseY) canvas.drawLine(0, baseY, Math.min(baseX, canvas.height), baseY)
// Transition line. // Transition line.
@ -115,17 +126,19 @@ export default class BodePhase extends ExecutableObject {
update() { update() {
super.update() super.update()
let sumObjs = Objects.currentObjects['Somme phases Bode'] /** @type {BodePhaseSum[]} */
let sumObjs = Objects.currentObjects["Somme phases Bode"]
if(sumObjs !== undefined && sumObjs.length > 0) { if(sumObjs !== undefined && sumObjs.length > 0) {
sumObjs[0].recalculateCache() sumObjs[0].recalculateCache()
} else { } else {
Objects.createNewRegisteredObject('Somme phases Bode') Objects.createNewRegisteredObject("Somme phases Bode")
} }
} }
delete() { delete() {
super.update() super.update()
let sumObjs = Objects.currentObjects['Somme phases Bode'] /** @type {BodePhaseSum[]} */
let sumObjs = Objects.currentObjects["Somme phases Bode"]
if(sumObjs !== undefined && sumObjs.length > 0) { if(sumObjs !== undefined && sumObjs.length > 0) {
sumObjs[0].recalculateCache() sumObjs[0].recalculateCache()
} }

View file

@ -24,18 +24,32 @@ import Latex from "../module/latex.mjs"
import { ExecutableObject } from "./common.mjs" import { ExecutableObject } from "./common.mjs"
export default class BodePhaseSum extends ExecutableObject { export default class BodePhaseSum extends ExecutableObject {
static type(){return 'Somme phases Bode'} static type() {
static displayType(){return qsTranslate("bodephasesum", 'Bode Phases Sum')} return "Somme phases Bode"
static displayTypeMultiple(){return qsTranslate("bodephasesum", 'Bode Phases Sum')} }
static createable() {return false}
static properties() {return {
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value', static displayType() {
labelPosition = 'above', labelX = 1) { return qsTranslate("bodephasesum", "Bode Phases Sum")
if(name == null) name = 'φ' }
static displayTypeMultiple() {
return qsTranslate("bodephasesum", "Bode Phases Sum")
}
static createable() {
return false
}
static properties() {
return {
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
[QT_TRANSLATE_NOOP("prop", "labelX")]: "number"
}
}
constructor(name = null, visible = true, color = null, labelContent = "name + value",
labelPosition = "above", labelX = 1) {
if(name == null) name = "φ"
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
this.labelPosition = labelPosition this.labelPosition = labelPosition
this.labelX = labelX this.labelX = labelX
@ -43,29 +57,29 @@ export default class BodePhaseSum extends ExecutableObject {
} }
getReadableString() { getReadableString() {
return `${this.name} = ${Objects.getObjectsName('Phase Bode').join(' + ')}` return `${this.name} = ${Objects.getObjectsName("Phase Bode").join(" + ")}`
} }
getLatexString() { getLatexString() {
return `${Latex.variable(this.name)} = ${Objects.getObjectsName('Phase Bode').map(name => Latex.variable(name)).join(' + ')}` return `${Latex.variable(this.name)} = ${Objects.getObjectsName("Phase Bode").map(name => Latex.variable(name)).join(" + ")}`
} }
execute(x=1) { execute(x = 1) {
if(typeof x == 'string') x = executeExpression(x) if(typeof x == "string") x = executeExpression(x)
for(let i = 0; i < this.om0xList.length-1; i++) { for(let i = 0; i < this.om0xList.length - 1; i++) {
if(x >= this.om0xList[i] && x < this.om0xList[i+1]) return this.phasesList[i] if(x >= this.om0xList[i] && x < this.om0xList[i + 1]) return this.phasesList[i]
} }
} }
simplify(x = 1) { simplify(x = 1) {
let xval = x let xval = x
if(typeof x == 'string') xval = executeExpression(x) if(typeof x == "string") xval = executeExpression(x)
for(let i = 0; i < this.om0xList.length-1; i++) { for(let i = 0; i < this.om0xList.length - 1; i++) {
if(xval >= this.om0xList[i] && xval < this.om0xList[i+1]) { if(xval >= this.om0xList[i] && xval < this.om0xList[i + 1]) {
return (new Expression(this.phasesExprList[i])).simplify() return (new Expression(this.phasesExprList[i])).simplify()
} }
} }
return '0' return "0"
} }
canExecute(x = 1) { canExecute(x = 1) {
@ -78,43 +92,42 @@ export default class BodePhaseSum extends ExecutableObject {
let drawMax = 1e20 let drawMax = 1e20
this.om0xList = [drawMin, drawMax] this.om0xList = [drawMin, drawMax]
this.phasesList = [0] this.phasesList = [0]
this.phasesExprList = ['0'] this.phasesExprList = ["0"]
let phasesDict = new Map() let phasesDict = new Map()
let phasesExprDict = new Map() let phasesExprDict = new Map()
phasesDict.set(drawMax, 0) phasesDict.set(drawMax, 0)
let phaseObjects = Objects.currentObjects['Phase Bode'] let phaseObjects = Objects.currentObjects["Phase Bode"]
if(phaseObjects === undefined || phaseObjects.length < 1) { if(phaseObjects === undefined || phaseObjects.length < 1) {
Objects.deleteObject(this.name) Objects.deleteObject(this.name)
} else { } else {
console.log('Recalculating cache phase') console.log("Recalculating cache phase")
for(/** @type {Bodephase} */ let obj of phaseObjects) { for(/** @type {BodePhase} */ let obj of phaseObjects) {
this.om0xList.push(obj.om_0.x.execute()) this.om0xList.push(obj.om_0.x.execute())
if(!phasesDict.has(obj.om_0.x.execute())) { if(!phasesDict.has(obj.om_0.x.execute())) {
phasesDict.set(obj.om_0.x.execute(), obj.phase.execute()) phasesDict.set(obj.om_0.x.execute(), obj.phase.execute())
phasesExprDict.set(obj.om_0.x.execute(), obj.phase.toEditableString()) phasesExprDict.set(obj.om_0.x.execute(), obj.phase.toEditableString())
} else { } else {
phasesDict.set(obj.om_0.x.execute(), obj.phase.execute() + phasesDict.get(obj.om_0.x.execute())) phasesDict.set(obj.om_0.x.execute(), obj.phase.execute() + phasesDict.get(obj.om_0.x.execute()))
phasesExprDict.set(obj.om_0.x.execute(), obj.phase.toEditableString() + '+' + phasesExprDict.get(obj.om_0.x.execute())) phasesExprDict.set(obj.om_0.x.execute(), obj.phase.toEditableString() + "+" + phasesExprDict.get(obj.om_0.x.execute()))
} }
this.phasesList[0] += obj.om_0.y.execute() this.phasesList[0] += obj.om_0.y.execute()
this.phasesExprList[0] += '+' + obj.om_0.y.toEditableString() this.phasesExprList[0] += "+" + obj.om_0.y.toEditableString()
} }
this.om0xList.sort((a,b) => a - b) this.om0xList.sort((a, b) => a - b)
let totalAdded = this.phasesList[0]
for(let i = 1; i < this.om0xList.length; i++) { for(let i = 1; i < this.om0xList.length; i++) {
this.phasesList[i] = this.phasesList[i-1] + phasesDict.get(this.om0xList[i]) this.phasesList[i] = this.phasesList[i - 1] + phasesDict.get(this.om0xList[i])
this.phasesExprList[i] = this.phasesExprList[i-1] + '+' + phasesDict.get(this.om0xList[i]) this.phasesExprList[i] = this.phasesExprList[i - 1] + "+" + phasesDict.get(this.om0xList[i])
} }
} }
} }
draw(canvas) { draw(canvas) {
for(let i = 0; i < this.om0xList.length-1; i++) { for(let i = 0; i < this.om0xList.length - 1; i++) {
let om0xBegin = canvas.x2px(this.om0xList[i]) let om0xBegin = canvas.x2px(this.om0xList[i])
let om0xEnd = canvas.x2px(this.om0xList[i+1]) let om0xEnd = canvas.x2px(this.om0xList[i + 1])
let baseY = canvas.y2px(this.phasesList[i]) let baseY = canvas.y2px(this.phasesList[i])
let nextY = canvas.y2px(this.phasesList[i+1]) let nextY = canvas.y2px(this.phasesList[i + 1])
canvas.drawLine(om0xBegin, baseY, om0xEnd, baseY) canvas.drawLine(om0xBegin, baseY, om0xEnd, baseY)
canvas.drawLine(om0xEnd, baseY, om0xEnd, nextY) canvas.drawLine(om0xEnd, baseY, om0xEnd, nextY)
} }

View file

@ -16,10 +16,10 @@
* 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 { getRandomColor, textsub } from "../utils.mjs" import { getRandomColor } from "../utils.mjs"
import Objects from "../module/objects.mjs" import Objects from "../module/objects.mjs"
import Latex from "../module/latex.mjs" import Latex from "../module/latex.mjs"
import {ensureTypeSafety, serializesByPropertyType} from "../parameters.mjs" import { ensureTypeSafety, serializesByPropertyType } from "../parameters.mjs"
// This file contains the default data to be imported from all other objects // This file contains the default data to be imported from all other objects
@ -32,26 +32,34 @@ export class DrawableObject {
* Base name of the object. Needs to be constant over time. * Base name of the object. Needs to be constant over time.
* @return {string} Type of the object. * @return {string} Type of the object.
*/ */
static type(){return 'Unknown'} static type() {
return "Unknown"
}
/** /**
* Translated name of the object to be shown to the user. * Translated name of the object to be shown to the user.
* @return {string} * @return {string}
*/ */
static displayType(){return 'Unknown'} static displayType() {
return "Unknown"
}
/** /**
* Translated name of the object in plural form to be shown to the user. * Translated name of the object in plural form to be shown to the user.
* @return {string} * @return {string}
*/ */
static displayTypeMultiple(){return 'Unknowns'} static displayTypeMultiple() {
return "Unknowns"
}
/** /**
* True if this object can be created by the user, false if it can only * True if this object can be created by the user, false if it can only
* be instantiated by other objects * be instantiated by other objects
* @return {boolean} * @return {boolean}
*/ */
static createable() {return true} static createable() {
return true
}
/** /**
* List of properties used in the Object Editor. * List of properties used in the Object Editor.
@ -70,14 +78,18 @@ export class DrawableObject {
* *
* @return {Object.<string,string|Enum|List|ObjectType|Dictionary>} * @return {Object.<string,string|Enum|List|ObjectType|Dictionary>}
*/ */
static properties() {return {}} static properties() {
return {}
}
/** /**
* True if this object can be executed, so that an y value might be computed * 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. * for an x value. Only ExecutableObjects have that property set to true.
* @return {boolean} * @return {boolean}
*/ */
static executable() {return false} static executable() {
return false
}
/** /**
* Imports the object from its serialized form. * Imports the object from its serialized form.
@ -85,10 +97,10 @@ export class DrawableObject {
*/ */
static import(name, visible, color, labelContent, ...args) { static import(name, visible, color, labelContent, ...args) {
let importedArgs = [name.toString(), visible === true, color.toString(), labelContent] let importedArgs = [name.toString(), visible === true, color.toString(), labelContent]
console.log('Importing', this.type(), name, args) console.log("Importing", this.type(), name, args)
for(let [name, propType] of Object.entries(this.properties())) for(let [name, propType] of Object.entries(this.properties()))
if(!name.startsWith('comment')) { if(!name.startsWith("comment")) {
importedArgs.push(ensureTypeSafety(propType, args[importedArgs.length-4])) importedArgs.push(ensureTypeSafety(propType, args[importedArgs.length - 4]))
} }
return new this(...importedArgs) return new this(...importedArgs)
} }
@ -98,10 +110,10 @@ export class DrawableObject {
* @param {string} name - Name of the object * @param {string} name - Name of the object
* @param {boolean} visible - true if the object is visible, false otherwise. * @param {boolean} visible - true if the object is visible, false otherwise.
* @param {color|string} color - Color of the object (can be string or QColor) * @param {color|string} color - Color of the object (can be string or QColor)
* @param {'null'|'name'|'name + value'} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label. * @param {"null"|"name"|"name + value"} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label.
* @constructor * @constructor
*/ */
constructor(name, visible = true, color = null, labelContent = 'name + value') { constructor(name, visible = true, color = null, labelContent = "name + value") {
if(color == null) color = getRandomColor() if(color == null) color = getRandomColor()
this.type = this.constructor.type() this.type = this.constructor.type()
this.name = name this.name = name
@ -120,7 +132,7 @@ export class DrawableObject {
export() { export() {
let exportList = [this.name, this.visible, this.color.toString(), this.labelContent] let exportList = [this.name, this.visible, this.color.toString(), this.labelContent]
for(let [name, propType] of Object.entries(this.constructor.properties())) for(let [name, propType] of Object.entries(this.constructor.properties()))
if(!name.startsWith('comment')) if(!name.startsWith("comment"))
exportList.push(serializesByPropertyType(propType, this[name])) exportList.push(serializesByPropertyType(propType, this[name]))
return exportList return exportList
} }
@ -151,12 +163,12 @@ export class DrawableObject {
*/ */
getLabel() { getLabel() {
switch(this.labelContent) { switch(this.labelContent) {
case 'name': case "name":
return this.name return this.name
case 'name + value': case "name + value":
return this.getReadableString() return this.getReadableString()
case 'null': case "null":
return '' return ""
} }
} }
@ -170,12 +182,12 @@ export class DrawableObject {
*/ */
getLatexLabel() { getLatexLabel() {
switch(this.labelContent) { switch(this.labelContent) {
case 'name': case "name":
return Latex.variable(this.name) return Latex.variable(this.name)
case 'name + value': case "name + value":
return this.getLatexString() return this.getLatexString()
case 'null': case "null":
return '' return ""
} }
} }
@ -203,8 +215,8 @@ export class DrawableObject {
let currentObjectsByName = Objects.currentObjectsByName let currentObjectsByName = Objects.currentObjectsByName
let properties = this.constructor.properties() let properties = this.constructor.properties()
for(let property in properties) for(let property in properties)
if(typeof properties[property] == 'object' && 'type' in properties[property]) if(typeof properties[property] == "object" && "type" in properties[property])
if(properties[property].type === 'Expression' && this[property] != null) { if(properties[property].type === "Expression" && this[property] != null) {
// Expressions with dependencies // Expressions with dependencies
for(let objName of this[property].requiredObjects()) { for(let objName of this[property].requiredObjects()) {
if(objName in currentObjectsByName && !this.requires.includes(currentObjectsByName[objName])) { if(objName in currentObjectsByName && !this.requires.includes(currentObjectsByName[objName])) {
@ -216,7 +228,7 @@ export class DrawableObject {
// Recalculate // Recalculate
this[property].recache() this[property].recache()
} else if(properties[property].type === 'ObjectType' && this[property] != null) { } else if(properties[property].type === "ObjectType" && this[property] != null) {
// Object dependency // Object dependency
this.requires.push(this[property]) this.requires.push(this[property])
this[property].requiredBy.push(this) this[property].requiredBy.push(this)
@ -242,7 +254,8 @@ export class DrawableObject {
* Abstract method. Draw the object onto the canvas with the. * Abstract method. Draw the object onto the canvas with the.
* @param {CanvasAPI} canvas * @param {CanvasAPI} canvas
*/ */
draw(canvas) {} draw(canvas) {
}
/** /**
* Applicates a drawFunction with two position arguments depending on * Applicates a drawFunction with two position arguments depending on
@ -252,46 +265,46 @@ export class DrawableObject {
* *
* @param {string|Enum} labelPosition - Position of the label relative to the marked position * @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 {number} offset - Margin between the position and the object to be drawn
* @param {Object.<string, int>} size - Size of the label item, containing two properties, "width" and "height" * @param {{width: number, height: number}} 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} posX - Component of the marked position on the x-axis
* @param {number} posY - Component of the marked position on the y-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 * @param {function} drawFunction - Function with two arguments (x, y) that will be called to draw the label
*/ */
drawPositionDivergence(labelPosition, offset, size, posX, posY, drawFunction) { drawPositionDivergence(labelPosition, offset, size, posX, posY, drawFunction) {
switch(labelPosition) { switch(labelPosition) {
case 'center': case "center":
drawFunction(posX-size.width/2, posY-size.height/2) drawFunction(posX - size.width / 2, posY - size.height / 2)
break; break
case 'top': case "top":
case 'above': case "above":
drawFunction(posX-size.width/2, posY-size.height-offset) drawFunction(posX - size.width / 2, posY - size.height - offset)
break; break
case 'bottom': case "bottom":
case 'below': case "below":
drawFunction(posX-size.width/2, posY+offset) drawFunction(posX - size.width / 2, posY + offset)
break; break
case 'left': case "left":
drawFunction(posX-size.width-offset, posY-size.height/2) drawFunction(posX - size.width - offset, posY - size.height / 2)
break; break
case 'right': case "right":
drawFunction(posX+offset, posY-size.height/2) drawFunction(posX + offset, posY - size.height / 2)
break; break
case 'top-left': case "top-left":
case 'above-left': case "above-left":
drawFunction(posX-size.width, posY-size.height-offset) drawFunction(posX - size.width, posY - size.height - offset)
break; break
case 'top-right': case "top-right":
case 'above-right': case "above-right":
drawFunction(posX+offset, posY-size.height-offset) drawFunction(posX + offset, posY - size.height - offset)
break; break
case 'bottom-left': case "bottom-left":
case 'below-left': case "below-left":
drawFunction(posX-size.width-offset, posY+offset) drawFunction(posX - size.width - offset, posY + offset)
break; break
case 'bottom-right': case "bottom-right":
case 'below-right': case "below-right":
drawFunction(posX+offset, posY+offset) drawFunction(posX + offset, posY + offset)
break; break
} }
} }
@ -315,7 +328,7 @@ export class DrawableObject {
* @param {function|null} drawFunctionLatex - Function (x,y,imageData) to display the latex image * @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 * @param {function|null} drawFunctionText - Function (x,y,text,textSize) to display the text
*/ */
drawLabel(canvas, labelPosition, posX, posY,forceText = false, drawLabel(canvas, labelPosition, posX, posY, forceText = false,
getLatexFunction = null, getTextFunction = null, drawFunctionLatex = null, drawFunctionText = null) { getLatexFunction = null, getTextFunction = null, drawFunctionLatex = null, drawFunctionText = null) {
// Default functions // Default functions
if(getLatexFunction == null) if(getLatexFunction == null)
@ -323,30 +336,29 @@ export class DrawableObject {
if(getTextFunction == null) if(getTextFunction == null)
getTextFunction = this.getLabel.bind(this) getTextFunction = this.getLabel.bind(this)
if(drawFunctionLatex == null) if(drawFunctionLatex == null)
drawFunctionLatex = (x,y,ltxImg) => canvas.drawVisibleImage(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) if(drawFunctionText == null)
drawFunctionText = (x,y,text,textSize) => canvas.drawVisibleText(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 // Drawing the label
let offset
if(!forceText && Latex.enabled) { if(!forceText && Latex.enabled) {
// With latex // With latex
let drawLblCb = ((ltxImg) => { let drawLblCb = ((ltxImg) => {
this.drawPositionDivergence(labelPosition, 8+canvas.linewidth/2, ltxImg, posX, posY, (x,y) => drawFunctionLatex(x,y,ltxImg)) this.drawPositionDivergence(labelPosition, 8 + canvas.linewidth / 2, ltxImg, posX, posY, (x, y) => drawFunctionLatex(x, y, ltxImg))
}).bind(this) })
let ltxLabel = getLatexFunction(); let ltxLabel = getLatexFunction()
if(ltxLabel !== "") if(ltxLabel !== "")
canvas.renderLatexImage(ltxLabel, this.color, drawLblCb.bind(this)) canvas.renderLatexImage(ltxLabel, this.color, drawLblCb)
} else { } else {
// Without latex // Without latex
let text = getTextFunction() let text = getTextFunction()
canvas.font = `${canvas.textsize}px sans-serif` canvas.font = `${canvas.textsize}px sans-serif`
let textSize = canvas.measureText(text) let textSize = canvas.measureText(text)
this.drawPositionDivergence(labelPosition, 8+canvas.linewidth/2, textSize, posX, posY, (x,y) => drawFunctionText(x,y,text,textSize)) this.drawPositionDivergence(labelPosition, 8 + canvas.linewidth / 2, textSize, posX, posY, (x, y) => drawFunctionText(x, y, text, textSize))
} }
} }
toString() { toString() {
return this.name; return this.name
} }
} }
@ -367,28 +379,38 @@ export class ExecutableObject extends DrawableObject {
* @param {number} x * @param {number} x
* @returns {number|null} * @returns {number|null}
*/ */
execute(x = 1) {return 0} execute(x = 1) {
return 0
}
/** /**
* Returns false if the object isn't defined on the given x, true otherwise. * Returns false if the object isn't defined on the given x, true otherwise.
* *
* @param {number} x * @param {number} x
* @returns {boolean} * @returns {boolean}
*/ */
canExecute(x = 1) {return true} canExecute(x = 1) {
return true
}
/** /**
* Returns the simplified expression string for a given x. * Returns the simplified expression string for a given x.
* *
* @param {number} x * @param {number} x
* @returns {string|Expression} * @returns {string|Expression}
*/ */
simplify(x = 1) {return '0'} simplify(x = 1) {
return "0"
}
/** /**
* True if this object can be executed, so that an y value might be computed * 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. * for an x value. Only ExecutableObjects have that property set to true.
* @return {boolean} * @return {boolean}
*/ */
static executable() {return true} static executable() {
return true
}
} }

View file

@ -24,18 +24,30 @@ import { ExecutableObject } from "./common.mjs"
export default class DistributionFunction extends ExecutableObject { export default class DistributionFunction extends ExecutableObject {
static type(){return 'Repartition'} static type() {
static displayType(){return qsTranslate("distribution", 'Repartition')} return "Repartition"
static displayTypeMultiple(){return qsTranslate("distribution", 'Repartition functions')} }
static properties() {return {
'comment1': QT_TRANSLATE_NOOP( static displayType() {
'comment', return qsTranslate("distribution", "Repartition")
'Note: Specify the probability for each value.' }
static displayTypeMultiple() {
return qsTranslate("distribution", "Repartition functions")
}
static properties() {
return {
"comment1": QT_TRANSLATE_NOOP(
"comment",
"Note: Specify the probability for each value."
), ),
[QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'double', /^-?[\d.,]+$/, /^-?[\d.,]+$/, 'P({name_} = ', ') = '), [QT_TRANSLATE_NOOP("prop", "probabilities")]: new P.Dictionary("string", "double", /^-?[\d.,]+$/, /^-?[\d.,]+$/, "P({name_} = ", ") = "),
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, [QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number', [QT_TRANSLATE_NOOP("prop", "labelX")]: "number"
}} }
}
static import(name, visible, color, labelContent, ...args) { static import(name, visible, color, labelContent, ...args) {
console.log(args, args.length) console.log(args, args.length)
if(args.length === 5) { if(args.length === 5) {
@ -46,9 +58,9 @@ export default class DistributionFunction extends ExecutableObject {
return super.import(name, visible, color, labelContent, ...args) return super.import(name, visible, color, labelContent, ...args)
} }
constructor(name = null, visible = true, color = null, labelContent = 'name + value', constructor(name = null, visible = true, color = null, labelContent = "name + value",
probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) { probabilities = { "0": "0" }, labelPosition = "above", labelX = 1) {
if(name == null) name = Objects.getNewName('XYZUVW', "F_") if(name == null) name = Objects.getNewName("XYZUVW", "F_")
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
this.probabilities = probabilities this.probabilities = probabilities
this.labelPosition = labelPosition this.labelPosition = labelPosition
@ -57,27 +69,29 @@ export default class DistributionFunction extends ExecutableObject {
} }
getReadableString() { getReadableString() {
let keys = Object.keys(this.probabilities).sort((a,b) => a-b); let keys = Object.keys(this.probabilities).sort((a, b) => a - b)
let varname = this.name.substring(this.name.indexOf("_")+1) let varname = this.name.substring(this.name.indexOf("_") + 1)
return `${this.name}(x) = P(${varname} ≤ x)\n` + keys.map(idx => `P(${varname}=${idx})=${this.probabilities[idx]}`).join("; ") return `${this.name}(x) = P(${varname} ≤ x)\n` + keys.map(idx => `P(${varname}=${idx})=${this.probabilities[idx]}`).join("; ")
} }
getLatexString() { getLatexString() {
let keys = Object.keys(this.probabilities).sort((a,b) => a-b); let keys = Object.keys(this.probabilities).sort((a, b) => a - b)
let funcName = Latex.variable(this.name) let funcName = Latex.variable(this.name)
let varName = Latex.variable(this.name.substring(this.name.indexOf("_")+1)) let varName = Latex.variable(this.name.substring(this.name.indexOf("_") + 1))
return `\\begin{array}{l}{${funcName}}(x) = P(${varName} \\le x)\\\\` + keys.map(idx => `P(${varName}=${idx})=${this.probabilities[idx]}`).join("; ") + '\\end{array}' return `\\begin{array}{l}{${funcName}}(x) = P(${varName} \\le x)\\\\` + keys.map(idx => `P(${varName}=${idx})=${this.probabilities[idx]}`).join("; ") + "\\end{array}"
} }
execute(x = 1) { execute(x = 1) {
let ret = 0; let ret = 0
Object.keys(this.probabilities).sort((a,b) => a-b).forEach(idx => { Object.keys(this.probabilities).sort((a, b) => a - b).forEach(idx => {
if(x >= idx) ret += parseFloat(this.probabilities[idx].replace(/,/g, '.')) if(x >= idx) ret += parseFloat(this.probabilities[idx].replace(/,/g, "."))
}) })
return ret return ret
} }
canExecute(x = 1) {return true} canExecute(x = 1) {
return true
}
// Simplify returns the simplified string of the expression. // Simplify returns the simplified string of the expression.
simplify(x = 1) { simplify(x = 1) {
@ -86,53 +100,53 @@ export default class DistributionFunction extends ExecutableObject {
getLabel() { getLabel() {
switch(this.labelContent) { switch(this.labelContent) {
case 'name': case "name":
return `${this.name}(x)` return `${this.name}(x)`
case 'name + value': case "name + value":
return this.getReadableString() return this.getReadableString()
case 'null': case "null":
return '' return ""
} }
} }
draw(canvas) { draw(canvas) {
let currentY = 0; let currentY = 0
let keys = Object.keys(this.probabilities).map(idx => parseInt(idx)).sort((a,b) => a-b) 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, '.'))) { if(canvas.isVisible(keys[0], this.probabilities[keys[0]].replace(/,/g, "."))) {
canvas.drawLine(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)) { if(canvas.isVisible(keys[0], 0)) {
canvas.arc(canvas.x2px(keys[0])+4,canvas.y2px(0), 4, Math.PI / 2, 3 * Math.PI / 2); 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++) { for(let i = 0; i < keys.length - 1; i++) {
let idx = keys[i]; let idx = keys[i]
currentY += parseFloat(this.probabilities[idx].replace(/,/g, '.')); currentY += parseFloat(this.probabilities[idx].replace(/,/g, "."))
if(canvas.isVisible(idx,currentY) || canvas.isVisible(keys[i+1],currentY)) { if(canvas.isVisible(idx, currentY) || canvas.isVisible(keys[i + 1], currentY)) {
canvas.drawLine( canvas.drawLine(
Math.max(0,canvas.x2px(idx)), Math.max(0, canvas.x2px(idx)),
canvas.y2px(currentY), canvas.y2px(currentY),
Math.min(canvas.width,canvas.x2px(keys[i+1])), Math.min(canvas.width, canvas.x2px(keys[i + 1])),
canvas.y2px(currentY) canvas.y2px(currentY)
) )
if(canvas.isVisible(idx,currentY)) { if(canvas.isVisible(idx, currentY)) {
canvas.disc(canvas.x2px(idx), canvas.y2px(currentY), 4) canvas.disc(canvas.x2px(idx), canvas.y2px(currentY), 4)
} }
if(canvas.isVisible(keys[i+1],currentY)) { if(canvas.isVisible(keys[i + 1], currentY)) {
canvas.arc(canvas.x2px(keys[i+1])+4,canvas.y2px(currentY), 4, Math.PI / 2, 3 * Math.PI / 2); 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]]))) { if(canvas.isVisible(keys[keys.length - 1], currentY + parseFloat(this.probabilities[keys[keys.length - 1]]))) {
canvas.drawLine( canvas.drawLine(
Math.max(0,canvas.x2px(keys[keys.length-1])), Math.max(0, canvas.x2px(keys[keys.length - 1])),
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))), canvas.y2px(currentY + parseFloat(this.probabilities[keys[keys.length - 1]].replace(/,/g, "."))),
canvas.width, canvas.width,
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))) canvas.y2px(currentY + parseFloat(this.probabilities[keys[keys.length - 1]].replace(/,/g, ".")))
) )
canvas.disc( canvas.disc(
canvas.x2px(keys[keys.length-1]), canvas.x2px(keys[keys.length - 1]),
canvas.y2px( canvas.y2px(
currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.')) currentY + parseFloat(this.probabilities[keys[keys.length - 1]].replace(/,/g, "."))
), ),
4 4
) )

View file

@ -25,39 +25,50 @@ import Latex from "../module/latex.mjs"
export default class Function extends ExecutableObject { export default class Function extends ExecutableObject {
static type(){return 'Function'} static type() {
static displayType(){return qsTranslate("function", 'Function')} return "Function"
static displayTypeMultiple(){return qsTranslate("function", 'Functions')} }
static properties() {return {
[QT_TRANSLATE_NOOP('prop','expression')]: new P.Expression('x'),
[QT_TRANSLATE_NOOP('prop','definitionDomain')]: 'Domain',
[QT_TRANSLATE_NOOP('prop','destinationDomain')]: 'Domain',
'comment1': QT_TRANSLATE_NOOP(
'comment',
'Ex: R+* (ℝ⁺*), N (), Z-* (ℤ⁻*), ]0;1[, {3;4;5}'
),
[QT_TRANSLATE_NOOP('prop','displayMode')]: P.Enum.FunctionDisplayType,
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
'comment2': QT_TRANSLATE_NOOP(
'comment',
'The following parameters are used when the definition domain is a non-continuous set. (Ex: , , sets like {0;3}...)'
),
[QT_TRANSLATE_NOOP('prop','drawPoints')]: 'boolean',
[QT_TRANSLATE_NOOP('prop','drawDashedLines')]: 'boolean'
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value', static displayType() {
expression = 'x', definitionDomain = 'RPE', destinationDomain = 'R', return qsTranslate("function", "Function")
displayMode = 'application', labelPosition = 'above', labelX = 1, }
static displayTypeMultiple() {
return qsTranslate("function", "Functions")
}
static properties() {
return {
[QT_TRANSLATE_NOOP("prop", "expression")]: new P.Expression("x"),
[QT_TRANSLATE_NOOP("prop", "definitionDomain")]: "Domain",
[QT_TRANSLATE_NOOP("prop", "destinationDomain")]: "Domain",
"comment1": QT_TRANSLATE_NOOP(
"comment",
"Ex: R+* (ℝ⁺*), N (), Z-* (ℤ⁻*), ]0;1[, {3;4;5}"
),
[QT_TRANSLATE_NOOP("prop", "displayMode")]: P.Enum.FunctionDisplayType,
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
[QT_TRANSLATE_NOOP("prop", "labelX")]: "number",
"comment2": QT_TRANSLATE_NOOP(
"comment",
"The following parameters are used when the definition domain is a non-continuous set. (Ex: , , sets like {0;3}...)"
),
[QT_TRANSLATE_NOOP("prop", "drawPoints")]: "boolean",
[QT_TRANSLATE_NOOP("prop", "drawDashedLines")]: "boolean"
}
}
constructor(name = null, visible = true, color = null, labelContent = "name + value",
expression = "x", definitionDomain = "RPE", destinationDomain = "R",
displayMode = "application", labelPosition = "above", labelX = 1,
drawPoints = true, drawDashedLines = true) { drawPoints = true, drawDashedLines = true) {
if(name == null) name = Objects.getNewName('fghjqlmnopqrstuvwabcde') if(name == null) name = Objects.getNewName("fghjqlmnopqrstuvwabcde")
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
if(typeof expression == 'number' || typeof expression == 'string') expression = new Expression(expression.toString()) if(typeof expression == "number" || typeof expression == "string") expression = new Expression(expression.toString())
this.expression = expression this.expression = expression
if(typeof definitionDomain == 'string') definitionDomain = parseDomain(definitionDomain) if(typeof definitionDomain == "string") definitionDomain = parseDomain(definitionDomain)
this.definitionDomain = definitionDomain this.definitionDomain = definitionDomain
if(typeof destinationDomain == 'string') destinationDomain = parseDomain(destinationDomain) if(typeof destinationDomain == "string") destinationDomain = parseDomain(destinationDomain)
this.destinationDomain = destinationDomain this.destinationDomain = destinationDomain
this.displayMode = displayMode this.displayMode = displayMode
this.labelPosition = labelPosition this.labelPosition = labelPosition
@ -67,15 +78,15 @@ export default class Function extends ExecutableObject {
} }
getReadableString() { getReadableString() {
if(this.displayMode === 'application') { if(this.displayMode === "application") {
return `${this.name}: ${this.definitionDomain}${this.destinationDomain}\n ${' '.repeat(this.name.length)}x ⟼ ${this.expression.toString()}` return `${this.name}: ${this.definitionDomain}${this.destinationDomain}\n ${" ".repeat(this.name.length)}x ⟼ ${this.expression.toString()}`
} else { } else {
return `${this.name}(x) = ${this.expression.toString()}\nD${textsub(this.name)} = ${this.definitionDomain}` return `${this.name}(x) = ${this.expression.toString()}\nD${textsub(this.name)} = ${this.definitionDomain}`
} }
} }
getLatexString() { getLatexString() {
if(this.displayMode === 'application') { if(this.displayMode === "application") {
return `${Latex.variable(this.name)}:\\begin{array}{llll}${this.definitionDomain.latexMarkup}\\textrm{ } & \\rightarrow & \\textrm{ }${this.destinationDomain.latexMarkup}\\\\ return `${Latex.variable(this.name)}:\\begin{array}{llll}${this.definitionDomain.latexMarkup}\\textrm{ } & \\rightarrow & \\textrm{ }${this.destinationDomain.latexMarkup}\\\\
x\\textrm{ } & \\mapsto & \\textrm{ }${this.expression.latexMarkup}\\end{array}` x\\textrm{ } & \\mapsto & \\textrm{ }${this.expression.latexMarkup}\\end{array}`
} else { } else {
@ -96,7 +107,7 @@ export default class Function extends ExecutableObject {
simplify(x = 1) { simplify(x = 1) {
if(this.definitionDomain.includes(x)) if(this.definitionDomain.includes(x))
return this.expression.simplify(x) return this.expression.simplify(x)
return '' return ""
} }
draw(canvas) { draw(canvas) {
@ -112,7 +123,7 @@ export default class Function extends ExecutableObject {
static drawFunction(canvas, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) { static drawFunction(canvas, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) {
let pxprecision = 10 let pxprecision = 10
let previousX = canvas.px2x(0) let previousX = canvas.px2x(0)
let previousY = null; let previousY = null
if(definitionDomain instanceof SpecialDomain && definitionDomain.moveSupported) { if(definitionDomain instanceof SpecialDomain && definitionDomain.moveSupported) {
// Point based functions. // Point based functions.
previousX = definitionDomain.next(previousX) previousX = definitionDomain.next(previousX)
@ -121,16 +132,16 @@ export default class Function extends ExecutableObject {
if(!drawPoints && !drawDash) return if(!drawPoints && !drawDash) return
while(previousX !== null && canvas.x2px(previousX) < canvas.width) { while(previousX !== null && canvas.x2px(previousX) < canvas.width) {
// Reconverted for canvas to fix for logarithmic scales. // Reconverted for canvas to fix for logarithmic scales.
let currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX)+pxprecision)); let currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX) + pxprecision))
let currentY = expr.execute(currentX) let currentY = expr.execute(currentX)
if(currentX === null) break; if(currentX === null) break
if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) && if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) &&
(destinationDomain.includes(currentY) || destinationDomain.includes(previousY))) { (destinationDomain.includes(currentY) || destinationDomain.includes(previousY))) {
if(drawDash) if(drawDash)
canvas.drawDashedLine(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) { if(drawPoints) {
canvas.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2) canvas.fillRect(canvas.x2px(previousX) - 5, canvas.y2px(previousY) - 1, 10, 2)
canvas.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10) canvas.fillRect(canvas.x2px(previousX) - 1, canvas.y2px(previousY) - 5, 2, 10)
} }
} }
previousX = currentX previousX = currentX
@ -138,8 +149,8 @@ export default class Function extends ExecutableObject {
} }
if(drawPoints) { if(drawPoints) {
// Drawing the last cross // Drawing the last cross
canvas.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2) canvas.fillRect(canvas.x2px(previousX) - 5, canvas.y2px(previousY) - 1, 10, 2)
canvas.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10) canvas.fillRect(canvas.x2px(previousX) - 1, canvas.y2px(previousY) - 5, 2, 10)
} }
} else { } else {
// Use max precision if function is trigonometrical on log scale. // Use max precision if function is trigonometrical on log scale.
@ -154,9 +165,9 @@ export default class Function extends ExecutableObject {
if(!definitionDomain.includes(previousX) && definitionDomain.includes(currentX)) { if(!definitionDomain.includes(previousX) && definitionDomain.includes(currentX)) {
// Should draw up to currentX, but NOT at previousX. // Should draw up to currentX, but NOT at previousX.
// Need to find the starting point. // Need to find the starting point.
let tmpPx = px-pxprecision let tmpPx = px - pxprecision
do { do {
tmpPx++; tmpPx++
previousX = canvas.px2x(tmpPx) previousX = canvas.px2x(tmpPx)
} while(!definitionDomain.includes(previousX)) } while(!definitionDomain.includes(previousX))
// Recaclulate previousY // Recaclulate previousY
@ -166,16 +177,16 @@ export default class Function extends ExecutableObject {
// Augmenting the pixel precision until this condition is fulfilled. // Augmenting the pixel precision until this condition is fulfilled.
let tmpPx = px let tmpPx = px
do { do {
tmpPx--; tmpPx--
currentX = canvas.px2x(tmpPx) currentX = canvas.px2x(tmpPx)
} while(!definitionDomain.includes(currentX) && currentX !== previousX) } while(!definitionDomain.includes(currentX) && currentX !== previousX)
} }
// This max variation is needed for functions with asymptotical vertical lines (e.g. 1/x, tan x...) // 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.height)) let maxvariation = (canvas.px2y(0) - canvas.px2y(canvas.height))
if(definitionDomain.includes(previousX) && definitionDomain.includes(currentX)) { if(definitionDomain.includes(previousX) && definitionDomain.includes(currentX)) {
let currentY = expr.execute(currentX) let currentY = expr.execute(currentX)
if(destinationDomain.includes(currentY)) { if(destinationDomain.includes(currentY)) {
if(previousY != null && destinationDomain.includes(previousY) && Math.abs(previousY-currentY) < maxvariation) { if(previousY != null && destinationDomain.includes(previousY) && Math.abs(previousY - currentY) < maxvariation) {
canvas.drawLine(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))
} }
} }

View file

@ -25,24 +25,34 @@ import { DrawableObject } from "./common.mjs"
export default class Point extends DrawableObject { export default class Point extends DrawableObject {
static type(){return 'Point'} static type() {
static displayType(){return qsTranslate("point", 'Point')} return "Point"
static displayTypeMultiple(){return qsTranslate("point", 'Points')} }
static properties() {return { static displayType() {
[QT_TRANSLATE_NOOP('prop','x')]: new P.Expression(), return qsTranslate("point", "Point")
[QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(), }
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
[QT_TRANSLATE_NOOP('prop','pointStyle')]: new P.Enum('●', '✕', '')
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value', static displayTypeMultiple() {
x = 1, y = 0, labelPosition = 'above', pointStyle = '●') { return qsTranslate("point", "Points")
if(name == null) name = Objects.getNewName('ABCDEFJKLMNOPQRSTUVW') }
static properties() {
return {
[QT_TRANSLATE_NOOP("prop", "x")]: new P.Expression(),
[QT_TRANSLATE_NOOP("prop", "y")]: new P.Expression(),
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
[QT_TRANSLATE_NOOP("prop", "pointStyle")]: new P.Enum("●", "✕", "")
}
}
constructor(name = null, visible = true, color = null, labelContent = "name + value",
x = 1, y = 0, labelPosition = "above", pointStyle = "●") {
if(name == null) name = Objects.getNewName("ABCDEFJKLMNOPQRSTUVW")
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
if(typeof x == 'number' || typeof x == 'string') x = new Expression(x.toString()) if(typeof x == "number" || typeof x == "string") x = new Expression(x.toString())
this.x = x this.x = x
if(typeof y == 'number' || typeof y == 'string') y = new Expression(y.toString()) if(typeof y == "number" || typeof y == "string") y = new Expression(y.toString())
this.y = y this.y = y
this.labelPosition = labelPosition this.labelPosition = labelPosition
this.pointStyle = pointStyle this.pointStyle = pointStyle
@ -58,19 +68,19 @@ export default class Point extends DrawableObject {
draw(canvas) { draw(canvas) {
let [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())] let [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())]
let pointSize = 8+(canvas.linewidth*2) let pointSize = 8 + (canvas.linewidth * 2)
switch(this.pointStyle) { switch(this.pointStyle) {
case '●': case "●":
canvas.disc(canvasX, canvasY, pointSize/2) canvas.disc(canvasX, canvasY, pointSize / 2)
break; break
case '✕': case "✕":
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)
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; break
case '': case "":
canvas.fillRect(canvasX-pointSize/2, canvasY-1, pointSize, 2) canvas.fillRect(canvasX - pointSize / 2, canvasY - 1, pointSize, 2)
canvas.fillRect(canvasX-1, canvasY-pointSize/2, 2, pointSize) canvas.fillRect(canvasX - 1, canvasY - pointSize / 2, 2, pointSize)
break; break
} }
this.drawLabel(canvas, this.labelPosition, canvasX, canvasY) this.drawLabel(canvas, this.labelPosition, canvasX, canvasY)
} }

View file

@ -26,27 +26,37 @@ import Function from "./function.mjs"
export default class Sequence extends ExecutableObject { export default class Sequence extends ExecutableObject {
static type(){return 'Sequence'} static type() {
static displayType(){return qsTranslate("sequence", 'Sequence')} return "Sequence"
static displayTypeMultiple(){return qsTranslate("sequence", 'Sequences')} }
static properties() {return { static displayType() {
[QT_TRANSLATE_NOOP('prop','drawPoints')]: 'boolean', return qsTranslate("sequence", "Sequence")
[QT_TRANSLATE_NOOP('prop','drawDashedLines')]: 'boolean', }
[QT_TRANSLATE_NOOP('prop','defaultExpression')]: new P.Dictionary('string', 'int', /^.+$/, /^\d+$/, '{name}[n+', '] = ', true),
'comment1': QT_TRANSLATE_NOOP( static displayTypeMultiple() {
'comment', return qsTranslate("sequence", "Sequences")
'Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁...' }
static properties() {
return {
[QT_TRANSLATE_NOOP("prop", "drawPoints")]: "boolean",
[QT_TRANSLATE_NOOP("prop", "drawDashedLines")]: "boolean",
[QT_TRANSLATE_NOOP("prop", "defaultExpression")]: new P.Dictionary("string", "int", /^.+$/, /^\d+$/, "{name}[n+", "] = ", true),
"comment1": QT_TRANSLATE_NOOP(
"comment",
"Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁..."
), ),
[QT_TRANSLATE_NOOP('prop','baseValues')]: new P.Dictionary('string', 'int', /^.+$/, /^\d+$/, '{name}[', '] = '), [QT_TRANSLATE_NOOP("prop", "baseValues")]: new P.Dictionary("string", "int", /^.+$/, /^\d+$/, "{name}[", "] = "),
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, [QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number', [QT_TRANSLATE_NOOP("prop", "labelX")]: "number"
}} }
}
constructor(name = null, visible = true, color = null, labelContent = 'name + value', constructor(name = null, visible = true, color = null, labelContent = "name + value",
drawPoints = true, drawDashedLines = true, defaultExp = {1: "n"}, drawPoints = true, drawDashedLines = true, defaultExp = { 1: "n" },
baseValues = {0: 0}, labelPosition = 'above', labelX = 1) { baseValues = { 0: 0 }, labelPosition = "above", labelX = 1) {
if(name == null) name = Objects.getNewName('uvwPSUVWabcde') if(name == null) name = Objects.getNewName("uvwPSUVWabcde")
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
this.drawPoints = drawPoints this.drawPoints = drawPoints
this.drawDashedLines = drawDashedLines this.drawDashedLines = drawDashedLines
@ -58,17 +68,17 @@ export default class Sequence extends ExecutableObject {
} }
update() { update() {
console.log('Updating sequence', this.sequence) console.log("Updating sequence", this.sequence)
super.update() super.update()
if( if(
this.sequence == null || this.baseValues !== this.sequence.baseValues || this.sequence == null || this.baseValues !== this.sequence.baseValues ||
this.sequence.name !== this.name || this.sequence.name !== this.name ||
this.sequence.expr !== Object.values(this.defaultExpression)[0] || this.sequence.expr !== Object.values(this.defaultExpression)[0] ||
this.sequence.valuePlus !== Object.keys(this.defaultExpression)[0] this.sequence.valuePlus.toString() !== Object.keys(this.defaultExpression)[0]
) )
this.sequence = new MathSequence( this.sequence = new MathSequence(
this.name, this.baseValues, this.name, this.baseValues,
Object.keys(this.defaultExpression)[0], parseFloat(Object.keys(this.defaultExpression)[0]),
Object.values(this.defaultExpression)[0] Object.values(this.defaultExpression)[0]
) )
} }
@ -86,7 +96,10 @@ export default class Sequence extends ExecutableObject {
return this.sequence.execute(x) return this.sequence.execute(x)
return null return null
} }
canExecute(x = 1) {return x%1 === 0}
canExecute(x = 1) {
return x % 1 === 0
}
// Simplify returns the simplified string of the expression. // Simplify returns the simplified string of the expression.
simplify(x = 1) { simplify(x = 1) {
@ -97,23 +110,23 @@ export default class Sequence extends ExecutableObject {
getLabel() { getLabel() {
switch(this.labelContent) { switch(this.labelContent) {
case 'name': case "name":
return `(${this.name}ₙ)` return `(${this.name}ₙ)`
case 'name + value': case "name + value":
return this.getReadableString() return this.getReadableString()
case 'null': case "null":
return '' return ""
} }
} }
getLatexLabel() { getLatexLabel() {
switch(this.labelContent) { switch(this.labelContent) {
case 'name': case "name":
return `(${Latex.variable(this.name)}_n)` return `(${Latex.variable(this.name)}_n)`
case 'name + value': case "name + value":
return this.getLatexString() return this.getLatexString()
case 'null': case "null":
return '' return ""
} }
} }

View file

@ -25,28 +25,39 @@ import { DrawableObject } from "./common.mjs"
export default class Text extends DrawableObject { export default class Text extends DrawableObject {
static type(){return 'Text'} static type() {
static displayType(){return qsTranslate("text", 'Text')} return "Text"
static displayTypeMultiple(){return qsTranslate("text", 'Texts')} }
static properties() {return {
[QT_TRANSLATE_NOOP('prop','x')]: new P.Expression(),
[QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(),
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning,
[QT_TRANSLATE_NOOP('prop','text')]: 'string',
'comment1': QT_TRANSLATE_NOOP(
'comment',
'If you have latex enabled, you can use use latex markup in between $$ to create equations.'
),
[QT_TRANSLATE_NOOP('prop','disableLatex')]: 'boolean'
}}
constructor(name = null, visible = true, color = null, labelContent = 'null', static displayType() {
x = 1, y = 0, labelPosition = 'center', text = 'New text', disableLatex = false) { return qsTranslate("text", "Text")
if(name == null) name = Objects.getNewName('t') }
static displayTypeMultiple() {
return qsTranslate("text", "Texts")
}
static properties() {
return {
[QT_TRANSLATE_NOOP("prop", "x")]: new P.Expression(),
[QT_TRANSLATE_NOOP("prop", "y")]: new P.Expression(),
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Positioning,
[QT_TRANSLATE_NOOP("prop", "text")]: "string",
"comment1": QT_TRANSLATE_NOOP(
"comment",
"If you have latex enabled, you can use use latex markup in between $$ to create equations."
),
[QT_TRANSLATE_NOOP("prop", "disableLatex")]: "boolean"
}
}
constructor(name = null, visible = true, color = null, labelContent = "null",
x = 1, y = 0, labelPosition = "center", text = "New text", disableLatex = false) {
if(name == null) name = Objects.getNewName("t")
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
if(typeof x == 'number' || typeof x == 'string') x = new Expression(x.toString()) if(typeof x == "number" || typeof x == "string") x = new Expression(x.toString())
this.x = x this.x = x
if(typeof y == 'number' || typeof y == 'string') y = new Expression(y.toString()) if(typeof y == "number" || typeof y == "string") y = new Expression(y.toString())
this.y = y this.y = y
this.labelPosition = labelPosition this.labelPosition = labelPosition
this.text = text this.text = text
@ -60,15 +71,17 @@ export default class Text extends DrawableObject {
latexMarkupText() { latexMarkupText() {
// Check whether the text contains latex escaped elements. // Check whether the text contains latex escaped elements.
let txt = [] let txt = []
this.text.split('$$').forEach(function(t) { txt = txt.concat(Latex.variable(t, true).replace(/\$\$/g, '').split('$')) }) this.text.split("$$").forEach(function(t) {
txt = txt.concat(Latex.variable(t, true).replace(/\$\$/g, "").split("$"))
})
let newTxt = txt[0] let newTxt = txt[0]
let i let i
// Split between normal text and latex escaped. // Split between normal text and latex escaped.
for(i = 0; i < txt.length-1; i++) for(i = 0; i < txt.length - 1; i++)
if(i & 0x01) // Every odd number if(i & 0x01) // Every odd number
newTxt += '\\textsf{'+Latex.variable(txt[i+1]) newTxt += "\\textsf{" + Latex.variable(txt[i + 1])
else else
newTxt += '}'+txt[i+1] newTxt += "}" + txt[i + 1]
// Finished by a } // Finished by a }
if(i & 0x01) if(i & 0x01)
newTxt += "{" newTxt += "{"
@ -88,8 +101,8 @@ export default class Text extends DrawableObject {
} }
draw(canvas) { draw(canvas) {
let yOffset = this.disableLatex ? canvas.textsize-4 : 0 let yOffset = this.disableLatex ? canvas.textsize - 4 : 0
this.drawLabel(canvas, 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)
} }
} }

View file

@ -73,29 +73,28 @@ export default class XCursor extends DrawableObject {
\\end{array}` \\end{array}`
} }
getTargetValueLabel() { getApprox() {
var t = this.targetElement let approx = ''
var approx = ''
if(this.approximate) { if(this.approximate) {
approx = (t.execute(this.x.execute())) approx = (this.targetElement.execute(this.x.execute()))
let intLength = Math.round(approx).toString().length let intLength = Math.round(approx).toString().length
let rounding = Math.min(this.rounding, approx.toString().length - intLength - 1) let rounding = Math.min(this.rounding, approx.toString().length - intLength - 1)
approx = approx.toPrecision(rounding + intLength) approx = approx.toPrecision(rounding + intLength)
} }
return approx
}
getTargetValueLabel() {
const t = this.targetElement
const approx = this.getApprox()
return `${t.name}(${this.name}) = ${t.simplify(this.x.toEditableString())}` + return `${t.name}(${this.name}) = ${t.simplify(this.x.toEditableString())}` +
(this.approximate ? ' ≈ ' + approx : '') (this.approximate ? ' ≈ ' + approx : '')
} }
getTargetValueLatexLabel() { getTargetValueLatexLabel() {
let t = this.targetElement const t = this.targetElement
let approx = '' const approx = this.getApprox()
if(this.approximate) { const simpl = t.simplify(this.x.toEditableString())
approx = (t.execute(this.x.execute()))
let intLength = Math.round(approx).toString().length
let rounding = Math.min(this.rounding, approx.toString().length - intLength - 1)
approx = approx.toPrecision(rounding + intLength)
}
let simpl = t.simplify(this.x.toEditableString())
return `${Latex.variable(t.name)}(${Latex.variable(this.name)}) = ${simpl.latexMarkup ? simpl.latexMarkup : Latex.variable(simpl)}` + return `${Latex.variable(t.name)}(${Latex.variable(this.name)}) = ${simpl.latexMarkup ? simpl.latexMarkup : Latex.variable(simpl)}` +
(this.approximate ? ' \\simeq ' + approx : '') (this.approximate ? ' \\simeq ' + approx : '')
} }
@ -104,16 +103,13 @@ export default class XCursor extends DrawableObject {
switch(this.labelContent) { switch(this.labelContent) {
case 'name': case 'name':
return this.name return this.name
break;
case 'name + value': case 'name + value':
switch(this.targetValuePosition) { switch(this.targetValuePosition) {
case 'Next to target': case 'Next to target':
case 'Hidden': case 'Hidden':
return `${this.name} = ${this.x.toString()}` return `${this.name} = ${this.x.toString()}`
break;
case 'With label': case 'With label':
return this.getReadableString() return this.getReadableString()
break;
} }
case 'null': case 'null':
return '' return ''
@ -124,16 +120,13 @@ export default class XCursor extends DrawableObject {
switch(this.labelContent) { switch(this.labelContent) {
case 'name': case 'name':
return Latex.variable(this.name) return Latex.variable(this.name)
break;
case 'name + value': case 'name + value':
switch(this.targetValuePosition) { switch(this.targetValuePosition) {
case 'Next to target': case 'Next to target':
case 'Hidden': case 'Hidden':
return `${Latex.variable(this.name)} = ${this.x.latexMarkup}` return `${Latex.variable(this.name)} = ${this.x.latexMarkup}`
break;
case 'With label': case 'With label':
return this.getLatexString() return this.getLatexString()
break;
} }
case 'null': case 'null':
return '' return ''

View file

@ -15,15 +15,16 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* 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 {parseDomain, Expression as Expr, Domain} from "./math/index.mjs" import { parseDomain, Expression as Expr, Domain } from "./math/index.mjs"
import Objects from "./module/objects.mjs" import Objects from "./module/objects.mjs"
const NONE = class Empty {} const NONE = class Empty {
}
let stringValuesValidators = { let stringValuesValidators = {
'int': [parseInt, (x) => !isNaN(x)], "int": [parseInt, (x) => !isNaN(x)],
'double': [parseFloat, (x) => !isNaN(x)], "double": [parseFloat, (x) => !isNaN(x)],
'string': [(x) => x, () => true] "string": [(x) => x, () => true]
} }
let stringValidatorTypes = Object.keys(stringValuesValidators) let stringValidatorTypes = Object.keys(stringValuesValidators)
@ -52,17 +53,17 @@ class PropertyType {
export class Expression extends PropertyType { export class Expression extends PropertyType {
constructor(...variables) { constructor(...variables) {
super() super()
this.type = 'Expression' this.type = "Expression"
this.variables = variables this.variables = variables
} }
toString() { toString() {
return this.variables.length === 0 ? 'Number' : `Expression(${this.variables.join(', ')})` return this.variables.length === 0 ? "Number" : `Expression(${this.variables.join(", ")})`
} }
parse(value) { parse(value) {
let result = NONE let result = NONE
if(typeof value == 'string') if(typeof value == "string")
try { try {
result = new Expr(value) result = new Expr(value)
} catch(e) { } catch(e) {
@ -86,14 +87,14 @@ export class Expression extends PropertyType {
export class Enum extends PropertyType { export class Enum extends PropertyType {
constructor(...values) { constructor(...values) {
super() super()
this.type = 'Enum' this.type = "Enum"
this.values = values this.values = values
this.legacyValues = {} this.legacyValues = {}
this.translatedValues = values.map(x => qsTranslate('parameters', x)) this.translatedValues = values.map(x => qsTranslate("parameters", x))
} }
toString() { toString() {
return `${this.type}(${this.values.join(', ')})` return `${this.type}(${this.values.join(", ")})`
} }
parse(value) { parse(value) {
@ -111,15 +112,15 @@ export class Enum extends PropertyType {
else if(this.legacyValues[value]) else if(this.legacyValues[value])
return this.legacyValues[value] return this.legacyValues[value]
else else
throw new TypeError(`Exportation error: ${value} not one of ${this.values.join(', ')}.`) throw new TypeError(`Exportation error: ${value} not one of ${this.values.join(", ")}.`)
} }
} }
export class ObjectType extends PropertyType { export class ObjectType extends PropertyType {
constructor(objType, allowNull=false) { constructor(objType, allowNull = false) {
super() super()
this.type = 'ObjectType' this.type = "ObjectType"
this.objType = objType this.objType = objType
this.allowNull = allowNull this.allowNull = allowNull
} }
@ -130,9 +131,9 @@ export class ObjectType extends PropertyType {
parse(name) { parse(name) {
let result = NONE let result = NONE
if(typeof name == 'string' && name in Objects.currentObjectsByName) { if(typeof name == "string" && name in Objects.currentObjectsByName) {
let obj = Objects.currentObjectsByName[name] let obj = Objects.currentObjectsByName[name]
if (obj.type === this.objType || (this.objType === 'ExecutableObject' && obj.execute)) { if(obj.type === this.objType || (this.objType === "ExecutableObject" && obj.execute)) {
result = obj result = obj
} else { } else {
// Silently error and return null // Silently error and return null
@ -147,7 +148,7 @@ export class ObjectType extends PropertyType {
export(value) { export(value) {
if(value == null && this.allowNull) if(value == null && this.allowNull)
return null return null
else if(value.type === this.objType || (this.objType === 'ExecutableObject' && value.execute)) else if(value.type === this.objType || (this.objType === "ExecutableObject" && value.execute))
return value.name return value.name
else else
throw new TypeError(`Exportation error: ${value} not a ${this.objType}.`) throw new TypeError(`Exportation error: ${value} not a ${this.objType}.`)
@ -156,13 +157,13 @@ export class ObjectType extends PropertyType {
export class List extends PropertyType { export class List extends PropertyType {
constructor(type, format = /^.+$/, label = '', forbidAdding = false) { constructor(type, format = /^.+$/, label = "", forbidAdding = false) {
super() super()
// type can be string, int and double. // type can be string, int and double.
this.type = 'List' this.type = "List"
this.valueType = type this.valueType = type
if(!stringValidatorTypes.includes(this.valueType)) if(!stringValidatorTypes.includes(this.valueType))
throw new TypeError(`${this.valueType} must be one of ${stringValidatorTypes.join(', ')}.`) throw new TypeError(`${this.valueType} must be one of ${stringValidatorTypes.join(", ")}.`)
this.format = format this.format = format
this.label = label this.label = label
this.forbidAdding = forbidAdding this.forbidAdding = forbidAdding
@ -174,10 +175,10 @@ export class List extends PropertyType {
parse(value) { parse(value) {
let result = NONE let result = NONE
if(typeof value == 'object' && value.__proto__ === Array) { if(typeof value == "object" && value.__proto__ === Array) {
let valid = 0 let valid = 0
for(let v of value) { for(let v of value) {
if (this.format.test(v)) { if(this.format.test(v)) {
v = stringValuesValidators[this.valueType][0](v) v = stringValuesValidators[this.valueType][0](v)
if(stringValuesValidators[this.valueType][1](v)) if(stringValuesValidators[this.valueType][1](v))
valid++ valid++
@ -190,7 +191,7 @@ export class List extends PropertyType {
} }
export(value) { export(value) {
if(typeof value == 'object' && value.__proto__ === Array) if(typeof value == "object" && value.__proto__ === Array)
return value return value
else else
throw new TypeError(`Exportation error: ${value} not a list.`) throw new TypeError(`Exportation error: ${value} not a list.`)
@ -200,10 +201,10 @@ export class List extends PropertyType {
export class Dictionary extends PropertyType { export class Dictionary extends PropertyType {
constructor(type, keyType = 'string', format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = '', postKeyLabel = ': ', forbidAdding = false) { constructor(type, keyType = "string", format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = "", postKeyLabel = ": ", forbidAdding = false) {
super() super()
// type & keyType can be string, int and double. // type & keyType can be string, int and double.
this.type = 'Dict' this.type = "Dict"
this.valueType = type this.valueType = type
this.keyType = keyType this.keyType = keyType
this.format = format this.format = format
@ -219,10 +220,10 @@ export class Dictionary extends PropertyType {
parse(value) { parse(value) {
let result = NONE let result = NONE
if(typeof value == 'object' && value.__proto__ !== Array) { if(typeof value == "object" && value.__proto__ !== Array) {
let dict = [] let dict = []
for(let [k, v] of Object.entries(value)) { for(let [k, v] of Object.entries(value)) {
if (this.format.test(v) && this.keyFormat.test(k)) { if(this.format.test(v) && this.keyFormat.test(k)) {
k = stringValuesValidators[this.keyType][0](k) k = stringValuesValidators[this.keyType][0](k)
v = stringValuesValidators[this.valueType][0](v) v = stringValuesValidators[this.valueType][0](v)
if(stringValuesValidators[this.keyType][1](k)) if(stringValuesValidators[this.keyType][1](k))
@ -237,7 +238,7 @@ export class Dictionary extends PropertyType {
} }
export(value) { export(value) {
if(typeof value == 'object' && value.__proto__ !== Array) if(typeof value == "object" && value.__proto__ !== Array)
return value return value
else else
throw new TypeError(`Exportation error: ${value} not a dictionary.`) throw new TypeError(`Exportation error: ${value} not a dictionary.`)
@ -247,51 +248,51 @@ export class Dictionary extends PropertyType {
// Common parameters for Enums // Common parameters for Enums
Enum.Position = new Enum( Enum.Position = new Enum(
QT_TRANSLATE_NOOP('parameters', 'above'), QT_TRANSLATE_NOOP("parameters", "above"),
QT_TRANSLATE_NOOP('parameters', 'below'), QT_TRANSLATE_NOOP("parameters", "below"),
QT_TRANSLATE_NOOP('parameters', 'left'), QT_TRANSLATE_NOOP("parameters", "left"),
QT_TRANSLATE_NOOP('parameters', 'right'), QT_TRANSLATE_NOOP("parameters", "right"),
QT_TRANSLATE_NOOP('parameters', 'above-left'), QT_TRANSLATE_NOOP("parameters", "above-left"),
QT_TRANSLATE_NOOP('parameters', 'above-right'), QT_TRANSLATE_NOOP("parameters", "above-right"),
QT_TRANSLATE_NOOP('parameters', 'below-left'), QT_TRANSLATE_NOOP("parameters", "below-left"),
QT_TRANSLATE_NOOP('parameters', 'below-right') QT_TRANSLATE_NOOP("parameters", "below-right")
) )
Enum.Position.legacyValues = { Enum.Position.legacyValues = {
'top': 'above', "top": "above",
'bottom': 'below', "bottom": "below",
'top-left': 'above-left', "top-left": "above-left",
'top-right': 'above-right', "top-right": "above-right",
'bottom-left': 'below-left', "bottom-left": "below-left",
'bottom-right': 'below-right', "bottom-right": "below-right"
} }
Enum.Positioning = new Enum( Enum.Positioning = new Enum(
QT_TRANSLATE_NOOP('parameters', 'center'), QT_TRANSLATE_NOOP("parameters", "center"),
QT_TRANSLATE_NOOP('parameters', 'top'), QT_TRANSLATE_NOOP("parameters", "top"),
QT_TRANSLATE_NOOP('parameters', 'bottom'), QT_TRANSLATE_NOOP("parameters", "bottom"),
QT_TRANSLATE_NOOP('parameters', 'left'), QT_TRANSLATE_NOOP("parameters", "left"),
QT_TRANSLATE_NOOP('parameters', 'right'), QT_TRANSLATE_NOOP("parameters", "right"),
QT_TRANSLATE_NOOP('parameters', 'top-left'), QT_TRANSLATE_NOOP("parameters", "top-left"),
QT_TRANSLATE_NOOP('parameters', 'top-right'), QT_TRANSLATE_NOOP("parameters", "top-right"),
QT_TRANSLATE_NOOP('parameters', 'bottom-left'), QT_TRANSLATE_NOOP("parameters", "bottom-left"),
QT_TRANSLATE_NOOP('parameters', 'bottom-right') QT_TRANSLATE_NOOP("parameters", "bottom-right")
) )
Enum.FunctionDisplayType = new Enum( Enum.FunctionDisplayType = new Enum(
QT_TRANSLATE_NOOP('parameters', 'application'), QT_TRANSLATE_NOOP("parameters", "application"),
QT_TRANSLATE_NOOP('parameters', 'function') QT_TRANSLATE_NOOP("parameters", "function")
) )
Enum.BodePass = new Enum( Enum.BodePass = new Enum(
QT_TRANSLATE_NOOP('parameters', 'high'), QT_TRANSLATE_NOOP("parameters", "high"),
QT_TRANSLATE_NOOP('parameters', 'low') QT_TRANSLATE_NOOP("parameters", "low")
) )
Enum.XCursorValuePosition = new Enum( Enum.XCursorValuePosition = new Enum(
QT_TRANSLATE_NOOP('parameters', 'Next to target'), QT_TRANSLATE_NOOP("parameters", "Next to target"),
QT_TRANSLATE_NOOP('parameters', 'With label'), QT_TRANSLATE_NOOP("parameters", "With label"),
QT_TRANSLATE_NOOP('parameters', 'Hidden') QT_TRANSLATE_NOOP("parameters", "Hidden")
) )
/** /**
@ -303,23 +304,23 @@ Enum.XCursorValuePosition = new Enum(
export function ensureTypeSafety(propertyType, value) { export function ensureTypeSafety(propertyType, value) {
let result let result
let error = false let error = false
if(typeof propertyType == 'string') if(typeof propertyType == "string")
switch(propertyType) { switch(propertyType) {
case 'string': case "string":
result = value result = value
error = typeof value !== 'string' error = typeof value !== "string"
break break
case 'number': case "number":
result = parseFloat(value) result = parseFloat(value)
error = isNaN(result) error = isNaN(result)
break break
case 'boolean': case "boolean":
result = value result = value
error = value !== true && value !== false error = value !== true && value !== false
break break
case 'Domain': case "Domain":
try { try {
error = typeof value !== 'string' error = typeof value !== "string"
if(!error) if(!error)
result = parseDomain(value) result = parseDomain(value)
} catch(e) { } catch(e) {
@ -353,18 +354,18 @@ export function ensureTypeSafety(propertyType, value) {
*/ */
export function serializesByPropertyType(propertyType, value) { export function serializesByPropertyType(propertyType, value) {
let result let result
if(typeof propertyType == 'string') if(typeof propertyType == "string")
switch(propertyType) { switch(propertyType) {
case 'string': case "string":
result = value.toString() result = value.toString()
break break
case 'number': case "number":
result = parseFloat(value) result = parseFloat(value)
break break
case 'boolean': case "boolean":
result = value === true result = value === true
break break
case 'Domain': case "Domain":
if(value instanceof Domain) if(value instanceof Domain)
result = value.toString() result = value.toString()
else else

View file

@ -19,28 +19,28 @@
export default class InputExpression { export default class InputExpression {
constructor(expression) { constructor(expression) {
this.position = 0; this.position = 0
this.input = expression; this.input = expression
} }
next() { next() {
return this.input[this.position++]; return this.input[this.position++]
} }
peek() { peek() {
return this.input[this.position]; return this.input[this.position]
} }
skip(char) { skip(char) {
if(!this.atEnd() && this.peek() === char) { if(!this.atEnd() && this.peek() === char) {
this.position++; this.position++
} else { } else {
this.raise("Unexpected character " + this.peek() + ". Expected character " + char); this.raise("Unexpected character " + this.peek() + ". Expected character " + char)
} }
} }
atEnd() { atEnd() {
return this.position >= this.input.length; return this.position >= this.input.length
} }
raise(message) { raise(message) {

View file

@ -25,8 +25,8 @@ export const CONSTANTS = {
"infinity": Infinity, "infinity": Infinity,
"∞": Infinity, "∞": Infinity,
"e": Math.E "e": Math.E
}; }
export const CONSTANTS_LIST = Object.keys(CONSTANTS); export const CONSTANTS_LIST = Object.keys(CONSTANTS)
export const FUNCTIONS = { export const FUNCTIONS = {
// The functions commented are the one either not implemented // The functions commented are the one either not implemented
@ -36,66 +36,66 @@ export const FUNCTIONS = {
//'-': (x) => -x, //'-': (x) => -x,
//'!' //'!'
// Other operations // Other operations
'length': (s) => Array.isArray(s) ? s.length : String(s).length, "length": (s) => Array.isArray(s) ? s.length : String(s).length,
// Boolean functions // Boolean functions
'not': (x) => !x, "not": (x) => !x,
// Math functions // Math functions
'abs': Math.abs, "abs": Math.abs,
'acos': Math.acos, "acos": Math.acos,
'acosh': Math.acosh, "acosh": Math.acosh,
'asin': Math.asin, "asin": Math.asin,
'asinh': Math.asinh, "asinh": Math.asinh,
'atan': Math.atan, "atan": Math.atan,
'atan2': Math.atan2, "atan2": Math.atan2,
'atanh': Math.atanh, "atanh": Math.atanh,
'cbrt': Math.cbrt, "cbrt": Math.cbrt,
'ceil': Math.ceil, "ceil": Math.ceil,
//'clz32': Math.clz32, //'clz32': Math.clz32,
'cos': Math.cos, "cos": Math.cos,
'cosh': Math.cosh, "cosh": Math.cosh,
'exp': Math.exp, "exp": Math.exp,
'expm1': Math.expm1, "expm1": Math.expm1,
'floor': Math.floor, "floor": Math.floor,
//'fround': Math.fround, //'fround': Math.fround,
'hypot': Math.hypot, "hypot": Math.hypot,
//'imul': Math.imul, //'imul': Math.imul,
'lg': Math.log10, "lg": Math.log10,
'ln': Math.log, "ln": Math.log,
'log': Math.log, "log": Math.log,
'log10': Math.log10, "log10": Math.log10,
'log1p': Math.log1p, "log1p": Math.log1p,
'log2': Math.log2, "log2": Math.log2,
'max': Math.max, "max": Math.max,
'min': Math.min, "min": Math.min,
'pow': Math.log2, "pow": Math.log2,
'random': Math.random, "random": Math.random,
'round': Math.round, "round": Math.round,
'sign': Math.sign, "sign": Math.sign,
'sin': Math.sin, "sin": Math.sin,
'sinh': Math.sinh, "sinh": Math.sinh,
'sqrt': Math.sqrt, "sqrt": Math.sqrt,
'tan': Math.tan, "tan": Math.tan,
'tanh': Math.tanh, "tanh": Math.tanh,
'trunc': Math.trunc, "trunc": Math.trunc,
// Functions in expr-eval, ported here. // Functions in expr-eval, ported here.
'fac': Polyfill.factorial, "fac": Polyfill.factorial,
'gamma': Polyfill.gamma, "gamma": Polyfill.gamma,
'Γ': Polyfill.gamma, "Γ": Polyfill.gamma,
'roundTo': (x, exp) => Number(x).toFixed(exp), "roundTo": (x, exp) => Number(x).toFixed(exp),
// 'map': Polyfill.arrayMap, // 'map': Polyfill.arrayMap,
// 'fold': Polyfill.arrayFold, // 'fold': Polyfill.arrayFold,
// 'filter': Polyfill.arrayFilter, // 'filter': Polyfill.arrayFilter,
// 'indexOf': Polyfill.indexOf, // 'indexOf': Polyfill.indexOf,
// 'join': Polyfill.arrayJoin, // 'join': Polyfill.arrayJoin,
// Integral & derivative (only here for autocomplete). // Integral & derivative (only here for autocomplete).
'integral': () => 0, // TODO: Implement "integral": () => 0, // TODO: Implement
'derivative': () => 0, "derivative": () => 0
} }
export const FUNCTIONS_LIST = Object.keys(FUNCTIONS); export const FUNCTIONS_LIST = Object.keys(FUNCTIONS)
export class P { export class P {
// Parameter class. // Parameter class.
constructor(type, name = '', optional = false, multipleAllowed = false) { constructor(type, name = "", optional = false, multipleAllowed = false) {
this.name = name this.name = name
this.type = type this.type = type
this.optional = optional this.optional = optional
@ -104,10 +104,10 @@ export class P {
toString() { toString() {
let base_string = this.type let base_string = this.type
if(this.name !== '') if(this.name !== "")
base_string = `${this.name}: ${base_string}` base_string = `${this.name}: ${base_string}`
if(this.multipleAllowed) if(this.multipleAllowed)
base_string += '...' base_string += "..."
if(!this.optional) if(!this.optional)
base_string = `<${base_string}>` base_string = `<${base_string}>`
else else
@ -116,59 +116,59 @@ export class P {
} }
} }
export let string = new P('string') export let string = new P("string")
export let bool = new P('bool') export let bool = new P("bool")
export let number = new P('number') export let number = new P("number")
export let array = new P('array') export let array = new P("array")
export const FUNCTIONS_USAGE = { export const FUNCTIONS_USAGE = {
'length': [string], "length": [string],
'not': [bool], "not": [bool],
// Math functions // Math functions
'abs': [number], "abs": [number],
'acos': [number], "acos": [number],
'acosh': [number], "acosh": [number],
'asin': [number], "asin": [number],
'asinh': [number], "asinh": [number],
'atan': [number], "atan": [number],
'atan2': [number], "atan2": [number],
'atanh': [number], "atanh": [number],
'cbrt': [number], "cbrt": [number],
'ceil': [number], "ceil": [number],
//'clz32': [number], //'clz32': [number],
'cos': [number], "cos": [number],
'cosh': [number], "cosh": [number],
'exp': [number], "exp": [number],
'expm1': [number], "expm1": [number],
'floor': [number], "floor": [number],
//'fround': [number], //'fround': [number],
'hypot': [number], "hypot": [number],
//'imul': [number], //'imul': [number],
'lg': [number], "lg": [number],
'ln': [number], "ln": [number],
'log': [number], "log": [number],
'log10': [number], "log10": [number],
'log1p': [number], "log1p": [number],
'log2': [number], "log2": [number],
'max': [number, number, new P('numbers', '', true, true)], "max": [number, number, new P("numbers", "", true, true)],
'min': [number, number, new P('numbers', '', true, true)], "min": [number, number, new P("numbers", "", true, true)],
'pow': [number, new P('number', 'exp')], "pow": [number, new P("number", "exp")],
'random': [number, number], "random": [number, number],
'round': [number], "round": [number],
'sign': [number], "sign": [number],
'sin': [number], "sin": [number],
'sinh': [number], "sinh": [number],
'sqrt': [number], "sqrt": [number],
'tan': [number], "tan": [number],
'tanh': [number], "tanh": [number],
'trunc': [number], "trunc": [number],
// Functions in expr-eval, ported here. // Functions in expr-eval, ported here.
'fac': [number], "fac": [number],
'gamma': [number], "gamma": [number],
'Γ': [number], "Γ": [number],
'roundTo': [number, new P('number')], "roundTo": [number, new P("number")],
// Function manipulation // Function manipulation
'derivative': [new P('f'), new P('string', 'var', true), number], "derivative": [new P("f"), new P("string", "var", true), number],
'integral': [new P('from'), new P('to'), new P('f'), new P('string', 'var', true)], "integral": [new P("from"), new P("to"), new P("f"), new P("string", "var", true)]
} }

View file

@ -20,9 +20,9 @@
import * as Reference from "./reference.mjs" import * as Reference from "./reference.mjs"
const WHITESPACES = " \t\n\r" const WHITESPACES = " \t\n\r"
const STRING_LIMITERS = '"\'`'; const STRING_LIMITERS = "\"'`"
const OPERATORS = "+-*/^%?:=!><"; const OPERATORS = "+-*/^%?:=!><"
const PUNCTUATION = "()[]{},."; const PUNCTUATION = "()[]{},."
const NUMBER_CHARS = "0123456789" const NUMBER_CHARS = "0123456789"
const IDENTIFIER_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_₀₁₂₃₄₅₆₇₈₉αβγδεζηθκλμξρςστφχψωₐₑₒₓₔₕₖₗₘₙₚₛₜ" const IDENTIFIER_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_₀₁₂₃₄₅₆₇₈₉αβγδεζηθκλμξρςστφχψωₐₑₒₓₔₕₖₗₘₙₚₛₜ"
@ -41,8 +41,8 @@ export const TokenType = {
export class Token { export class Token {
constructor(type, value, startPosition) { constructor(type, value, startPosition) {
this.type = type; this.type = type
this.value = value; this.value = value
this.startPosition = startPosition this.startPosition = startPosition
} }
} }
@ -55,38 +55,38 @@ export class ExpressionTokenizer {
* @param {boolean} errorOnUnknown * @param {boolean} errorOnUnknown
*/ */
constructor(input, tokenizeWhitespaces = false, errorOnUnknown = true) { constructor(input, tokenizeWhitespaces = false, errorOnUnknown = true) {
this.input = input; this.input = input
this.currentToken = null; this.currentToken = null
this.tokenizeWhitespaces = tokenizeWhitespaces this.tokenizeWhitespaces = tokenizeWhitespaces
this.errorOnUnknown = errorOnUnknown this.errorOnUnknown = errorOnUnknown
} }
skipWhitespaces() { skipWhitespaces() {
while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek())) while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek()))
this.input.next(); this.input.next()
} }
readWhitespaces() { readWhitespaces() {
let included = ""; let included = ""
while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek())) { while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek())) {
included += this.input.next(); included += this.input.next()
} }
return new Token(TokenType.WHITESPACE, included, this.input.position-included.length) return new Token(TokenType.WHITESPACE, included, this.input.position - included.length)
} }
readString() { readString() {
let delimitation = this.input.peek(); let delimitation = this.input.peek()
if(STRING_LIMITERS.includes(delimitation)) { if(STRING_LIMITERS.includes(delimitation)) {
this.input.skip(delimitation) this.input.skip(delimitation)
let included = ""; let included = ""
let justEscaped = false; let justEscaped = false
while(!this.input.atEnd() && (!STRING_LIMITERS.includes(this.input.peek()) || justEscaped)) { while(!this.input.atEnd() && (!STRING_LIMITERS.includes(this.input.peek()) || justEscaped)) {
justEscaped = this.input.peek() === "\\" justEscaped = this.input.peek() === "\\"
if(!justEscaped) if(!justEscaped)
included += this.input.next(); included += this.input.next()
} }
this.input.skip(delimitation) this.input.skip(delimitation)
let token = new Token(TokenType.STRING, included, this.input.position-included.length) let token = new Token(TokenType.STRING, included, this.input.position - included.length)
token.limitator = delimitation token.limitator = delimitation
return token return token
} else { } else {
@ -95,80 +95,80 @@ export class ExpressionTokenizer {
} }
readNumber() { readNumber() {
let included = ""; let included = ""
let hasDot = false; let hasDot = false
while(!this.input.atEnd() && (NUMBER_CHARS.includes(this.input.peek()) || this.input.peek() === '.')) { while(!this.input.atEnd() && (NUMBER_CHARS.includes(this.input.peek()) || this.input.peek() === ".")) {
if(this.input.peek() === ".") { if(this.input.peek() === ".") {
if(hasDot) this.input.raise("Unexpected '.'. Expected digit") if(hasDot) this.input.raise("Unexpected '.'. Expected digit")
hasDot = true; hasDot = true
} }
included += this.input.next(); included += this.input.next()
} }
return new Token(TokenType.NUMBER, included, this.input.position-included.length) return new Token(TokenType.NUMBER, included, this.input.position - included.length)
} }
readOperator() { readOperator() {
let included = ""; let included = ""
while(!this.input.atEnd() && OPERATORS.includes(this.input.peek())) { while(!this.input.atEnd() && OPERATORS.includes(this.input.peek())) {
included += this.input.next(); included += this.input.next()
} }
return new Token(TokenType.OPERATOR, included, this.input.position-included.length) return new Token(TokenType.OPERATOR, included, this.input.position - included.length)
} }
readIdentifier() { readIdentifier() {
let identifier = ""; let identifier = ""
while(!this.input.atEnd() && IDENTIFIER_CHARS.includes(this.input.peek().toLowerCase())) { while(!this.input.atEnd() && IDENTIFIER_CHARS.includes(this.input.peek().toLowerCase())) {
identifier += this.input.next(); identifier += this.input.next()
} }
if(Reference.CONSTANTS_LIST.includes(identifier.toLowerCase())) { if(Reference.CONSTANTS_LIST.includes(identifier.toLowerCase())) {
return new Token(TokenType.CONSTANT, identifier.toLowerCase(), this.input.position-identifier.length) return new Token(TokenType.CONSTANT, identifier.toLowerCase(), this.input.position - identifier.length)
} else if(Reference.FUNCTIONS_LIST.includes(identifier.toLowerCase())) { } else if(Reference.FUNCTIONS_LIST.includes(identifier.toLowerCase())) {
return new Token(TokenType.FUNCTION, identifier.toLowerCase(), this.input.position-identifier.length) return new Token(TokenType.FUNCTION, identifier.toLowerCase(), this.input.position - identifier.length)
} else { } else {
return new Token(TokenType.VARIABLE, identifier, this.input.position-identifier.length) return new Token(TokenType.VARIABLE, identifier, this.input.position - identifier.length)
} }
} }
readNextToken() { readNextToken() {
if(!this.tokenizeWhitespaces) if(!this.tokenizeWhitespaces)
this.skipWhitespaces() this.skipWhitespaces()
if(this.input.atEnd()) return null; if(this.input.atEnd()) return null
let c = this.input.peek(); let c = this.input.peek()
if(this.tokenizeWhitespaces && WHITESPACES.includes(c)) return this.readWhitespaces(); if(this.tokenizeWhitespaces && WHITESPACES.includes(c)) return this.readWhitespaces()
if(STRING_LIMITERS.includes(c)) return this.readString(); if(STRING_LIMITERS.includes(c)) return this.readString()
if(NUMBER_CHARS.includes(c)) return this.readNumber(); if(NUMBER_CHARS.includes(c)) return this.readNumber()
if(IDENTIFIER_CHARS.includes(c.toLowerCase())) return this.readIdentifier(); if(IDENTIFIER_CHARS.includes(c.toLowerCase())) return this.readIdentifier()
if(OPERATORS.includes(c)) return this.readOperator(); if(OPERATORS.includes(c)) return this.readOperator()
if(Reference.CONSTANTS_LIST.includes(c)) return new Token(TokenType.CONSTANT, this.input.next(), this.input.position-1); if(Reference.CONSTANTS_LIST.includes(c)) return new Token(TokenType.CONSTANT, this.input.next(), this.input.position - 1)
if(PUNCTUATION.includes(c)) return new Token(TokenType.PUNCT, this.input.next(), this.input.position-1); if(PUNCTUATION.includes(c)) return new Token(TokenType.PUNCT, this.input.next(), this.input.position - 1)
if(this.errorOnUnknown) if(this.errorOnUnknown)
this.input.raise("Unknown token character " + c) this.input.raise("Unknown token character " + c)
else else
return new Token(TokenType.UNKNOWN, this.input.next(), this.input.position-1); return new Token(TokenType.UNKNOWN, this.input.next(), this.input.position - 1)
} }
peek() { peek() {
if(this.currentToken == null) this.currentToken = this.readNextToken(); if(this.currentToken == null) this.currentToken = this.readNextToken()
return this.currentToken; return this.currentToken
} }
next() { next() {
let tmp; let tmp
if(this.currentToken == null) if(this.currentToken == null)
tmp = this.readNextToken(); tmp = this.readNextToken()
else else
tmp = this.currentToken; tmp = this.currentToken
this.currentToken = null; this.currentToken = null
return tmp; return tmp
} }
atEnd() { atEnd() {
return this.peek() == null; return this.peek() == null
} }
skip(type) { skip(type) {
let next = this.next(); let next = this.next()
if(next.type !== type) if(next.type !== type)
this.input.raise("Unexpected token " + next.type.toLowerCase() + ' "' + next.value + '". Expected ' + type.toLowerCase()); this.input.raise("Unexpected token " + next.type.toLowerCase() + " \"" + next.value + "\". Expected " + type.toLowerCase())
} }
} }

View file

@ -16,7 +16,7 @@
* 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 {Expression} from "../math/index.mjs" import { Expression } from "../math/index.mjs"
class Setting { class Setting {
constructor(type, name, nameInConfig, icon) { constructor(type, name, nameInConfig, icon) {
@ -49,7 +49,7 @@ class Setting {
export class BoolSetting extends Setting { export class BoolSetting extends Setting {
constructor(name, nameInConfig, icon) { constructor(name, nameInConfig, icon) {
super('bool', name, nameInConfig, icon) super("bool", name, nameInConfig, icon)
} }
value() { value() {
@ -63,9 +63,9 @@ export class BoolSetting extends Setting {
export class NumberSetting extends Setting { export class NumberSetting extends Setting {
constructor(name, nameInConfig, icon, min = -Infinity, max = +Infinity) { constructor(name, nameInConfig, icon, min = -Infinity, max = +Infinity) {
super('number', name, nameInConfig, icon) super("number", name, nameInConfig, icon)
this.min = typeof min == 'number' ? () => min : min this.min = typeof min == "number" ? () => min : min
this.max = typeof max == 'number' ? () => max : max this.max = typeof max == "number" ? () => max : max
} }
value() { value() {
@ -79,7 +79,7 @@ export class NumberSetting extends Setting {
export class EnumIntSetting extends Setting { export class EnumIntSetting extends Setting {
constructor(name, nameInConfig, icon, values = []) { constructor(name, nameInConfig, icon, values = []) {
super('enum', name, nameInConfig, icon) super("enum", name, nameInConfig, icon)
this.values = values this.values = values
} }
@ -94,7 +94,7 @@ export class EnumIntSetting extends Setting {
export class ExpressionSetting extends Setting { export class ExpressionSetting extends Setting {
constructor(name, nameInConfig, icon, variables = []) { constructor(name, nameInConfig, icon, variables = []) {
super('expression', name, nameInConfig, icon) super("expression", name, nameInConfig, icon)
this.variables = variables this.variables = variables
} }
@ -112,17 +112,17 @@ export class ExpressionSetting extends Setting {
Helper.setSetting(this.nameInConfig, value) Helper.setSetting(this.nameInConfig, value)
else { else {
let undefinedVars = vars.filter(x => !this.variables.includes(x)) let undefinedVars = vars.filter(x => !this.variables.includes(x))
let allowed = '' let allowed = ""
if(this.variables.length > 0) if(this.variables.length > 0)
allowed = `Allowed variables: ${this.variables.join(', ')}.` allowed = `Allowed variables: ${this.variables.join(", ")}.`
throw new TypeError(`Cannot use variable(s) ${undefinedVars.join(', or ')} to define ${this.displayName}. ${allowed}`) throw new TypeError(`Cannot use variable(s) ${undefinedVars.join(", or ")} to define ${this.displayName}. ${allowed}`)
} }
} }
} }
export class StringSetting extends Setting { export class StringSetting extends Setting {
constructor(name, nameInConfig, icon, defaultValues = []) { constructor(name, nameInConfig, icon, defaultValues = []) {
super('string', name, nameInConfig, icon) super("string", name, nameInConfig, icon)
this.defaultValues = defaultValues this.defaultValues = defaultValues
} }

View file

@ -16,7 +16,7 @@
* 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 {BoolSetting, ExpressionSetting, NumberSetting, StringSetting} from "./common.mjs" import { BoolSetting, ExpressionSetting, NumberSetting, StringSetting } from "./common.mjs"
const XZOOM = new NumberSetting( const XZOOM = new NumberSetting(
@ -49,13 +49,13 @@ const YMAX = new NumberSetting(
const XAXISSTEP = new ExpressionSetting( const XAXISSTEP = new ExpressionSetting(
qsTranslate("Settings", "X Axis Step"), qsTranslate("Settings", "X Axis Step"),
"default_graph.xaxisstep", "default_graph.xaxisstep",
"xaxisstep", "xaxisstep"
) )
const YAXISSTEP = new ExpressionSetting( const YAXISSTEP = new ExpressionSetting(
qsTranslate("Settings", "Y Axis Step"), qsTranslate("Settings", "Y Axis Step"),
"default_graph.yaxisstep", "default_graph.yaxisstep",
"yaxisstep", "yaxisstep"
) )
const LINE_WIDTH = new NumberSetting( const LINE_WIDTH = new NumberSetting(
@ -72,33 +72,33 @@ const TEXT_SIZE = new NumberSetting(
) )
const X_LABEL = new StringSetting( const X_LABEL = new StringSetting(
qsTranslate("Settings", 'X Label'), qsTranslate("Settings", "X Label"),
"default_graph.xlabel", "default_graph.xlabel",
"xlabel", "xlabel",
["", "x", "ω (rad/s)"] ["", "x", "ω (rad/s)"]
) )
const Y_LABEL = new StringSetting( const Y_LABEL = new StringSetting(
qsTranslate("Settings", 'Y Label'), qsTranslate("Settings", "Y Label"),
"default_graph.ylabel", "default_graph.ylabel",
"xlabel", "xlabel",
["", "y", "G (dB)", "φ (°)", "φ (deg)", "φ (rad)"] ["", "y", "G (dB)", "φ (°)", "φ (deg)", "φ (rad)"]
) )
const LOG_SCALE_X = new BoolSetting( const LOG_SCALE_X = new BoolSetting(
qsTranslate("Settings", 'X Log scale'), qsTranslate("Settings", "X Log scale"),
"default_graph.logscalex", "default_graph.logscalex",
"logscalex" "logscalex"
) )
const SHOW_X_GRAD = new BoolSetting( const SHOW_X_GRAD = new BoolSetting(
qsTranslate("Settings", 'Show X graduation'), qsTranslate("Settings", "Show X graduation"),
"default_graph.showxgrad", "default_graph.showxgrad",
"showxgrad" "showxgrad"
) )
const SHOW_Y_GRAD = new BoolSetting( const SHOW_Y_GRAD = new BoolSetting(
qsTranslate("Settings", 'Show Y graduation'), qsTranslate("Settings", "Show Y graduation"),
"default_graph.showygrad", "default_graph.showygrad",
"showygrad" "showygrad"
) )

View file

@ -16,30 +16,30 @@
* 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 {BoolSetting, EnumIntSetting} from "./common.mjs" import { BoolSetting, EnumIntSetting } from "./common.mjs"
const AUTOCLOSE_FORMULA = new BoolSetting( const AUTOCLOSE_FORMULA = new BoolSetting(
qsTranslate("expression", "Automatically close parenthesises and brackets"), qsTranslate("expression", "Automatically close parenthesises and brackets"),
'expression_editor.autoclose', "expression_editor.autoclose",
'text' "text"
) )
const ENABLE_SYNTAX_HIGHLIGHTING = new BoolSetting( const ENABLE_SYNTAX_HIGHLIGHTING = new BoolSetting(
qsTranslate("expression", "Enable syntax highlighting"), qsTranslate("expression", "Enable syntax highlighting"),
'expression_editor.colorize', "expression_editor.colorize",
'appearance' "appearance"
) )
const ENABLE_AUTOCOMPLETE = new BoolSetting( const ENABLE_AUTOCOMPLETE = new BoolSetting(
qsTranslate("expression", "Enable autocompletion"), qsTranslate("expression", "Enable autocompletion"),
'autocompletion.enabled', "autocompletion.enabled",
'label' "label"
) )
const PICK_COLOR_SCHEME = new EnumIntSetting( const PICK_COLOR_SCHEME = new EnumIntSetting(
qsTranslate("expression", "Color Scheme"), qsTranslate("expression", "Color Scheme"),
'expression_editor.color_scheme', "expression_editor.color_scheme",
'color', "color",
["Breeze Light", "Breeze Dark", "Solarized", "Github Light", "Github Dark", "Nord", "Monokai"] ["Breeze Light", "Breeze Dark", "Solarized", "Github Light", "Github Dark", "Nord", "Monokai"]
) )

View file

@ -16,25 +16,25 @@
* 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 {BoolSetting} from "./common.mjs" import { BoolSetting } from "./common.mjs"
import Canvas from "../module/canvas.mjs" import Canvas from "../module/canvas.mjs"
import LatexAPI from "../module/latex.mjs" import LatexAPI from "../module/latex.mjs"
const CHECK_FOR_UPDATES = new BoolSetting( const CHECK_FOR_UPDATES = new BoolSetting(
qsTranslate("general", "Check for updates on startup"), qsTranslate("general", "Check for updates on startup"),
'check_for_updates', "check_for_updates",
'update' "update"
) )
const RESET_REDO_STACK = new BoolSetting( const RESET_REDO_STACK = new BoolSetting(
qsTranslate("general", "Reset redo stack automaticly"), qsTranslate("general", "Reset redo stack automaticly"),
'reset_redo_stack', "reset_redo_stack",
'timeline' "timeline"
) )
class EnableLatex extends BoolSetting { class EnableLatex extends BoolSetting {
constructor() { constructor() {
super(qsTranslate("general","Enable LaTeX rendering"), 'enable_latex', 'Expression') super(qsTranslate("general", "Enable LaTeX rendering"), "enable_latex", "Expression")
} }
set(value) { set(value) {

View file

@ -16,6 +16,25 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
// Add string methods
/**
* Replaces latin characters with their uppercase versions.
* @return {string}
*/
String.prototype.toLatinUppercase = String.prototype.toLatinUppercase || function() {
return this.replace(/[a-z]/g, function(match) {
return match.toUpperCase()
})
}
/**
* Removes the 'enclosers' of a string (e.g. quotes, parentheses, brackets...)
* @return {string}
*/
String.prototype.removeEnclosure = function() {
return this.substring(1, this.length - 1)
}
const powerpos = { const powerpos = {
"-": "⁻", "-": "⁻",
"+": "⁺", "+": "⁺",
@ -350,10 +369,6 @@ export function parseName(str, removeUnallowed = true) {
return str return str
} }
String.prototype.toLatinUppercase = function() {
return this.replace(/[a-z]/g, function(match){return match.toUpperCase()})
}
/** /**
* Transforms camel case strings to a space separated one. * Transforms camel case strings to a space separated one.
* *

View file

@ -29,7 +29,7 @@ class LOG_COLORS:
GRAY = "\033[90m" GRAY = "\033[90m"
BLUE = "\033[94m" BLUE = "\033[94m"
ORANGE = "\033[38;5;166m" ORANGE = "\033[38;5;166m"
RED = "\033[e[38;5;204m" RED = "\033[38;5;204m"
INVERT = "\033[7m" INVERT = "\033[7m"
RESET_INVERT = "\033[27m" RESET_INVERT = "\033[27m"
RESET = "\033[0m" RESET = "\033[0m"

138
package-lock.json generated
View file

@ -13,7 +13,6 @@
"@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^28.0.0", "@rollup/plugin-commonjs": "^28.0.0",
"@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-terser": "^0.4.4",
"install": "^0.13.0", "install": "^0.13.0",
"rollup": "^4.22.4", "rollup": "^4.22.4",
"rollup-plugin-cleanup": "^3.2.1" "rollup-plugin-cleanup": "^3.2.1"
@ -1724,16 +1723,6 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@jridgewell/source-map": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25"
}
},
"node_modules/@jridgewell/sourcemap-codec": { "node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
@ -1838,28 +1827,6 @@
} }
} }
}, },
"node_modules/@rollup/plugin-terser": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz",
"integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==",
"license": "MIT",
"dependencies": {
"serialize-javascript": "^6.0.1",
"smob": "^1.0.0",
"terser": "^5.17.4"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/pluginutils": { "node_modules/@rollup/pluginutils": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz",
@ -2114,18 +2081,6 @@
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/acorn": {
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/ansi-styles": { "node_modules/ansi-styles": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
@ -2209,12 +2164,6 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
} }
}, },
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"license": "MIT"
},
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001663", "version": "1.0.30001663",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz",
@ -2264,12 +2213,6 @@
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"license": "MIT"
},
"node_modules/commondir": { "node_modules/commondir": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
@ -2602,15 +2545,6 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"license": "MIT",
"dependencies": {
"safe-buffer": "^5.1.0"
}
},
"node_modules/regenerate": { "node_modules/regenerate": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@ -2770,26 +2704,6 @@
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/semver": { "node_modules/semver": {
"version": "6.3.1", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@ -2799,15 +2713,6 @@
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
}, },
"node_modules/serialize-javascript": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"license": "BSD-3-Clause",
"dependencies": {
"randombytes": "^2.1.0"
}
},
"node_modules/skip-regex": { "node_modules/skip-regex": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/skip-regex/-/skip-regex-1.0.2.tgz", "resolved": "https://registry.npmjs.org/skip-regex/-/skip-regex-1.0.2.tgz",
@ -2817,31 +2722,6 @@
"node": ">=4.2" "node": ">=4.2"
} }
}, },
"node_modules/smob": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz",
"integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==",
"license": "MIT"
},
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/source-map-support/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sourcemap-codec": { "node_modules/sourcemap-codec": {
"version": "1.4.8", "version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
@ -2873,24 +2753,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/terser": {
"version": "5.33.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.33.0.tgz",
"integrity": "sha512-JuPVaB7s1gdFKPKTelwUyRq5Sid2A3Gko2S0PncwdBq7kN9Ti9HPWDQ06MPsEDGsZeVESjKEnyGy68quBk1w6g==",
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},
"bin": {
"terser": "bin/terser"
},
"engines": {
"node": ">=10"
}
},
"node_modules/to-fast-properties": { "node_modules/to-fast-properties": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",

View file

@ -17,7 +17,6 @@
"@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^28.0.0", "@rollup/plugin-commonjs": "^28.0.0",
"@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-terser": "^0.4.4",
"install": "^0.13.0", "install": "^0.13.0",
"rollup": "^4.22.4", "rollup": "^4.22.4",
"rollup-plugin-cleanup": "^3.2.1" "rollup-plugin-cleanup": "^3.2.1"

View file

@ -20,7 +20,6 @@ import { nodeResolve } from "@rollup/plugin-node-resolve"
import commonjs from "@rollup/plugin-commonjs" import commonjs from "@rollup/plugin-commonjs"
import { babel } from "@rollup/plugin-babel" import { babel } from "@rollup/plugin-babel"
import cleanup from "rollup-plugin-cleanup" import cleanup from "rollup-plugin-cleanup"
import terser from "@rollup/plugin-terser"
const path = "LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js" const path = "LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js"
@ -39,9 +38,6 @@ export default {
babel({ babel({
babelHelpers: "bundled" babelHelpers: "bundled"
}), }),
// terser({
// ecma: 2015
// })
] ]
} }