Reformatting JS files
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Adsooi 2024-09-27 02:54:20 +02:00
parent 8cefc56ac7
commit c32d70e9ed
Signed by: Ad5001
GPG key ID: EF45F9C6AFE20160
45 changed files with 1390 additions and 1326 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

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -21,36 +21,41 @@ 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)
} }
export() { export() {
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.")
.arg(Objects.types[this.targetType].displayType()).arg(this.targetName) .arg(Objects.types[this.targetType].displayType()).arg(this.targetName)
.arg(this.previousValue).arg(this.newValue) .arg(this.previousValue).arg(this.newValue)
} }
formatColor(color) { formatColor(color) {
return `<span style="color: ${color}; font-family: monospace; padding: 2px;">██</span>` return `<span style="color: ${color}; font-family: monospace; padding: 2px;">██</span>`
} }
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

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -22,66 +22,72 @@ import Latex from "../module/latex.mjs"
export class Action { export class Action {
/** /**
* Type of the action. * Type of the 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") {
this.targetName = targetName this.targetName = targetName
this.targetType = targetType this.targetType = targetType
} }
/** /**
* 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.
* NOTE: These arguments will be reinputed in the constructor in this order. * NOTE: These arguments will be reinputed in the constructor in this order.
* *
* @returns {string[]} * @returns {string[]}
*/ */
export() { export() {
return [this.targetName, this.targetType] return [this.targetName, this.targetType]
} }
/** /**
* 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"
} }
/** /**
* Returns a string containing an HTML tag describing the icon of a type * Returns a string containing an HTML tag describing the icon of a type
* *
* @param {string} type - Name of the icon to put in rich text. * @param {string} type - Name of the icon to put in rich text.
* @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>`
} }
/** /**
* Renders a LaTeX-formatted string to an image and wraps it in an HTML tag in a string. * Renders a LaTeX-formatted string to an image and wraps it in an HTML tag in a string.
* *
* @param {string} latexString - Source string of the latex. * @param {string} latexString - Source string of the latex.
* @returns {Promise<string>} * @returns {Promise<string>}
*/ */
@ -94,12 +100,12 @@ export class Action {
imgDepth * (History.fontSize + 2), imgDepth * (History.fontSize + 2),
History.themeTextColor History.themeTextColor
) )
return `<img src="${source}" width="${width/imgDepth}" height="${height/imgDepth}" style="vertical-align: middle"/>` return `<img src="${source}" width="${width / imgDepth}" height="${height / imgDepth}" style="vertical-align: middle"/>`
} }
/** /**
* Returns a string with the HTML-formatted description of the action. * Returns a string with the HTML-formatted description of the action.
* *
* @returns {string|Promise<string>} * @returns {string|Promise<string>}
*/ */
getHTMLString() { getHTMLString() {

View file

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -19,43 +19,51 @@
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() {
color(darkVer=false){return darkVer ? 'green' : 'lime'} return "create"
}
color(darkVer = false) {
return darkVer ? "green" : "lime"
}
constructor(targetName = "", targetType = "Point", properties = []) { constructor(targetName = "", targetType = "Point", properties = []) {
super(targetName, targetType) super(targetName, targetType)
this.targetProperties = properties this.targetProperties = properties
} }
undo() { undo() {
Objects.deleteObject(this.targetName) Objects.deleteObject(this.targetName)
} }
redo() { redo() {
Objects.createNewRegisteredObject(this.targetType, this.targetProperties) Objects.createNewRegisteredObject(this.targetType, this.targetProperties)
Objects.currentObjectsByName[this.targetName].update() Objects.currentObjectsByName[this.targetName].update()
} }
export() { export() {
return [this.targetName, this.targetType, this.targetProperties] return [this.targetName, this.targetType, this.targetProperties]
} }
getReadableString() { getReadableString() {
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(this.targetName) .arg(this.targetName)
} }
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"
/**
* 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 { 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.
*/
type(){return 'DeleteObject'} type(){return 'DeleteObject'}
icon(){return 'delete'} icon(){return 'delete'}

View file

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -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() {
color(darkVer=false){ return "modify"
return darkVer ? 'darkslateblue' : 'cyan'; }
color(darkVer = false) {
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);
@ -63,17 +69,17 @@ export default class EditedProperty extends Action {
} }
this.setReadableValues() this.setReadableValues()
} }
undo() { undo() {
Objects.currentObjectsByName[this.targetName][this.targetProperty] = this.previousValue Objects.currentObjectsByName[this.targetName][this.targetProperty] = this.previousValue
Objects.currentObjectsByName[this.targetName].update() Objects.currentObjectsByName[this.targetName].update()
} }
redo() { redo() {
Objects.currentObjectsByName[this.targetName][this.targetProperty] = this.newValue Objects.currentObjectsByName[this.targetName][this.targetProperty] = this.newValue
Objects.currentObjectsByName[this.targetName].update() Objects.currentObjectsByName[this.targetName].update()
} }
export() { export() {
if(this.previousValue instanceof MathLib.Expression) { if(this.previousValue instanceof MathLib.Expression) {
return [this.targetName, this.targetType, this.targetProperty, this.previousValue.toEditableString(), this.newValue.toEditableString(), true] return [this.targetName, this.targetType, this.targetProperty, this.previousValue.toEditableString(), this.newValue.toEditableString(), true]
@ -83,7 +89,7 @@ export default class EditedProperty extends Action {
return [this.targetName, this.targetType, this.targetProperty, this.previousValue, this.newValue, false] return [this.targetName, this.targetType, this.targetProperty, this.previousValue, this.newValue, false]
} }
} }
setReadableValues() { setReadableValues() {
this.prevString = "" this.prevString = ""
this.nextString = "" this.nextString = ""
@ -93,45 +99,45 @@ 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)
} }
/** /**
@ -139,9 +145,9 @@ export default class EditedProperty extends Action {
* @return {Promise<string>|string} * @return {Promise<string>|string}
*/ */
async getHTMLString() { async getHTMLString() {
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) {
const [prevHTML, nextHTML] = await Promise.all(this._renderPromises) const [prevHTML, nextHTML] = await Promise.all(this._renderPromises)

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

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -21,43 +21,48 @@ 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'}
color(darkVer=false){
return darkVer ? 'seagreen' : 'lightseagreen';
} }
icon() {
return "position"
}
color(darkVer = false) {
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()
} }
undo() { undo() {
Objects.currentObjectsByName[this.targetName].x = this.previousXValue Objects.currentObjectsByName[this.targetName].x = this.previousXValue
Objects.currentObjectsByName[this.targetName].y = this.previousYValue Objects.currentObjectsByName[this.targetName].y = this.previousYValue
Objects.currentObjectsByName[this.targetName].update() Objects.currentObjectsByName[this.targetName].update()
} }
redo() { redo() {
Objects.currentObjectsByName[this.targetName].x = this.newXValue Objects.currentObjectsByName[this.targetName].x = this.newXValue
Objects.currentObjectsByName[this.targetName].y = this.newYValue Objects.currentObjectsByName[this.targetName].y = this.newYValue
Objects.currentObjectsByName[this.targetName].update() Objects.currentObjectsByName[this.targetName].update()
} }
setReadableValues() { setReadableValues() {
this.prevString = `(${this.previousXValue.toString()},${this.previousYValue.toString()})` this.prevString = `(${this.previousXValue.toString()},${this.previousYValue.toString()})`
this.nextString = `(${this.newXValue.toString()},${this.newYValue.toString()})` this.nextString = `(${this.newXValue.toString()},${this.newYValue.toString()})`
@ -71,27 +76,27 @@ 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>"
} }
} }
export() { export() {
return [this.targetName, this.targetType, return [this.targetName, this.targetType,
this.previousXValue.toEditableString(), this.newXValue.toEditableString(), this.previousXValue.toEditableString(), this.newXValue.toEditableString(),
this.previousYValue.toEditableString(), this.newYValue.toEditableString()] this.previousYValue.toEditableString(), this.newYValue.toEditableString()]
} }
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)
} }
async getHTMLString() { async getHTMLString() {
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) {
const [prevHTML, nextHTML] = await Promise.all(this._renderPromises) const [prevHTML, nextHTML] = await Promise.all(this._renderPromises)
@ -99,6 +104,6 @@ export default class EditedPosition extends Action {
this.nextHTML = this.nextHTML ?? nextHTML this.nextHTML = this.nextHTML ?? nextHTML
} }
return translation.arg(this.prevHTML).arg(this.nextHTML) return translation.arg(this.prevHTML).arg(this.nextHTML)
} }
} }

View file

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -20,36 +20,42 @@ 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'}
color(darkVer=false){
return this.newValue ?
(darkVer ? 'darkgray' : 'whitesmoke') :
(darkVer ? 'dimgray' : 'lightgray')
} }
icon() {
return "visibility"
}
color(darkVer = false) {
return this.newValue ?
(darkVer ? "darkgray" : "whitesmoke") :
(darkVer ? "dimgray" : "lightgray")
}
constructor(targetName = "", targetType = "Point", newValue = true) { constructor(targetName = "", targetType = "Point", newValue = true) {
super(targetName, targetType, "visible", !newValue, newValue) super(targetName, targetType, "visible", !newValue, newValue)
} }
export() { export() {
return [this.targetName, this.targetType, this.newValue] return [this.targetName, this.targetType, this.newValue]
} }
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

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -23,35 +23,44 @@ 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.
* @return {Domain} Found domain, string otherwise. * @return {Domain} Found domain, string otherwise.
@ -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;
} }
} }
} }
@ -170,16 +165,26 @@ export class EmptySet extends Domain {
this.displayName = "∅" this.displayName = "∅"
this.latexMarkup = "\\emptyset" this.latexMarkup = "\\emptyset"
} }
includes(x) { return false } includes(x) {
return false
toString() { return this.displayName } }
union(domain) { return domain } toString() {
return this.displayName
intersection(domain) { return this } }
static import(frm) { return new EmptySet() } union(domain) {
return domain
}
intersection(domain) {
return this
}
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,27 +195,27 @@ 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
this.displayName = (openBegin ? "]" : "[") + begin.toString() + ";" + end.toString() + (openEnd ? "[" : "]") this.displayName = (openBegin ? "]" : "[") + begin.toString() + ";" + end.toString() + (openEnd ? "[" : "]")
this.latexMarkup = `\\mathopen${openBegin ? "]" : "["}${this.begin.latexMarkup};${this.end.latexMarkup}\\mathclose${openEnd ? "[" : "]"}` this.latexMarkup = `\\mathopen${openBegin ? "]" : "["}${this.begin.latexMarkup};${this.end.latexMarkup}\\mathclose${openEnd ? "[" : "]"}`
} }
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()))
} }
toString() { toString() {
return this.displayName return this.displayName
} }
union(domain) { union(domain) {
if(domain instanceof EmptySet) return this if(domain instanceof EmptySet) return this
if(domain instanceof DomainSet) return domain.union(this) if(domain instanceof DomainSet) return domain.union(this)
@ -219,7 +224,7 @@ export class Range extends Domain {
if(domain instanceof MinusDomain) return new UnionDomain(this, domain) if(domain instanceof MinusDomain) return new UnionDomain(this, domain)
if(domain instanceof Range) return new UnionDomain(this, domain) if(domain instanceof Range) return new UnionDomain(this, domain)
} }
intersection(domain) { intersection(domain) {
if(domain instanceof EmptySet) return domain if(domain instanceof EmptySet) return domain
if(domain instanceof DomainSet) return domain.intersection(this) if(domain instanceof DomainSet) return domain.intersection(this)
@ -228,11 +233,11 @@ export class Range extends Domain {
if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain)
if(domain instanceof Range) return new IntersectionDomain(this, domain) if(domain instanceof Range) return new IntersectionDomain(this, 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)
} }
} }
@ -250,7 +255,7 @@ export class SpecialDomain extends Domain {
* @param {boolean} moveSupported - Only true if next and previous functions are valid. * @param {boolean} moveSupported - Only true if next and previous functions are valid.
*/ */
constructor(displayName, isValid, next = () => true, previous = () => true, constructor(displayName, isValid, next = () => true, previous = () => true,
moveSupported = true) { moveSupported = true) {
super() super()
this.displayName = displayName this.displayName = displayName
this.isValid = isValid this.isValid = isValid
@ -258,29 +263,29 @@ export class SpecialDomain extends Domain {
this.prevValue = previous this.prevValue = previous
this.moveSupported = moveSupported this.moveSupported = moveSupported
} }
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)
} }
toString() { toString() {
return this.displayName return this.displayName
} }
union(domain) { union(domain) {
if(domain instanceof EmptySet) return this if(domain instanceof EmptySet) return this
if(domain instanceof DomainSet) return domain.union(this) if(domain instanceof DomainSet) return domain.union(this)
@ -289,7 +294,7 @@ export class SpecialDomain extends Domain {
if(domain instanceof MinusDomain) return new UnionDomain(this, domain) if(domain instanceof MinusDomain) return new UnionDomain(this, domain)
if(domain instanceof Range) return new UnionDomain(this, domain) if(domain instanceof Range) return new UnionDomain(this, domain)
} }
intersection(domain) { intersection(domain) {
if(domain instanceof EmptySet) return domain if(domain instanceof EmptySet) return domain
if(domain instanceof DomainSet) return domain.intersection(this) if(domain instanceof DomainSet) return domain.intersection(this)
@ -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,54 +319,54 @@ 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(";")}\\}`
} }
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
} }
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
} }
return null return null
} }
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
} }
return null return null
} }
toString() { toString() {
return this.displayName return this.displayName
} }
union(domain) { union(domain) {
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
}) })
@ -378,18 +383,18 @@ export class DomainSet extends SpecialDomain {
domain.openEnd = false domain.openEnd = false
} }
} }
if(!domain.includes(value)) if(!domain.includes(value))
notIncludedValues.push(this.values[i].toEditableString()) notIncludedValues.push(this.values[i].toEditableString())
} }
if(notIncludedValues.length === 0) return domain if(notIncludedValues.length === 0) return domain
return new UnionDomain(domain, new DomainSet(notIncludedValues)) return new UnionDomain(domain, new DomainSet(notIncludedValues))
} }
intersection(domain) { intersection(domain) {
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
@ -405,16 +410,16 @@ export class DomainSet extends SpecialDomain {
domain.openEnd = false domain.openEnd = false
} }
} }
if(domain.includes(value)) if(domain.includes(value))
includedValues.push(this.values[i].toEditableString()) includedValues.push(this.values[i].toEditableString())
} }
if(includedValues.length === 0) return new EmptySet() if(includedValues.length === 0) return new EmptySet()
if(includedValues.length === this.values.length) return this if(includedValues.length === this.values.length) return this
return new IntersectionDomain(domain, new DomainSet(includedValues)) return new IntersectionDomain(domain, new DomainSet(includedValues))
} }
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(";"))
} }
} }
@ -429,15 +434,15 @@ export class UnionDomain extends Domain {
this.displayName = this.dom1.toString() + " " + this.dom2.toString() this.displayName = this.dom1.toString() + " " + this.dom2.toString()
this.latexMarkup = `${dom1.latexMarkup}\\cup${dom2.latexMarkup}` this.latexMarkup = `${dom1.latexMarkup}\\cup${dom2.latexMarkup}`
} }
includes(x) { includes(x) {
return this.dom1.includes(x) || this.dom2.includes(x) return this.dom1.includes(x) || this.dom2.includes(x)
} }
toString() { toString() {
return this.displayName return this.displayName
} }
union(domain) { union(domain) {
if(domain instanceof EmptySet) return this if(domain instanceof EmptySet) return this
if(domain instanceof DomainSet) return domain.union(this) if(domain instanceof DomainSet) return domain.union(this)
@ -446,7 +451,7 @@ export class UnionDomain extends Domain {
if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain) if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain)
if(domain instanceof MinusDomain) return new MinusDomain(this, domain) if(domain instanceof MinusDomain) return new MinusDomain(this, domain)
} }
intersection(domain) { intersection(domain) {
if(domain instanceof EmptySet) return domain if(domain instanceof EmptySet) return domain
if(domain instanceof DomainSet) return domain.intersection(this) if(domain instanceof DomainSet) return domain.intersection(this)
@ -454,12 +459,12 @@ export class UnionDomain extends Domain {
if(domain instanceof IntersectionDomain) return this.dom1.intersection(domain.dom1).intersection(this.dom2).intersection(domain.dom2) if(domain instanceof IntersectionDomain) return this.dom1.intersection(domain.dom1).intersection(this.dom2).intersection(domain.dom2)
if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain)
} }
static import(frm) { static import(frm) {
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)
} }
} }
@ -475,24 +480,24 @@ export class IntersectionDomain extends Domain {
this.displayName = dom1.toString() + " ∩ " + dom2.toString() this.displayName = dom1.toString() + " ∩ " + dom2.toString()
this.latexMarkup = `${dom1.latexMarkup}\\cap${dom2.latexMarkup}` this.latexMarkup = `${dom1.latexMarkup}\\cap${dom2.latexMarkup}`
} }
includes(x) { includes(x) {
return this.dom1.includes(x) && this.dom2.includes(x) return this.dom1.includes(x) && this.dom2.includes(x)
} }
toString() { toString() {
return this.displayName return this.displayName
} }
union(domain) { union(domain) {
if(domain instanceof EmptySet) return this if(domain instanceof EmptySet) return this
if(domain instanceof DomainSet) return domain.union(this) if(domain instanceof DomainSet) return domain.union(this)
if(domain instanceof Range) return domain.union(this) if(domain instanceof Range) return domain.union(this)
if(domain instanceof UnionDomain) return this.dom1.union(domain.dom1).union(this.dom2).union(domain.dom2) if(domain instanceof UnionDomain) return this.dom1.union(domain.dom1).union(this.dom2).union(domain.dom2)
if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain) if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain)
if(domain instanceof MinusDomain) return new MinusDomain(this, domain) if(domain instanceof MinusDomain) return new MinusDomain(this, domain)
} }
intersection(domain) { intersection(domain) {
if(domain instanceof EmptySet) return domain if(domain instanceof EmptySet) return domain
if(domain instanceof DomainSet) return domain.intersection(this) if(domain instanceof DomainSet) return domain.intersection(this)
@ -500,11 +505,11 @@ export class IntersectionDomain extends Domain {
if(domain instanceof IntersectionDomain) return new IntersectionDomain(this, domain) if(domain instanceof IntersectionDomain) return new IntersectionDomain(this, domain)
if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) if(domain instanceof MinusDomain) return new IntersectionDomain(this, 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)
} }
} }
@ -520,20 +525,20 @@ export class MinusDomain extends Domain {
this.displayName = dom1.toString() + "" + dom2.toString() this.displayName = dom1.toString() + "" + dom2.toString()
this.latexMarkup = `${dom1.latexMarkup}\\setminus${dom2.latexMarkup}` this.latexMarkup = `${dom1.latexMarkup}\\setminus${dom2.latexMarkup}`
} }
includes(x) { includes(x) {
return this.dom1.includes(x) && !this.dom2.includes(x) return this.dom1.includes(x) && !this.dom2.includes(x)
} }
toString() { toString() {
return this.displayName return this.displayName
} }
static import(frm) { static import(frm) {
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,53 +547,53 @@ 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}"
let refedDomains = [] let refedDomains = []
@ -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

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -37,70 +37,90 @@ 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))
} }
recache() { recache() {
if(this.cached) if(this.cached)
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName) this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
} }
execute(x = 1) { execute(x = 1) {
if(this.cached) { if(this.cached) {
if(this.cachedValue == null) if(this.cachedValue == null)
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)
} }
toEditableString() { toEditableString() {
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

@ -94,6 +94,10 @@ export class CanvasInterface extends SettingsInterface {
markDirty = FUNCTION markDirty = FUNCTION
/** @type {function(string)} */ /** @type {function(string)} */
loadImage = FUNCTION loadImage = FUNCTION
/** @type {function(string)} */
isImageLoading = FUNCTION
/** @type {function(string)} */
isImageLoaded = FUNCTION
/** @type {function()} */ /** @type {function()} */
requestPaint = FUNCTION requestPaint = 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 {

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",
@ -71,7 +73,7 @@ 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 {LatexRenderResult|null} * @returns {LatexRenderResult|null}
*/ */
findPrerendered(markup, fontSize, color) { findPrerendered(markup, fontSize, color) {
@ -87,7 +89,7 @@ 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>}
*/ */
async requestAsyncRender(markup, fontSize, color) { async requestAsyncRender(markup, fontSize, color) {
@ -119,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
} }
@ -134,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)`
} }
} }
@ -166,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

@ -1,28 +1,28 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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 { 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

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */

View file

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -27,89 +27,100 @@ 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
this.omGraduation = omGraduation this.omGraduation = omGraduation
} }
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 {
return this.om_0.y.execute() return this.om_0.y.execute()
} }
} }
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 {
return this.om_0.y.toString() return this.om_0.y.toString()
} }
} }
canExecute(x = 1) { canExecute(x = 1) {
return true return true
} }
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,26 +133,28 @@ 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)))
} }
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

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -26,32 +26,46 @@ 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 { static displayType() {
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, return qsTranslate("bodemagnitudesum", "Bode Magnitudes Sum")
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number', }
}}
static displayTypeMultiple() {
constructor(name = null, visible = true, color = null, labelContent = 'name + value', return qsTranslate("bodemagnitudesum", "Bode Magnitudes Sum")
labelPosition = 'above', labelX = 1) { }
if(name == null) name = 'G'
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
this.recalculateCache() this.recalculateCache()
} }
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) {
for(let [limitedDrawFunction, inDrawDom] of this.cachedParts) { for(let [limitedDrawFunction, inDrawDom] of this.cachedParts) {
if(inDrawDom.includes(x)) { if(inDrawDom.includes(x)) {
@ -60,50 +74,50 @@ export default class BodeMagnitudeSum extends ExecutableObject {
} }
return null return null
} }
canExecute(x = 1) { canExecute(x = 1) {
return true // Should always be true return true // Should always be true
} }
simplify(x = 1) { simplify(x = 1) {
for(let [limitedDrawFunction, inDrawDom] of this.cachedParts) { for(let [limitedDrawFunction, inDrawDom] of this.cachedParts) {
if(inDrawDom.includes(x)) { if(inDrawDom.includes(x)) {
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)]]
const magnitudes = [] const magnitudes = []
const XVALUE = 0 const XVALUE = 0
const MAGNITUDE = 1 const MAGNITUDE = 1
const PASS = 2 const PASS = 2
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)
@ -129,7 +143,7 @@ export default class BodeMagnitudeSum extends ExecutableObject {
} }
} }
} }
draw(canvas) { draw(canvas) {
if(this.cachedParts.length > 0) { if(this.cachedParts.length > 0) {
for(let [limitedDrawFunction, drawDomain] of this.cachedParts) { for(let [limitedDrawFunction, drawDomain] of this.cachedParts) {

View file

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -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'), static displayType() {
[QT_TRANSLATE_NOOP('prop','phase')]: new P.Expression(), return qsTranslate("bodephase", "Bode Phase")
[QT_TRANSLATE_NOOP('prop','unit')]: new P.Enum('°', 'deg', 'rad'), }
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number' static displayTypeMultiple() {
}} return qsTranslate("bodephase", "Bode Phases")
}
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
om_0 = '', phase = 90, unit = '°', labelPosition = 'above', labelX = 1) { static properties() {
if(name == null) name = Objects.getNewName('φ') return {
if(name === 'φ') name = 'φ₀' // φ is reserved for sum of Bode phases. [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)
} }
@ -63,69 +74,71 @@ export default class BodePhase extends ExecutableObject {
this.labelPosition = labelPosition this.labelPosition = labelPosition
this.labelX = labelX this.labelX = labelX
} }
getReadableString() { getReadableString() {
return `${this.name}: ${this.phase.toString(true)}${this.unit} at ${this.om_0.name} = ${this.om_0.x}` return `${this.name}: ${this.phase.toString(true)}${this.unit} at ${this.om_0.name} = ${this.om_0.x}`
} }
getLatexString() { getLatexString() {
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 {
return this.om_0.y.execute() + this.phase.execute() return this.om_0.y.execute() + this.phase.execute()
} }
} }
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)
} }
} }
canExecute(x = 1) { canExecute(x = 1) {
return true return true
} }
draw(canvas) { draw(canvas) {
let baseX = canvas.x2px(this.om_0.x.execute()) let baseX = canvas.x2px(this.om_0.x.execute())
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.
canvas.drawLine(baseX, baseY, baseX, augmtY) canvas.drawLine(baseX, baseY, baseX, augmtY)
// After change line // After change line
canvas.drawLine(Math.max(0, baseX), augmtY, canvas.width, augmtY) canvas.drawLine(Math.max(0, baseX), augmtY, canvas.width, augmtY)
// 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)))
} }
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

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -24,101 +24,114 @@ 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 { static displayType() {
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, return qsTranslate("bodephasesum", "Bode Phases Sum")
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number', }
}}
static displayTypeMultiple() {
constructor(name = null, visible = true, color = null, labelContent = 'name + value', return qsTranslate("bodephasesum", "Bode Phases Sum")
labelPosition = 'above', labelX = 1) { }
if(name == null) name = 'φ'
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
this.recalculateCache() this.recalculateCache()
} }
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) {
return true return true
} }
recalculateCache() { recalculateCache() {
// 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.
let drawMin = 1e-20 let drawMin = 1e-20
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)
} }
// 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)))
} }

View file

@ -1,25 +1,25 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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 { 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,52 +32,64 @@ 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.
* *
* Properties are set with key as property name and * Properties are set with key as property name and
* value as it's type name (e.g 'numbers', 'string'...), * value as it's type name (e.g 'numbers', 'string'...),
* an Enum for enumerations, an ObjectType for DrawableObjects * an Enum for enumerations, an ObjectType for DrawableObjects
* with a specific type, a List instance for lists, a * with a specific type, a List instance for lists, a
* Dictionary instance for dictionaries, an Expression for expressions... * Dictionary instance for dictionaries, an Expression for expressions...
* *
* If the property is to be translated, the key should be passed * If the property is to be translated, the key should be passed
* through the QT_TRANSLATE_NOOP macro in that form: * through the QT_TRANSLATE_NOOP macro in that form:
* [QT_TRANSLATE_NOOP('prop','key')] * [QT_TRANSLATE_NOOP('prop','key')]
* Enums that are translated should be indexed in parameters.mjs and * Enums that are translated should be indexed in parameters.mjs and
* then be linked directly here. * then be linked directly here.
* *
* @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,23 +97,23 @@ 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)
} }
/** /**
* Base constructor for the object. * Base constructor for the object.
* @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,11 +132,11 @@ 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
} }
/** /**
* String representing the object that will be displayed to the user. * String representing the object that will be displayed to the user.
* It allows for 2 lines and translated text, but it shouldn't be too long. * It allows for 2 lines and translated text, but it shouldn't be too long.
@ -133,10 +145,10 @@ export class DrawableObject {
getReadableString() { getReadableString() {
return `${this.name} = Unknown` return `${this.name} = Unknown`
} }
/** /**
* Latex markuped version of the readable string. * Latex markuped version of the readable string.
* Every non latin character should be passed as latex symbols and formulas * Every non latin character should be passed as latex symbols and formulas
* should be in latex form. * should be in latex form.
* See ../latex.mjs for helper methods. * See ../latex.mjs for helper methods.
* @return {string} * @return {string}
@ -144,42 +156,42 @@ export class DrawableObject {
getLatexString() { getLatexString() {
return this.getReadableString() return this.getReadableString()
} }
/** /**
* Readable string content of the label depending on the value of the latexContent. * Readable string content of the label depending on the value of the latexContent.
* @return {string} * @return {string}
*/ */
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 ""
} }
} }
/** /**
* Latex markup string content of the label depending on the value of the latexContent. * Latex markup string content of the label depending on the value of the latexContent.
* Every non latin character should be passed as latex symbols and formulas * Every non latin character should be passed as latex symbols and formulas
* should be in latex form. * should be in latex form.
* See ../latex.mjs for helper methods. * See ../latex.mjs for helper methods.
* @return {string} * @return {string}
*/ */
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 ""
} }
} }
/** /**
* Returns the recursive list of objects this one depends on. * Returns the recursive list of objects this one depends on.
* @return {array} * @return {array}
@ -190,7 +202,7 @@ export class DrawableObject {
dependencies = dependencies.concat(obj.getDependenciesList()) dependencies = dependencies.concat(obj.getDependenciesList())
return dependencies return dependencies
} }
/** /**
* Callback method when one of the properties of the object is updated. * Callback method when one of the properties of the object is updated.
*/ */
@ -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])) {
@ -215,8 +227,8 @@ export class DrawableObject {
if(this[property].cached && this[property].requiredObjects().length > 0) if(this[property].cached && this[property].requiredObjects().length > 0)
// 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)
@ -225,7 +237,7 @@ export class DrawableObject {
for(let req of this.requiredBy) for(let req of this.requiredBy)
req.update() req.update()
} }
/** /**
* Callback method when the object is about to get deleted. * Callback method when the object is about to get deleted.
*/ */
@ -237,64 +249,65 @@ export class DrawableObject {
toRemoveFrom.requiredBy = toRemoveFrom.requiredBy.filter(o => o !== this) toRemoveFrom.requiredBy = toRemoveFrom.requiredBy.filter(o => o !== this)
} }
} }
/** /**
* 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
* both the posX and posY of where the label should be displayed, * both the posX and posY of where the label should be displayed,
* and the labelPosition which declares the label should be displayed * and the labelPosition which declares the label should be displayed
* relatively to that position. * relatively to that position.
* *
* @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
} }
} }
/** /**
* Automatically draw text (by default the label of the object on the canvas * Automatically draw text (by default the label of the object on the canvas
* depending on user settings. * depending on user settings.
@ -304,55 +317,54 @@ export class DrawableObject {
* depending on whether to use latex. * depending on whether to use latex.
* Then, it's displayed using the drawFunctionLatex (x,y,imageData) and * Then, it's displayed using the drawFunctionLatex (x,y,imageData) and
* drawFunctionText (x,y,text) depending on whether to use latex. * drawFunctionText (x,y,text) depending on whether to use latex.
* *
* @param {CanvasAPI} canvas * @param {CanvasAPI} canvas
* @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} 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 {boolean} forceText - Force the rendering of the label as text * @param {boolean} forceText - Force the rendering of the label as text
* @param {function|null} getLatexFunction - Function (no argument) to get the latex markup to be displayed * @param {function|null} getLatexFunction - Function (no argument) to get the latex markup to be displayed
* @param {function|null} getTextFunction - Function (no argument) to get the text to be displayed * @param {function|null} getTextFunction - Function (no argument) to get the text to be displayed
* @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)
getLatexFunction = this.getLatexLabel.bind(this) getLatexFunction = this.getLatexLabel.bind(this)
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
} }
} }
/** /**
* Class to be extended for every object on which * Class to be extended for every object on which
* an y for a x can be computed with the execute function. * an y for a x can be computed with the execute function.
* If a value cannot be found during execute, it will * If a value cannot be found during execute, it will
* return null. However, theses same x values will * return null. However, theses same x values will
@ -363,32 +375,42 @@ export class ExecutableObject extends DrawableObject {
* Returns the corresponding y value for an x value. * Returns the corresponding y value for an x value.
* If the object isn't defined on the given x, then * If the object isn't defined on the given x, then
* this function will return null. * this function will return null.
* *
* @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

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -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.' }
),
[QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'double', /^-?[\d.,]+$/, /^-?[\d.,]+$/, 'P({name_} = ', ') = '), static displayTypeMultiple() {
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, return qsTranslate("distribution", "Repartition functions")
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number', }
}}
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", "labelPosition")]: P.Enum.Position,
[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) {
@ -45,99 +57,101 @@ 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
this.labelX = labelX this.labelX = labelX
this.update() this.update()
} }
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) {
return this.execute(x).toString() return this.execute(x).toString()
} }
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
) )
} }
// 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)))
} }

View file

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -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'), static displayType() {
[QT_TRANSLATE_NOOP('prop','definitionDomain')]: 'Domain', return qsTranslate("function", "Function")
[QT_TRANSLATE_NOOP('prop','destinationDomain')]: 'Domain', }
'comment1': QT_TRANSLATE_NOOP(
'comment', static displayTypeMultiple() {
'Ex: R+* (ℝ⁺*), N (), Z-* (ℤ⁻*), ]0;1[, {3;4;5}' return qsTranslate("function", "Functions")
), }
[QT_TRANSLATE_NOOP('prop','displayMode')]: P.Enum.FunctionDisplayType,
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, static properties() {
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number', return {
'comment2': QT_TRANSLATE_NOOP( [QT_TRANSLATE_NOOP("prop", "expression")]: new P.Expression("x"),
'comment', [QT_TRANSLATE_NOOP("prop", "definitionDomain")]: "Domain",
'The following parameters are used when the definition domain is a non-continuous set. (Ex: , , sets like {0;3}...)' [QT_TRANSLATE_NOOP("prop", "destinationDomain")]: "Domain",
), "comment1": QT_TRANSLATE_NOOP(
[QT_TRANSLATE_NOOP('prop','drawPoints')]: 'boolean', "comment",
[QT_TRANSLATE_NOOP('prop','drawDashedLines')]: 'boolean' "Ex: R+* (ℝ⁺*), N (), Z-* (ℤ⁻*), ]0;1[, {3;4;5}"
}} ),
[QT_TRANSLATE_NOOP("prop", "displayMode")]: P.Enum.FunctionDisplayType,
constructor(name = null, visible = true, color = null, labelContent = 'name + value', [QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
expression = 'x', definitionDomain = 'RPE', destinationDomain = 'R', [QT_TRANSLATE_NOOP("prop", "labelX")]: "number",
displayMode = 'application', labelPosition = 'above', labelX = 1, "comment2": QT_TRANSLATE_NOOP(
drawPoints = true, drawDashedLines = true) { "comment",
if(name == null) name = Objects.getNewName('fghjqlmnopqrstuvwabcde') "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) {
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
@ -65,17 +76,17 @@ export default class Function extends ExecutableObject {
this.drawPoints = drawPoints this.drawPoints = drawPoints
this.drawDashedLines = drawDashedLines this.drawDashedLines = drawDashedLines
} }
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 {
@ -88,17 +99,17 @@ export default class Function extends ExecutableObject {
return this.expression.execute(x) return this.expression.execute(x)
return null return null
} }
canExecute(x = 1) { canExecute(x = 1) {
return this.definitionDomain.includes(x) return this.definitionDomain.includes(x)
} }
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) {
Function.drawFunction(canvas, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines) Function.drawFunction(canvas, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines)
// Label // Label
@ -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

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -24,53 +24,63 @@ import Latex from "../module/latex.mjs"
import { DrawableObject } from "./common.mjs" 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('●', '✕', '') static displayTypeMultiple() {
}} return qsTranslate("point", "Points")
}
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
x = 1, y = 0, labelPosition = 'above', pointStyle = '●') { static properties() {
if(name == null) name = Objects.getNewName('ABCDEFJKLMNOPQRSTUVW') 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
} }
getReadableString() { getReadableString() {
return `${this.name} = (${this.x}, ${this.y})` return `${this.name} = (${this.x}, ${this.y})`
} }
getLatexString() { getLatexString() {
return `${Latex.variable(this.name)} = \\left(${this.x.latexMarkup}, ${this.y.latexMarkup}\\right)` return `${Latex.variable(this.name)} = \\left(${this.x.latexMarkup}, ${this.y.latexMarkup}\\right)`
} }
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

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -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ₙ₊₁...' }
),
[QT_TRANSLATE_NOOP('prop','baseValues')]: new P.Dictionary('string', 'int', /^.+$/, /^\d+$/, '{name}[', '] = '), static properties() {
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, return {
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number', [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),
constructor(name = null, visible = true, color = null, labelContent = 'name + value', "comment1": QT_TRANSLATE_NOOP(
drawPoints = true, drawDashedLines = true, defaultExp = {1: "n"}, "comment",
baseValues = {0: 0}, labelPosition = 'above', labelX = 1) { "Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁..."
if(name == null) name = Objects.getNewName('uvwPSUVWabcde') ),
[QT_TRANSLATE_NOOP("prop", "baseValues")]: new P.Dictionary("string", "int", /^.+$/, /^\d+$/, "{name}[", "] = "),
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
[QT_TRANSLATE_NOOP("prop", "labelX")]: "number"
}
}
constructor(name = null, visible = true, color = null, labelContent = "name + value",
drawPoints = true, drawDashedLines = true, defaultExp = { 1: "n" },
baseValues = { 0: 0 }, labelPosition = "above", labelX = 1) {
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,68 +68,71 @@ 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]
) )
} }
getReadableString() { getReadableString() {
return this.sequence.toString() return this.sequence.toString()
} }
getLatexString() { getLatexString() {
return this.sequence.toLatexString() return this.sequence.toLatexString()
} }
execute(x = 1) { execute(x = 1) {
if(x % 1 === 0) if(x % 1 === 0)
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) {
if(x % 1 === 0) if(x % 1 === 0)
return this.sequence.simplify(x) return this.sequence.simplify(x)
return null return null
} }
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 ""
} }
} }
draw(canvas) { draw(canvas) {
Function.drawFunction(canvas, this.sequence, canvas.logscalex ? Domain.NE : Domain.N, Domain.R, this.drawPoints, this.drawDashedLines) Function.drawFunction(canvas, this.sequence, canvas.logscalex ? Domain.NE : Domain.N, Domain.R, this.drawPoints, this.drawDashedLines)
// 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)))
} }

View file

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -24,72 +24,85 @@ import Latex from "../module/latex.mjs"
import { DrawableObject } from "./common.mjs" 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(), static displayType() {
[QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(), return qsTranslate("text", "Text")
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning, }
[QT_TRANSLATE_NOOP('prop','text')]: 'string',
'comment1': QT_TRANSLATE_NOOP( static displayTypeMultiple() {
'comment', return qsTranslate("text", "Texts")
'If you have latex enabled, you can use use latex markup in between $$ to create equations.' }
),
[QT_TRANSLATE_NOOP('prop','disableLatex')]: 'boolean' static properties() {
}} return {
[QT_TRANSLATE_NOOP("prop", "x")]: new P.Expression(),
constructor(name = null, visible = true, color = null, labelContent = 'null', [QT_TRANSLATE_NOOP("prop", "y")]: new P.Expression(),
x = 1, y = 0, labelPosition = 'center', text = 'New text', disableLatex = false) { [QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Positioning,
if(name == null) name = Objects.getNewName('t') [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
this.disableLatex = disableLatex this.disableLatex = disableLatex
} }
getReadableString() { getReadableString() {
return `${this.name} = "${this.text}"` return `${this.name} = "${this.text}"`
} }
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 += "{"
return newTxt return newTxt
} }
getLatexString() { getLatexString() {
return `${Latex.variable(this.name)} = "\\textsf{${this.latexMarkupText()}}"` return `${Latex.variable(this.name)} = "\\textsf{${this.latexMarkupText()}}"`
} }
getLabel() { getLabel() {
return this.text return this.text
} }
getLatexLabel() { getLatexLabel() {
return `\\textsf{${this.latexMarkupText()}}` return `\\textsf{${this.latexMarkupText()}}`
} }
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

@ -72,30 +72,29 @@ export default class XCursor extends DrawableObject {
${this.getTargetValueLatexLabel()} ${this.getTargetValueLatexLabel()}
\\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,28 +112,28 @@ 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
} }
toString() { toString() {
return this.objType return this.objType
} }
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,28 +157,28 @@ 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
} }
toString() { toString() {
return `${this.type}(${this.valueType}:${this.format})` return `${this.type}(${this.valueType}:${this.format})`
} }
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

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -19,30 +19,30 @@
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) {
throw new SyntaxError(message + " at " + this.position + ".") throw new SyntaxError(message + " at " + this.position + ".")
} }

View file

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -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,78 +36,78 @@ 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
this.multipleAllowed = multipleAllowed this.multipleAllowed = multipleAllowed
} }
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

@ -1,17 +1,17 @@
/** /**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001 * Copyright (C) 2021-2024 Ad5001
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* 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/>.
*/ */
@ -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,120 +55,120 @@ 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 {
this.input.raise("Unexpected " + delimitation + ". Expected string delimitator") this.input.raise("Unexpected " + delimitation + ". Expected string delimitator")
} }
} }
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
// })
] ]
} }