Converting as many JS libraries to ECMAScript modules.
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Adsooi 2024-03-28 03:09:37 +01:00
parent a6fcf6da19
commit 08fea34366
Signed by: Ad5001
GPG key ID: EF45F9C6AFE20160
68 changed files with 2604 additions and 4255 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,2 +1,2 @@
#!/bin/bash #!/bin/bash
lupdate -extensions js,qs,qml,py -recursive .. -ts lp_*.ts lupdate -extensions mjs,js,qs,qml,py -recursive .. -ts lp_*.ts

View file

@ -112,6 +112,7 @@ def run():
global tmpfile global tmpfile
helper = Helper(pwd, tmpfile) helper = Helper(pwd, tmpfile)
latex = Latex(tempdir) latex = Latex(tempdir)
engine.globalObject().setProperty('Runtime', engine.newObject())
engine.rootContext().setContextProperty("Helper", helper) engine.rootContext().setContextProperty("Helper", helper)
engine.rootContext().setContextProperty("Latex", latex) engine.rootContext().setContextProperty("Latex", latex)
engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv) engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv)

View file

@ -20,9 +20,7 @@ import QtQuick
import Qt.labs.platform as Native import Qt.labs.platform as Native
//import QtQuick.Controls 2.15 //import QtQuick.Controls 2.15
import eu.ad5001.MixedMenu 1.1 import eu.ad5001.MixedMenu 1.1
import "js/objects.js" as Objects import "js/historylib.mjs" as HistoryLib
import "js/historylib.js" as HistoryLib
import "js/math/latex.js" as LatexJS
/*! /*!
@ -105,17 +103,17 @@ MenuBar {
title: qsTr("&Create") title: qsTr("&Create")
// Services repeater // Services repeater
Repeater { Repeater {
model: Object.keys(Objects.types) model: Object.keys(Runtime.Objects.types)
MenuItem { MenuItem {
text: Objects.types[modelData].displayType() text: Runtime.Objects.types[modelData].displayType()
visible: Objects.types[modelData].createable() visible: Runtime.Objects.types[modelData].createable()
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
icon.name: modelData icon.name: modelData
icon.source: './icons/objects/' + modelData + '.svg' icon.source: './icons/objects/' + modelData + '.svg'
icon.color: sysPalette.buttonText icon.color: sysPalette.buttonText
onTriggered: { onTriggered: {
var newObj = Objects.createNewRegisteredObject(modelData) var newObj = Runtime.Objects.createNewRegisteredObject(modelData)
history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export())) history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export()))
objectLists.update() objectLists.update()
} }
@ -153,7 +151,7 @@ MenuBar {
checked: Helper.getSettingBool("enable_latex") checked: Helper.getSettingBool("enable_latex")
onTriggered: { onTriggered: {
Helper.setSettingBool("enable_latex", checked) Helper.setSettingBool("enable_latex", checked)
LatexJS.enabled = checked Runtime.Latex.enabled = checked
drawCanvas.requestPaint() drawCanvas.requestPaint()
} }
icon.name: 'Expression' icon.name: 'Expression'

View file

@ -19,9 +19,7 @@
import QtQuick import QtQuick
import QtQml import QtQml
import QtQuick.Window import QtQuick.Window
import "../js/objects.js" as Objects import "../js/historylib.mjs" as HistoryLib
import "../js/historylib.js" as HistoryLib
import "../js/history/common.js" as HistoryCommon
/*! /*!
\qmltype History \qmltype History
@ -214,8 +212,8 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
HistoryLib.history = historyObj Runtime.History.history = historyObj
HistoryCommon.themeTextColor = sysPalette.windowText Runtime.History.themeTextColor = sysPalette.windowText
HistoryCommon.imageDepth = Screen.devicePixelRatio Runtime.History.imageDepth = Screen.devicePixelRatio
} }
} }

View file

@ -19,7 +19,7 @@
import QtQuick.Controls import QtQuick.Controls
import QtQuick import QtQuick
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
import "../js/utils.js" as Utils import "../js/utils.mjs" as Utils
/*! /*!

View file

@ -19,7 +19,7 @@
import QtQuick.Controls import QtQuick.Controls
import QtQuick import QtQuick
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import "../js/utils.js" as Utils import "../js/utils.mjs" as Utils
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting

View file

@ -18,9 +18,8 @@
import QtQuick import QtQuick
import Qt.labs.platform as Native import Qt.labs.platform as Native
import "js/objects.js" as Objects import "js/utils.mjs" as Utils
import "js/utils.js" as Utils import "js/mathlib.mjs" as MathLib
import "js/mathlib.js" as MathLib
/*! /*!
\qmltype LogGraphCanvas \qmltype LogGraphCanvas
@ -190,8 +189,8 @@ Canvas {
drawAxises(ctx) drawAxises(ctx)
drawLabels(ctx) drawLabels(ctx)
ctx.lineWidth = linewidth ctx.lineWidth = linewidth
for(var objType in Objects.currentObjects) { for(var objType in Runtime.Objects.currentObjects) {
for(var obj of Objects.currentObjects[objType]){ for(var obj of Runtime.Objects.currentObjects[objType]){
ctx.strokeStyle = obj.color ctx.strokeStyle = obj.color
ctx.fillStyle = obj.color ctx.fillStyle = obj.color
if(obj.visible) if(obj.visible)

View file

@ -21,11 +21,10 @@ import QtQuick.Controls
import eu.ad5001.MixedMenu 1.1 import eu.ad5001.MixedMenu 1.1
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12
import QtQuick import QtQuick
// Auto loading all objects.
import "js/objs/autoload.js" as ALObjects
import "js/objects.js" as Objects // Auto loading all modules.
import "js/math/latex.js" as LatexJS import "js/modules.js" as Modules
import eu.ad5001.LogarithmPlotter.History 1.0 import eu.ad5001.LogarithmPlotter.History 1.0
import eu.ad5001.LogarithmPlotter.ObjectLists 1.0 import eu.ad5001.LogarithmPlotter.ObjectLists 1.0
import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup
@ -50,9 +49,9 @@ ApplicationWindow {
Component.onCompleted: { Component.onCompleted: {
// LatexJS initialization. // LatexJS initialization.
LatexJS.enabled = Helper.getSettingBool("enable_latex") Runtime.Latex.enabled = Helper.getSettingBool("enable_latex")
LatexJS.Renderer = Latex Runtime.Latex.Renderer = Latex
LatexJS.defaultColor = sysPalette.windowText Runtime.Latex.defaultColor = sysPalette.windowText
} }
} }
SystemPalette { id: sysPaletteIn; colorGroup: SystemPalette.Disabled } SystemPalette { id: sysPaletteIn; colorGroup: SystemPalette.Disabled }
@ -201,9 +200,9 @@ ApplicationWindow {
filename += '.lpf' filename += '.lpf'
settings.saveFilename = filename settings.saveFilename = filename
var objs = {} var objs = {}
for(var objType in Objects.currentObjects){ for(var objType in Runtime.Objects.currentObjects){
objs[objType] = [] objs[objType] = []
for(var obj of Objects.currentObjects[objType]) { for(var obj of Runtime.Objects.currentObjects[objType]) {
objs[objType].push(obj.export()) objs[objType].push(obj.export())
} }
} }
@ -265,19 +264,19 @@ ApplicationWindow {
root.width = data["width"] root.width = data["width"]
// Importing objects // Importing objects
Objects.currentObjects = {} Runtime.Objects.currentObjects = {}
Object.keys(Objects.currentObjectsByName).forEach(key => { Runtime.Object.keys(Objects.currentObjectsByName).forEach(key => {
delete Objects.currentObjectsByName[key]; delete Runtime.Objects.currentObjectsByName[key];
// Required to keep the same reference for the copy of the object used in expression variable detection. // Required to keep the same reference for the copy of the object used in expression variable detection.
// Another way would be to change the reference as well, but I feel like the code would be less clean. // Another way would be to change the reference as well, but I feel like the code would be less clean.
}) })
for(let objType in data['objects']) { for(let objType in data['objects']) {
if(Object.keys(Objects.types).indexOf(objType) > -1) { if(Object.keys(Runtime.Objects.types).indexOf(objType) > -1) {
Objects.currentObjects[objType] = [] Runtime.Objects.currentObjects[objType] = []
for(let objData of data['objects'][objType]) { for(let objData of data['objects'][objType]) {
let obj = new Objects.types[objType](...objData) let obj = new Runtime.Objects.types[objType](...objData)
Objects.currentObjects[objType].push(obj) Runtime.Objects.currentObjects[objType].push(obj)
Objects.currentObjectsByName[obj.name] = obj Runtime.Objects.currentObjectsByName[obj.name] = obj
} }
} else { } else {
error += qsTr("Unknown object type: %1.").arg(objType) + "\n"; error += qsTr("Unknown object type: %1.").arg(objType) + "\n";
@ -285,8 +284,8 @@ ApplicationWindow {
} }
// Updating object dependencies. // Updating object dependencies.
for(let objName in Objects.currentObjectsByName) for(let objName in Runtime.Objects.currentObjectsByName)
Objects.currentObjectsByName[objName].update() Runtime.Objects.currentObjectsByName[objName].update()
// Importing history // Importing history
if("history" in data) if("history" in data)

View file

@ -20,10 +20,9 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import Qt.labs.platform as Native import Qt.labs.platform as Native
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
import "../../js/objects.js" as Objects import "../../js/historylib.mjs" as HistoryLib
import "../../js/historylib.js" as HistoryLib import "../../js/utils.mjs" as Utils
import "../../js/utils.js" as Utils import "../../js/mathlib.mjs" as MathLib
import "../../js/mathlib.js" as MathLib
/*! /*!
\qmltype CustomPropertyList \qmltype CustomPropertyList
@ -188,8 +187,8 @@ Repeater {
// Base, untranslated version of the model. // Base, untranslated version of the model.
property var baseModel: selectObjMode ? property var baseModel: selectObjMode ?
Objects.getObjectsName(propertyType.objType).concat( Runtime.Objects.getObjectsName(propertyType.objType).concat(
isRealObject ? [qsTr("+ Create new %1").arg(Objects.types[propertyType.objType].displayType())] : []) isRealObject ? [qsTr("+ Create new %1").arg(Runtime.Objects.types[propertyType.objType].displayType())] : [])
: propertyType.values : propertyType.values
// Translated version of the model. // Translated version of the model.
model: selectObjMode ? baseModel : propertyType.translatedValues model: selectObjMode ? baseModel : propertyType.translatedValues
@ -199,20 +198,20 @@ Repeater {
if(selectObjMode) { if(selectObjMode) {
// This is only done when what we're selecting are Objects. // This is only done when what we're selecting are Objects.
// Setting object property. // Setting object property.
var selectedObj = Objects.currentObjectsByName[baseModel[newIndex]] var selectedObj = Runtime.Objects.currentObjectsByName[baseModel[newIndex]]
if(newIndex != 0) { if(newIndex != 0) {
// Make sure we don't set the object to null. // Make sure we don't set the object to null.
if(selectedObj == null) { if(selectedObj == null) {
// Creating new object. // Creating new object.
selectedObj = Objects.createNewRegisteredObject(propertyType.objType) selectedObj = Runtime.Objects.createNewRegisteredObject(propertyType.objType)
history.addToHistory(new HistoryLib.CreateNewObject(selectedObj.name, propertyType.objType, selectedObj.export())) history.addToHistory(new HistoryLib.CreateNewObject(selectedObj.name, propertyType.objType, selectedObj.export()))
baseModel = Objects.getObjectsName(propertyType.objType).concat( baseModel = Runtime.Objects.getObjectsName(propertyType.objType).concat(
isRealObject ? [qsTr("+ Create new %1").arg(Objects.types[propertyType.objType].displayType())] : isRealObject ? [qsTr("+ Create new %1").arg(Runtime.Objects.types[propertyType.objType].displayType())] :
[]) [])
currentIndex = baseModel.indexOf(selectedObj.name) currentIndex = baseModel.indexOf(selectedObj.name)
} }
selectedObj.requiredBy.push(Objects.currentObjects[objType][objIndex]) selectedObj.requiredBy.push(Runtime.Objects.currentObjects[objType][objIndex])
//Objects.currentObjects[objType][objIndex].requiredBy = obj[propertyName].filter((obj) => obj.name != obj.name) //Runtime.Objects.currentObjects[objType][objIndex].requiredBy = obj[propertyName].filter((obj) => obj.name != obj.name)
} }
obj.requiredBy = obj.requiredBy.filter((obj) => obj.name != obj.name) obj.requiredBy = obj.requiredBy.filter((obj) => obj.name != obj.name)
history.addToHistory(new HistoryLib.EditedProperty( history.addToHistory(new HistoryLib.EditedProperty(
@ -256,7 +255,7 @@ Repeater {
obj.name, objType, propertyName, obj.name, objType, propertyName,
obj[propertyName], exported obj[propertyName], exported
)) ))
//Objects.currentObjects[objType][objIndex][propertyName] = exported //Runtime.Objects.currentObjects[objType][objIndex][propertyName] = exported
obj[propertyName] = exported obj[propertyName] = exported
root.changed() root.changed()
} }

View file

@ -22,11 +22,9 @@ import QtQuick.Dialogs as D
import Qt.labs.platform as Native import Qt.labs.platform as Native
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup
import "../../js/objects.js" as Objects import "../../js/historylib.mjs" as HistoryLib
import "../../js/objs/common.js" as ObjectsCommons import "../../js/utils.mjs" as Utils
import "../../js/historylib.js" as HistoryLib import "../../js/mathlib.mjs" as MathLib
import "../../js/utils.js" as Utils
import "../../js/mathlib.js" as MathLib
/*! /*!
\qmltype Dialog \qmltype Dialog
@ -54,7 +52,7 @@ Popup.BaseDialog {
\qmlproperty var EditorDialog::obj \qmlproperty var EditorDialog::obj
Instance of the object being edited. Instance of the object being edited.
*/ */
property var obj: Objects.currentObjects[objType][objIndex] property var obj: Runtime.Objects.currentObjects[objType][objIndex]
/*! /*!
\qmlproperty var EditorDialog::posPicker \qmlproperty var EditorDialog::posPicker
Reference to the global PositionPicker QML object. Reference to the global PositionPicker QML object.
@ -87,7 +85,7 @@ Popup.BaseDialog {
Label { Label {
id: dlgTitle id: dlgTitle
verticalAlignment: TextInput.AlignVCenter verticalAlignment: TextInput.AlignVCenter
text: qsTr("Edit properties of %1 %2").arg(Objects.types[objEditor.objType].displayType()).arg(objEditor.obj.name) text: qsTr("Edit properties of %1 %2").arg(Runtime.Objects.types[objEditor.objType].displayType()).arg(objEditor.obj.name)
font.pixelSize: 20 font.pixelSize: 20
color: sysPalette.windowText color: sysPalette.windowText
} }
@ -113,14 +111,14 @@ Popup.BaseDialog {
onChanged: function(newValue) { onChanged: function(newValue) {
let newName = Utils.parseName(newValue) let newName = Utils.parseName(newValue)
if(newName != '' && objEditor.obj.name != newName) { if(newName != '' && objEditor.obj.name != newName) {
if(newName in Objects.currentObjectsByName) { if(newName in Runtime.Objects.currentObjectsByName) {
invalidNameDialog.showDialog(newName) invalidNameDialog.showDialog(newName)
} else { } else {
history.addToHistory(new HistoryLib.NameChanged( history.addToHistory(new HistoryLib.NameChanged(
objEditor.obj.name, objEditor.objType, newName objEditor.obj.name, objEditor.objType, newName
)) ))
Objects.renameObject(obj.name, newName) Runtime.Objects.renameObject(obj.name, newName)
objEditor.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex] objEditor.obj = Runtime.Objects.currentObjects[objEditor.objType][objEditor.objIndex]
objectListList.update() objectListList.update()
} }
} }
@ -165,7 +163,7 @@ Popup.BaseDialog {
*/ */
function open() { function open() {
dlgCustomProperties.model = [] // Reset dlgCustomProperties.model = [] // Reset
let objProps = Objects.types[objEditor.objType].properties() let objProps = Runtime.Objects.types[objEditor.objType].properties()
dlgCustomProperties.model = Object.keys(objProps).map(prop => [prop, objProps[prop]]) // Converted to 2-dimentional array. dlgCustomProperties.model = Object.keys(objProps).map(prop => [prop, objProps[prop]]) // Converted to 2-dimentional array.
objEditor.show() objEditor.show()
} }

View file

@ -18,8 +18,7 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import "../js/objects.js" as Objects import "../js/historylib.mjs" as HistoryLib
import "../js/historylib.js" as HistoryLib
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
@ -44,7 +43,7 @@ Column {
// Open editor // Open editor
objectEditor.obj = obj objectEditor.obj = obj
objectEditor.objType = obj.type objectEditor.objType = obj.type
objectEditor.objIndex = Objects.currentObjects[obj.type].indexOf(obj) objectEditor.objIndex = Runtime.Objects.currentObjects[obj.type].indexOf(obj)
objectEditor.open() objectEditor.open()
// Disconnect potential link // Disconnect potential link
posPicker.picked.disconnect(openEditorDialog) posPicker.picked.disconnect(openEditorDialog)
@ -61,12 +60,12 @@ Column {
width: parent.width width: parent.width
columns: 3 columns: 3
Repeater { Repeater {
model: Object.keys(Objects.types) model: Object.keys(Runtime.Objects.types)
Button { Button {
id: createBtn id: createBtn
width: 96 width: 96
visible: Objects.types[modelData].createable() visible: Runtime.Objects.types[modelData].createable()
height: visible ? width*0.8 : 0 height: visible ? width*0.8 : 0
// The KDE SDK is kinda buggy, so it respects neither specified color nor display propreties. // The KDE SDK is kinda buggy, so it respects neither specified color nor display propreties.
//display: AbstractButton.TextUnderIcon //display: AbstractButton.TextUnderIcon
@ -94,7 +93,7 @@ Column {
anchors.rightMargin: 4 anchors.rightMargin: 4
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
font.pixelSize: 14 font.pixelSize: 14
text: Objects.types[modelData].displayType() text: Runtime.Objects.types[modelData].displayType()
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
clip: true clip: true
} }
@ -104,7 +103,7 @@ Column {
ToolTip.text: label.text ToolTip.text: label.text
onClicked: { onClicked: {
let newObj = Objects.createNewRegisteredObject(modelData) let newObj = Runtime.Objects.createNewRegisteredObject(modelData)
history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export())) history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export()))
objectLists.update() objectLists.update()

View file

@ -21,7 +21,6 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
import eu.ad5001.LogarithmPlotter.ObjectLists.Editor 1.0 as Editor import eu.ad5001.LogarithmPlotter.ObjectLists.Editor 1.0 as Editor
import "../js/objects.js" as Objects
/*! /*!
\qmltype ObjectLists \qmltype ObjectLists
@ -47,7 +46,7 @@ ScrollView {
ListView { ListView {
id: objectsListView id: objectsListView
model: Object.keys(Objects.types) model: Object.keys(Runtime.Objects.types)
//width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0) //width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0)
implicitHeight: contentItem.childrenRect.height + footerItem.height + 10 implicitHeight: contentItem.childrenRect.height + footerItem.height + 10
@ -55,7 +54,7 @@ ScrollView {
id: objTypeList id: objTypeList
property string objType: objectsListView.model[index] property string objType: objectsListView.model[index]
property var editingRows: [] property var editingRows: []
model: Objects.currentObjects[objType] model: Runtime.Objects.currentObjects[objType]
width: objectsListView.width width: objectsListView.width
implicitHeight: contentItem.childrenRect.height implicitHeight: contentItem.childrenRect.height
visible: model != undefined && model.length > 0 visible: model != undefined && model.length > 0
@ -70,21 +69,23 @@ ScrollView {
CheckBox { CheckBox {
id: typeVisibilityCheckBox id: typeVisibilityCheckBox
checked: Objects.currentObjects[objType] != undefined ? Objects.currentObjects[objType].every(obj => obj.visible) : true checked: Runtime.Objects.currentObjects[objType] != undefined ? Runtime.Objects.currentObjects[objType].every(obj => obj.visible) : true
onClicked: { onClicked: {
for(var obj of Objects.currentObjects[objType]) obj.visible = this.checked for(var obj of Runtime.Objects.currentObjects[objType]) obj.visible = this.checked
for(var obj of objTypeList.editingRows) obj.objVisible = this.checked for(var obj of objTypeList.editingRows) obj.objVisible = this.checked
objectListList.changed() objectListList.changed()
} }
ToolTip.visible: hovered ToolTip.visible: hovered
ToolTip.text: checked ? qsTr("Hide all %1").arg(Objects.types[objType].displayTypeMultiple()) : qsTr("Show all %1").arg(Objects.types[objType].displayTypeMultiple()) ToolTip.text: checked ?
qsTr("Hide all %1").arg(Runtime.Objects.types[objType].displayTypeMultiple()) :
qsTr("Show all %1").arg(Runtime.Objects.types[objType].displayTypeMultiple())
} }
Label { Label {
id: typeHeaderText id: typeHeaderText
verticalAlignment: TextInput.AlignVCenter verticalAlignment: TextInput.AlignVCenter
text: qsTranslate("control", "%1: ").arg(Objects.types[objType].displayTypeMultiple()) text: qsTranslate("control", "%1: ").arg(Runtime.Objects.types[objType].displayTypeMultiple())
font.pixelSize: 20 font.pixelSize: 20
} }
} }
@ -92,11 +93,11 @@ ScrollView {
delegate: ObjectRow { delegate: ObjectRow {
id: controlRow id: controlRow
width: objTypeList.width width: objTypeList.width
obj: Objects.currentObjects[objType][index] obj: Runtime.Objects.currentObjects[objType][index]
posPicker: positionPicker posPicker: positionPicker
onChanged: { onChanged: {
obj = Objects.currentObjects[objType][index] obj = Runtime.Objects.currentObjects[objType][index]
objectListList.update() objectListList.update()
} }
@ -128,7 +129,7 @@ ScrollView {
function update() { function update() {
objectListList.changed() objectListList.changed()
for(var objType in objectListList.listViews) { for(var objType in objectListList.listViews) {
objectListList.listViews[objType].model = Objects.currentObjects[objType] objectListList.listViews[objType].model = Runtime.Objects.currentObjects[objType]
} }
} }

View file

@ -21,9 +21,7 @@ import QtQuick.Dialogs
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Window import QtQuick.Window
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
import "../js/objects.js" as Objects import "../js/historylib.mjs" as HistoryLib
import "../js/historylib.js" as HistoryLib
import "../js/math/latex.js" as LatexJS
/*! /*!
@ -91,16 +89,16 @@ Item {
id: objDescription id: objDescription
anchors.left: objVisibilityCheckBox.right anchors.left: objVisibilityCheckBox.right
anchors.right: deleteButton.left anchors.right: deleteButton.left
height: LatexJS.enabled ? Math.max(parent.minHeight, latexDescription.height+4) : parent.minHeight height: Runtime.Latex.enabled ? Math.max(parent.minHeight, latexDescription.height+4) : parent.minHeight
verticalAlignment: TextInput.AlignVCenter verticalAlignment: TextInput.AlignVCenter
text: LatexJS.enabled ? "" : obj.getReadableString() text: Runtime.Latex.enabled ? "" : obj.getReadableString()
font.pixelSize: 14 font.pixelSize: 14
Image { Image {
id: latexDescription id: latexDescription
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left anchors.left: parent.left
visible: LatexJS.enabled visible: Runtime.Latex.enabled
property double depth: Screen.devicePixelRatio property double depth: Screen.devicePixelRatio
property var ltxInfo: visible ? Latex.render(obj.getLatexString(), depth*(parent.font.pixelSize+2), parent.color).split(",") : ["","0","0"] property var ltxInfo: visible ? Latex.render(obj.getLatexString(), depth*(parent.font.pixelSize+2), parent.color).split(",") : ["","0","0"]
source: visible ? ltxInfo[0] : "" source: visible ? ltxInfo[0] : ""
@ -111,7 +109,7 @@ Item {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
objEditor.obj = Objects.currentObjects[obj.type][index] objEditor.obj = Runtime.Objects.currentObjects[obj.type][index]
objEditor.objType = obj.type objEditor.objType = obj.type
objEditor.objIndex = index objEditor.objIndex = index
//objEditor.editingRow = objectRow //objEditor.editingRow = objectRow
@ -213,10 +211,14 @@ Item {
function deleteRecursively(object) { function deleteRecursively(object) {
for(let toRemove of object.requiredBy) for(let toRemove of object.requiredBy)
deleteRecursively(toRemove) deleteRecursively(toRemove)
object.requiredBy = [] if(Runtime.Objects.currentObjectsByName[object.name] != undefined) {
history.addToHistory(new HistoryLib.DeleteObject( // Object still exists
object.name, object.type, object.export() // Temporary fix for objects require not being propertly updated.
)) object.requiredBy = []
Objects.deleteObject(object.name) history.addToHistory(new HistoryLib.DeleteObject(
object.name, object.type, object.export()
))
Runtime.Objects.deleteObject(object.name)
}
} }
} }

View file

@ -19,9 +19,8 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
import "js/objects.js" as Objects import "js/mathlib.mjs" as MathLib
import "js/mathlib.js" as MathLib import "js/historylib.mjs" as HistoryLib
import "js/historylib.js" as HistoryLib
/*! /*!
\qmltype PickLocationOverlay \qmltype PickLocationOverlay
@ -114,7 +113,7 @@ Item {
if(mouse.button == Qt.LeftButton) { // Validate if(mouse.button == Qt.LeftButton) { // Validate
let newValueX = !parent.userPickX ? null : parseValue(picked.mouseX.toString(), objType, propertyX) let newValueX = !parent.userPickX ? null : parseValue(picked.mouseX.toString(), objType, propertyX)
let newValueY = !parent.userPickY ? null : parseValue(picked.mouseY.toString(), objType, propertyY) let newValueY = !parent.userPickY ? null : parseValue(picked.mouseY.toString(), objType, propertyY)
let obj = Objects.currentObjectsByName[objName] let obj = Runtime.Objects.currentObjectsByName[objName]
// Set values // Set values
if(parent.userPickX && parent.userPickY) { if(parent.userPickX && parent.userPickY) {
history.addToHistory(new HistoryLib.EditedPosition( history.addToHistory(new HistoryLib.EditedPosition(
@ -324,7 +323,7 @@ Item {
Parses a given \c value as an expression or a number depending on the type of \c propertyName of all \c objType. Parses a given \c value as an expression or a number depending on the type of \c propertyName of all \c objType.
*/ */
function parseValue(value, objType, propertyName) { function parseValue(value, objType, propertyName) {
if(Objects.types[objType].properties()[propertyName] == 'number') if(Runtime.Objects.types[objType].properties()[propertyName] == 'number')
return parseFloat(value) return parseFloat(value)
else else
return new MathLib.Expression(value) return new MathLib.Expression(value)

View file

@ -18,7 +18,6 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import "../js/math/latex.js" as Latex
/*! /*!
\qmltype GreetScreen \qmltype GreetScreen

View file

@ -20,10 +20,9 @@ import QtQuick.Controls
import QtQuick import QtQuick
import Qt.labs.platform as Native import Qt.labs.platform as Native
import eu.ad5001.LogarithmPlotter.Popup 1.0 as P import eu.ad5001.LogarithmPlotter.Popup 1.0 as P
import "../js/mathlib.js" as MathLib import "../js/mathlib.mjs" as MathLib
import "../js/utils.js" as Utils import "../js/utils.mjs" as Utils
import "../js/objects.js" as Objects import "../js/parsing/parsing.mjs" as Parsing
import "../js/parsing/parsing.js" as Parsing
/*! /*!
@ -392,9 +391,9 @@ Item {
property string objectName: isEnteringProperty ? property string objectName: isEnteringProperty ?
(parent.currentToken.dot ? parent.previousToken.value : parent.previousToken2.value) (parent.currentToken.dot ? parent.previousToken.value : parent.previousToken2.value)
: "" : ""
property bool doesObjectExist: isEnteringProperty && (objectName in Objects.currentObjectsByName) property bool doesObjectExist: isEnteringProperty && (objectName in Runtime.Objects.currentObjectsByName)
property var objectProperties: doesObjectExist ? property var objectProperties: doesObjectExist ?
Objects.currentObjectsByName[objectName].constructor.properties() : Runtime.Objects.currentObjectsByName[objectName].constructor.properties() :
{} {}
categoryItems: Object.keys(objectProperties) categoryItems: Object.keys(objectProperties)
autocompleteGenerator: (item) => { autocompleteGenerator: (item) => {
@ -461,9 +460,9 @@ Item {
visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot
itemStartIndex: functionsList.itemStartIndex + functionsList.model.length itemStartIndex: functionsList.itemStartIndex + functionsList.model.length
itemSelected: parent.itemSelected itemSelected: parent.itemSelected
categoryItems: Objects.getObjectsName("ExecutableObject").filter(obj => obj != self) categoryItems: Runtime.Objects.getObjectsName("ExecutableObject").filter(obj => obj != self)
autocompleteGenerator: (item) => {return { autocompleteGenerator: (item) => {return {
'text': item, 'annotation': Objects.currentObjectsByName[item] == null ? '' : Objects.currentObjectsByName[item].constructor.displayType(), 'text': item, 'annotation': Runtime.Objects.currentObjectsByName[item] == null ? '' : Objects.currentObjectsByName[item].constructor.displayType(),
'autocomplete': item+'()', 'cursorFinalOffset': -1 'autocomplete': item+'()', 'cursorFinalOffset': -1
}} }}
baseText: parent.visible ? parent.currentToken.value : "" baseText: parent.visible ? parent.currentToken.value : ""
@ -476,9 +475,9 @@ Item {
visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot
itemStartIndex: executableObjectsList.itemStartIndex + executableObjectsList.model.length itemStartIndex: executableObjectsList.itemStartIndex + executableObjectsList.model.length
itemSelected: parent.itemSelected itemSelected: parent.itemSelected
categoryItems: Object.keys(Objects.currentObjectsByName).filter(obj => obj != self) categoryItems: Object.keys(Runtime.Objects.currentObjectsByName).filter(obj => obj != self)
autocompleteGenerator: (item) => {return { autocompleteGenerator: (item) => {return {
'text': item, 'annotation': `${Objects.currentObjectsByName[item].constructor.displayType()}`, 'text': item, 'annotation': `${Runtime.Objects.currentObjectsByName[item].constructor.displayType()}`,
'autocomplete': item+'.', 'cursorFinalOffset': 0 'autocomplete': item+'.', 'cursorFinalOffset': 0
}} }}
baseText: parent.visible ? parent.currentToken.value : "" baseText: parent.visible ? parent.currentToken.value : ""
@ -538,8 +537,8 @@ Item {
throw new Error(qsTranslate('error', 'Object cannot be dependent on itself.')) throw new Error(qsTranslate('error', 'Object cannot be dependent on itself.'))
// Recursive dependencies // Recursive dependencies
let dependentOnSelfObjects = expr.requiredObjects().filter( let dependentOnSelfObjects = expr.requiredObjects().filter(
(obj) => Objects.currentObjectsByName[obj].getDependenciesList() (obj) => Runtime.Objects.currentObjectsByName[obj].getDependenciesList()
.includes(Objects.currentObjectsByName[control.self]) .includes(Runtime.Objects.currentObjectsByName[control.self])
) )
if(dependentOnSelfObjects.length == 1) if(dependentOnSelfObjects.length == 1)
throw new Error(qsTranslate('error', 'Circular dependency detected. Object %1 depends on %2.').arg(dependentOnSelfObjects[0].toString()).arg(control.self)) throw new Error(qsTranslate('error', 'Circular dependency detected. Object %1 depends on %2.').arg(dependentOnSelfObjects[0].toString()).arg(control.self))

View file

@ -16,11 +16,11 @@
* 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 QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup
import "js/utils.js" as Utils import "js/utils.mjs" as Utils
/*! /*!
\qmltype Settings \qmltype Settings

View file

@ -19,9 +19,8 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
import "js/objects.js" as Objects import "js/mathlib.mjs" as MathLib
import "js/mathlib.js" as MathLib import "js/historylib.mjs" as HistoryLib
import "js/historylib.js" as HistoryLib
/*! /*!
\qmltype ViewPositionChangeOverlay \qmltype ViewPositionChangeOverlay

View file

@ -16,13 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import EditedProperty from "editproperty.mjs"
import Objects from "../objects.mjs"
.import "editproperty.js" as EP export default class ColorChanged extends EditedProperty {
.import "../objects.js" as Objects
class ColorChanged extends EP.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'}

View file

@ -16,16 +16,29 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import { RuntimeAPI } from "../runtime.mjs"
import Latex from "../math/latex.mjs"
.import "../math/latex.js" as Latex
var themeTextColor;
var imageDepth = 2;
var fontSize = 14;
class Action { class HistoryCommonAPI extends RuntimeAPI {
constructor() {
super('History', [
Runtime.Latex
])
// History QML object
this.history = null;
this.themeTextColor = "#ff0000";
this.imageDepth = 2;
this.fontSize = 14;
}
}
/** @type {HistoryCommonAPI} */
Runtime.History = Runtime.History || new HistoryCommonAPI()
export const API = Runtime.History
export class Action {
/** /**
* Type of the action. * Type of the action.
* *
@ -48,15 +61,11 @@ class Action {
/** /**
* Undoes the action. * Undoes the action.
*
* @returns {string}
*/ */
undo() {} undo() {}
/** /**
* Redoes the action. * Redoes the action.
*
* @returns {string}
*/ */
redo() {} redo() {}
@ -64,7 +73,7 @@ class Action {
* 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]
@ -86,7 +95,7 @@ class Action {
* @returns {string} * @returns {string}
*/ */
getIconRichText(type) { getIconRichText(type) {
return `<img source="../icons/objects/${type}.svg" style="color: ${themeTextColor};" width=18 height=18></img>` return `<img source="../icons/objects/${type}.svg" style="color: ${Runtime.History.themeTextColor};" width=18 height=18></img>`
} }
/** /**
@ -98,8 +107,13 @@ class Action {
renderLatexAsHtml(latexString) { renderLatexAsHtml(latexString) {
if(!Latex.enabled) if(!Latex.enabled)
throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.") throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.")
let latexInfo = Latex.Renderer.render(latexString, imageDepth*(fontSize+2), themeTextColor).split(",") let imgDepth = Runtime.History.imageDepth
return `<img src="${latexInfo[0]}" width="${parseInt(latexInfo[1])/imageDepth}" height="${parseInt(latexInfo[2])/imageDepth}" style="vertical-align: middle"></img>` let [src, width, height] = Latex.Renderer.render(
latexString,
imgDepth * (Runtime.History.fontSize + 2),
Runtime.History.themeTextColor
).split(",")
return `<img src="${src}" width="${parseInt(width)/imgDepth}" height="${parseInt(height)/imgDepth}" style="vertical-align: middle"/>`
} }
/** /**

View file

@ -16,12 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import Objects from "../objects.mjs"
import { Action } from "common.mjs"
.import "../objects.js" as Objects export default class CreateNewObject extends Action {
.import "common.js" as C
class CreateNewObject extends C.Action {
// Action used for the creation of an object // Action used for the creation of an object
type(){return 'CreateNewObject'} type(){return 'CreateNewObject'}
@ -37,10 +35,6 @@ class CreateNewObject extends C.Action {
undo() { undo() {
Objects.deleteObject(this.targetName) Objects.deleteObject(this.targetName)
//let targetIndex = Objects.getObjectsName(this.targetType).indexOf(this.targetName)
//delete Objects.currentObjectsByName[this.targetName]
//Objects.currentObjects[this.targetType][targetIndex].delete()
//Objects.currentObjects[this.targetType].splice(targetIndex, 1)
} }
redo() { redo() {

View file

@ -16,14 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import Objects from "../objects.mjs"
import CreateNewObject from "create.mjs"
.import "../objects.js" as Objects
.import "create.js" as Create
class DeleteObject extends Create.CreateNewObject { export default class DeleteObject extends CreateNewObject {
// Action used at the deletion of an object. Basicly the same thing as creating a new object, except Redo & Undo are reversed. /**
* Action used at the deletion of an object. Basically the same thing as creating a new object, except Redo & Undo are reversed.
*/
type(){return 'DeleteObject'} type(){return 'DeleteObject'}
icon(){return 'delete'} icon(){return 'delete'}

View file

@ -16,15 +16,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import Objects from "../objects.mjs"
import Latex from "../math/latex.mjs"
import * as MathLib from "../mathlib.mjs"
import { Action } from "common.mjs"
import { DrawableObject } from "../objs/common.mjs"
.import "../objects.js" as Objects export default class EditedProperty extends Action {
.import "../math/latex.js" as Latex
.import "../mathlib.js" as MathLib
.import "../objs/common.js" as Common
.import "common.js" as C
class EditedProperty extends C.Action {
// Action used everytime an object's property has been changed // Action used everytime an object's property has been changed
type(){return 'EditedProperty'} type(){return 'EditedProperty'}
@ -33,7 +31,16 @@ class EditedProperty extends C.Action {
color(darkVer=false){ color(darkVer=false){
return darkVer ? 'darkslateblue' : 'cyan'; return darkVer ? 'darkslateblue' : 'cyan';
} }
/**
*
* @param {string} targetName - Name of the object to target
* @param {string} targetType - Type of the object to target.
* @param {string} targetProperty - Property being changed
* @param {any} previousValue - Previous value before change
* @param {any} newValue - New value after change
* @param {boolean} valueIsExpressionNeedingImport - True if the value needs to be imported. (e.g expressions)
*/
constructor(targetName = "", targetType = "Point", targetProperty = "visible", previousValue = false, newValue = true, valueIsExpressionNeedingImport = false) { constructor(targetName = "", targetType = "Point", targetProperty = "visible", previousValue = false, newValue = true, valueIsExpressionNeedingImport = false) {
super(targetName, targetType) super(targetName, targetType)
this.targetProperty = targetProperty this.targetProperty = targetProperty
@ -42,10 +49,10 @@ class EditedProperty extends C.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 {
@ -70,7 +77,7 @@ class EditedProperty extends C.Action {
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]
} else if(this.previousValue instanceof Common.DrawableObject) { } else if(this.previousValue instanceof DrawableObject) {
return [this.targetName, this.targetType, this.targetProperty, this.previousValue.name, this.newValue.name, true] return [this.targetName, this.targetType, this.targetProperty, this.previousValue.name, this.newValue.name, true]
} else { } else {
return [this.targetName, this.targetType, this.targetProperty, this.previousValue, this.newValue, false] return [this.targetName, this.targetType, this.targetProperty, this.previousValue, this.newValue, false]
@ -110,7 +117,7 @@ class EditedProperty extends C.Action {
// 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") {
this.prevHTML= this.renderLatexAsHtml(this.previousValue.latexMarkup) this.prevHTML= this.renderLatexAsHtml(this.previousValue.latexMarkup)
this.nextHTML= this.renderLatexAsHtml(this.newValue.latexMarkup) this.nextHTML= this.renderLatexAsHtml(this.newValue.latexMarkup)
} }

View file

@ -16,19 +16,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import EditedProperty from "editproperty.mjs"
import Objects from "../objects.mjs"
.import "editproperty.js" as EP
.import "../objects.js" as Objects
class NameChanged extends EP.EditedProperty { export default class NameChanged extends EditedProperty {
// Action used everytime an object's property has been changed // Action used everytime an object's property has been changed
type(){return 'NameChanged'} type(){return 'NameChanged'}
icon(){return 'name'} icon(){return 'name'}
color(darkVer=false){return darkVer ? 'darkorange' : 'orange'} color(darkVer=false){return darkVer ? 'darkorange' : 'orange'}
constructor(targetName = "", targetType = "Point", newName = "") { constructor(targetName = "", targetType = "Point", newName = "") {
@ -45,10 +42,6 @@ class NameChanged extends EP.EditedProperty {
redo() { redo() {
Objects.renameObject(this.previousValue, this.newValue) Objects.renameObject(this.previousValue, this.newValue)
//let obj = Objects.currentObjectsByName[this.previousValue]
//obj.name = this.newValue
//Objects.currentObjectsByName[this.newValue] = obj
//delete Objects.currentObjectsByName[this.previousValue]
} }
getReadableString() { getReadableString() {

View file

@ -16,16 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import Objects from "../objects.mjs"
import Latex from "../math/latex.mjs"
import * as MathLib from "../mathlib.mjs"
import { escapeHTML } from "../utils.mjs"
import { Action } from "common.mjs"
import { DrawableObject } from "../objs/common.mjs"
.import "../objects.js" as Objects export default class EditedPosition extends Action {
.import "../mathlib.js" as MathLib
.import "../math/latex.js" as Latex
.import "../utils.js" as Utils
.import "../objs/common.js" as Common
.import "common.js" as C
class EditedPosition extends C.Action {
// Action used for objects that have a X and Y expression properties (points, texts...) // Action used for objects that have a X and Y expression properties (points, texts...)
type(){return 'EditedPosition'} type(){return 'EditedPosition'}
@ -68,8 +66,8 @@ class EditedPosition extends C.Action {
this.prevHTML = this.renderLatexAsHtml(`\\left(${this.previousXValue.latexMarkup},${this.previousYValue.latexMarkup}\\right)`) this.prevHTML = this.renderLatexAsHtml(`\\left(${this.previousXValue.latexMarkup},${this.previousYValue.latexMarkup}\\right)`)
this.nextHTML = this.renderLatexAsHtml(`\\left(${this.newXValue.latexMarkup},${this.newYValue.latexMarkup}\\right)`) this.nextHTML = this.renderLatexAsHtml(`\\left(${this.newXValue.latexMarkup},${this.newYValue.latexMarkup}\\right)`)
} else { } else {
this.prevHTML = '<tt style="background: rgba(128,128,128,0.1);">&nbsp;'+Utils.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;'+Utils.escapeHTML(this.nextString)+'&nbsp;</tt>' this.nextHTML = '<tt style="background: rgba(128,128,128,0.1);">&nbsp;'+escapeHTML(this.nextString)+'&nbsp;</tt>'
} }
} }

View file

@ -16,13 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import EditedProperty from "editproperty.mjs"
import Objects from "../objects.mjs"
.import "editproperty.js" as EP
.import "../objects.js" as Objects
class EditedVisibility extends EP.EditedProperty { export default class EditedVisibility extends EditedProperty {
// Action used when an object's shown or hidden. // Action used when an object's shown or hidden.
type(){return 'EditedVisibility'} type(){return 'EditedVisibility'}

View file

@ -18,30 +18,27 @@
// This library helps containing actions to be undone or redone (in other words, editing history) // This library helps containing actions to be undone or redone (in other words, editing history)
// Each type of event is repertoried as an action that can be listed for everything that's undoable. // Each type of event is repertoried as an action that can be listed for everything that's undoable.
.pragma library
.import "history/common.js" as Common import { Action as A } from "history/common.mjs"
.import "history/create.js" as Create import Create from "history/create.mjs"
.import "history/delete.js" as Delete import Delete from "history/delete.mjs"
.import "history/editproperty.js" as EP import EP from "history/editproperty.mjs"
.import "history/position.js" as Pos import Pos from "history/position.mjs"
.import "history/visibility.js" as V import V from "history/visibility.mjs"
.import "history/name.js" as Name import Name from "history/name.mjs"
.import "history/color.js" as Color import Color from "history/color.mjs"
var history = null;
var Action = Common.Action export const Action = A
var CreateNewObject = Create.CreateNewObject export const CreateNewObject = Create
var DeleteObject = Delete.DeleteObject export const DeleteObject = Delete
var EditedProperty = EP.EditedProperty export const EditedProperty = EP
var EditedPosition = Pos.EditedPosition export const EditedPosition = Pos
var EditedVisibility = V.EditedVisibility export const EditedVisibility = V
var NameChanged = Name.NameChanged export const NameChanged = Name
var ColorChanged = Color.ColorChanged export const ColorChanged = Color
var Actions = { export const Actions = {
"Action": Action, "Action": Action,
"CreateNewObject": CreateNewObject, "CreateNewObject": CreateNewObject,
"DeleteObject": DeleteObject, "DeleteObject": DeleteObject,

View file

@ -0,0 +1,121 @@
/**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
.pragma library
.import "expr-eval.js" as ExprEval
.import "../../runtime.mjs" as R
let RuntimeAPI = R.RuntimeAPI
const evalVariables = {
// Variables not provided by expr-eval.js, needs to be provided manually
"pi": Math.PI,
"PI": Math.PI,
"π": Math.PI,
"inf": Infinity,
"infinity": Infinity,
"Infinity": Infinity,
"∞": Infinity,
"e": Math.E,
"E": Math.E,
"true": true,
"false": false
}
class ExprParserAPI extends RuntimeAPI {
constructor() {
super('ExprParser', [
/** @type {ObjectsAPI} */
Runtime.Objects
])
this.currentVars = {}
this.Internals = ExprEval
this._parser = new ExprEval.Parser()
this._parser.consts = Object.assign({}, this._parser.consts, evalVariables)
this._parser.functions.integral = this.integral.bind(this)
this._parser.functions.derivative = this.derivative.bind(this)
}
/**
* Parses arguments for a function, returns the corresponding JS function if it exists.
* Throws either usage error otherwise.
* @param {array} args - Arguments of the function, either [ ExecutableObject ] or [ string, variable ].
* @param {string} usage1 - Usage for executable object.
* @param {string} usage2 - Usage for string function.
* @return {function} JS function to call.
*/
parseArgumentsForFunction(args, usage1, usage2) {
let f, target, variable
if(args.length === 1) {
// Parse object
f = args[0]
if(typeof f !== 'object' || !f.execute)
throw EvalError(qsTranslate('usage', 'Usage: %1').arg(usage1))
let target = f
f = (x) => target.execute(x)
} else if(args.length === 2) {
// Parse variable
[f,variable] = args
if(typeof f !== 'string' || typeof variable !== 'string')
throw EvalError(qsTranslate('usage', 'Usage: %1').arg(usage2))
f = this._parser.parse(f).toJSFunction(variable, this.currentVars)
} else
throw EvalError(qsTranslate('usage', 'Usage: %1 or\n%2').arg(usage1).arg(usage2))
return f
}
/**
* @param {string} expression - Expression to parse
*/
parse(expression) {
return this._parser.parse(expression)
}
integral(a, b, ...args) {
let usage1 = qsTranslate('usage', 'integral(<from: number>, <to: number>, <f: ExecutableObject>)')
let usage2 = qsTranslate('usage', 'integral(<from: number>, <to: number>, <f: string>, <variable: string>)')
let f = this.parseArgumentsForFunction(args, usage1, usage2)
if(a == null || b == null)
throw EvalError(qsTranslate('usage', 'Usage: %1 or\n%2').arg(usage1).arg(usage2))
// https://en.wikipedia.org/wiki/Simpson%27s_rule
// Simpler, faster than tokenizing the expression
return (b-a)/6*(f(a)+4*f((a+b)/2)+f(b))
}
derivative(...args) {
let usage1 = qsTranslate('usage', 'derivative(<f: ExecutableObject>, <x: number>)')
let usage2 = qsTranslate('usage', 'derivative(<f: string>, <variable: string>, <x: number>)')
let x = args.pop()
let f = this.parseArgumentsForFunction(args, usage1, usage2)
if(x == null)
throw EvalError(qsTranslate('usage', 'Usage: %1 or\n%2').arg(usage1).arg(usage2))
let derivative_precision = x/10
return (f(x+derivative_precision/2)-f(x-derivative_precision/2))/derivative_precision
}
}
/** @type {ExprParserAPI} */
Runtime.ExprParser = Runtime.ExprParser || new ExprParserAPI()

View file

@ -1,42 +1,30 @@
/** /**
* 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/>.
*/ */
.pragma library // Type polyfills for IDEs.
// Never directly imported.
.import "common.js" as C Runtime = Runtime || {}
.import "point.js" as P /** @type {function(string, string): string} */
.import "text.js" as T qsTranslate = qsTranslate || function(category, string) { throw new Error('qsTranslate not implemented.'); }
.import "function.js" as F /** @type {function(string): string} */
.import "gainbode.js" as GB qsTr = qsTr || function(string) { throw new Error('qsTr not implemented.'); }
.import "phasebode.js" as PB /** @type {function(string, string): string} */
.import "sommegainsbode.js" as SGB QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); }
.import "sommephasesbode.js" as SPB /** @type {function(string|boolean|int): string} */
.import "xcursor.js" as X String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); }
.import "sequence.js" as S
.import "repartition.js" as R
C.registerObject(P.Point)
C.registerObject(T.Text)
C.registerObject(F.Function)
C.registerObject(GB.GainBode)
C.registerObject(PB.PhaseBode)
C.registerObject(SGB.SommeGainsBode)
C.registerObject(SPB.SommePhasesBode)
C.registerObject(X.XCursor)
C.registerObject(S.Sequence)
C.registerObject(R.RepartitionFunction)

View file

@ -1,99 +0,0 @@
/**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
.pragma library
.import "../expr-eval.js" as ExprEval
.import "../utils.js" as Utils
.import "latex.js" as Latex
var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manually
"pi": Math.PI,
"PI": Math.PI,
"π": Math.PI,
"inf": Infinity,
"infinity": Infinity,
"Infinity": Infinity,
"∞": Infinity,
"e": Math.E,
"E": Math.E,
"true": true,
"false": false
}
var currentVars = {}
var currentObjectsByName = {} // Mirror of currentObjectsByName in objects.js
const parser = new ExprEval.Parser()
parser.consts = Object.assign({}, parser.consts, evalVariables)
/**
* Parses arguments for a function, returns the corresponding JS function if it exists.
* Throws either usage error otherwise.
* @param {array} args - Arguments of the function, either [ ExecutableObject ] or [ string, variable ].
* @param {string} usage1 - Usage for executable object.
* @param {string} usage2 - Usage for string function.
* @return {callable} JS function to call..
*/
function parseArgumentsForFunction(args, usage1, usage2) {
let f, target, variable
if(args.length == 1) {
// Parse object
f = args[0]
if(typeof f != 'object' || !f.execute)
throw EvalError(qsTranslate('usage', 'Usage: %1').arg(usage1))
let target = f
f = (x) => target.execute(x)
} else if(args.length == 2) {
// Parse variable
[f,variable] = args
if(typeof f != 'string' || typeof variable != 'string')
throw EvalError(qsTranslate('usage', 'Usage: %1').arg(usage2))
f = parser.parse(f).toJSFunction(variable, currentVars)
} else
throw EvalError(qsTranslate('usage', 'Usage: %1 or\n%2').arg(usage1).arg(usage2))
return f
}
// Function definition
parser.functions.integral = function(a, b, ...args) {
let usage1 = qsTranslate('usage', 'integral(<from: number>, <to: number>, <f: ExecutableObject>)')
let usage2 = qsTranslate('usage', 'integral(<from: number>, <to: number>, <f: string>, <variable: string>)')
let f = parseArgumentsForFunction(args, usage1, usage2)
if(a == null || b == null)
throw EvalError(qsTranslate('usage', 'Usage: %1 or\n%2').arg(usage1).arg(usage2))
// https://en.wikipedia.org/wiki/Simpson%27s_rule
// Simpler, faster than tokenizing the expression
return (b-a)/6*(f(a)+4*f((a+b)/2)+f(b))
}
parser.functions.derivative = function(...args) {
let usage1 = qsTranslate('usage', 'derivative(<f: ExecutableObject>, <x: number>)')
let usage2 = qsTranslate('usage', 'derivative(<f: string>, <variable: string>, <x: number>)')
let x = args.pop()
let f = parseArgumentsForFunction(args, usage1, usage2)
if(x == null)
throw EvalError(qsTranslate('usage', 'Usage: %1 or\n%2').arg(usage1).arg(usage2))
let derivative_precision = x/10
return (f(x+derivative_precision/2)-f(x-derivative_precision/2))/derivative_precision
}

View file

@ -16,21 +16,19 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import { Expression, executeExpression } from "expression.mjs"
.import "expression.js" as Expr
/** /**
* Main abstract domain class * Main abstract domain class
* 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.
*/ */
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 {bool} true if included, false otherwise. * @return {boolean} true if included, false otherwise.
*/ */
includes(x) { return false } includes(x) { return false }
@ -166,7 +164,7 @@ class Domain {
/** /**
* Represents an empty set. * Represents an empty set.
*/ */
class EmptySet extends Domain { export class EmptySet extends Domain {
constructor() { constructor() {
super() super()
this.displayName = "∅" this.displayName = "∅"
@ -187,12 +185,12 @@ class EmptySet extends Domain {
/** /**
* Domain classes for ranges (e.g ]0;3[, [1;2[ ...) * Domain classes for ranges (e.g ]0;3[, [1;2[ ...)
*/ */
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 Expr.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 Expr.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
@ -201,7 +199,7 @@ class Range extends Domain {
} }
includes(x) { includes(x) {
if(typeof x == 'string') x = Expr.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()))
} }
@ -229,9 +227,9 @@ class Range extends Domain {
} }
static import(frm) { static import(frm) {
var openBegin = frm.trim().charAt(0) == "]" let openBegin = frm.trim().charAt(0) === "]"
var openEnd = frm.trim().charAt(frm.length -1) == "[" let openEnd = frm.trim().charAt(frm.length -1) === "["
var [begin, end] = frm.substr(1, frm.length-2).split(";") let [begin, end] = frm.substr(1, frm.length-2).split(";")
return new Range(begin.trim(), end.trim(), openBegin, openEnd) return new Range(begin.trim(), end.trim(), openBegin, openEnd)
} }
} }
@ -239,17 +237,16 @@ class Range extends Domain {
/** /**
* Domain classes for special domains (N, Z, ...) * Domain classes for special domains (N, Z, ...)
*/ */
class SpecialDomain extends Domain { export class SpecialDomain extends Domain {
/** /**
* @constructs SpecialDomain * @constructs SpecialDomain
* @param {string} displayName * @param {string} displayName
* @param {function} isValid - function returning true when number is in domain false when it isn't. * @param {function} isValid - function returning true when number is in domain false when it isn't.
* @param {function} next - function provides the next positive value in the domain after the one given. * @param {function} next - function provides the next positive value in the domain after the one given.
* @param {function} previous - function provides the previous positive value in the domain before the one given. * @param {function} previous - function provides the previous positive value in the domain before the one given.
* @param {bool} moveSupported - Only true if next and previous functions are valid. * @param {boolean} moveSupported - Only true if next and previous functions are valid.
* @param items
*/ */
constructor(displayName, isValid, next = x => true, previous = x => true, constructor(displayName, isValid, next = () => true, previous = () => true,
moveSupported = true) { moveSupported = true) {
super() super()
this.displayName = displayName this.displayName = displayName
@ -260,17 +257,17 @@ class SpecialDomain extends Domain {
} }
includes(x) { includes(x) {
if(typeof x == 'string') x = Expr.executeExpression(x) if(typeof x == 'string') x = executeExpression(x)
return this.isValid(x) return this.isValid(x)
} }
next(x) { next(x) {
if(typeof x == 'string') x = Expr.executeExpression(x) if(typeof x == 'string') x = executeExpression(x)
return this.nextValue(x) return this.nextValue(x)
} }
previous(x) { previous(x) {
if(typeof x == 'string') x = Expr.executeExpression(x) if(typeof x == 'string') x = executeExpression(x)
return this.prevValue(x) return this.prevValue(x)
} }
@ -300,14 +297,14 @@ class SpecialDomain extends Domain {
/** /**
* Domain classes for sets (e.g {0;3}, {0;1;2;pi} ...) * Domain classes for sets (e.g {0;3}, {0;1;2;pi} ...)
*/ */
class DomainSet extends SpecialDomain { export class DomainSet extends SpecialDomain {
constructor(values) { constructor(values) {
super('', x => true, x => x, true) super('', x => true, x => x, true)
var newVals = {} let newVals = {}
this.executedValues = [] this.executedValues = []
for(var value of values) { for(let value of values) {
var expr = new Expr.Expression(value.toString()) let expr = new Expression(value.toString())
var ex = expr.execute() let ex = expr.execute()
newVals[ex] = expr newVals[ex] = expr
this.executedValues.push(ex) this.executedValues.push(ex)
} }
@ -318,30 +315,30 @@ class DomainSet extends SpecialDomain {
} }
includes(x) { includes(x) {
if(typeof x == 'string') x = Expr.executeExpression(x) if(typeof x == 'string') x = executeExpression(x)
for(var 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(typeof x == 'string') x = Expr.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(var i = 1; i < this.values.length; i++) { for(let i = 1; i < this.values.length; i++) {
var prevValue = this.executedValues[i-1] let prevValue = this.executedValues[i-1]
var 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(typeof x == 'string') x = Expr.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(var i = 1; i < this.values.length; i++) { for(let i = 1; i < this.values.length; i++) {
var prevValue = this.executedValues[i-1] let prevValue = this.executedValues[i-1]
var 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
@ -354,56 +351,56 @@ class DomainSet extends SpecialDomain {
union(domain) { union(domain) {
if(domain instanceof EmptySet) return this if(domain instanceof EmptySet) return this
if(domain instanceof DomainSet) { if(domain instanceof DomainSet) {
var newValues = [] let newValues = []
var 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
}) })
return new DomainSet(values) return new DomainSet(values)
} }
var notIncludedValues = [] let notIncludedValues = []
for(var value in this.values) { for(let i = 0; i < this.values.length; i++) {
var value = this.executedValues[i] let value = this.executedValues[i]
if(domain instanceof Range) { if(domain instanceof Range) {
if(domain.begin.execute() == value && domain.openBegin) { if(domain.begin.execute() === value && domain.openBegin) {
domain.openBegin = false domain.openBegin = false
} }
if(domain.end.execute() == value && domain.openEnd) { if(domain.end.execute() === value && domain.openEnd) {
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) {
var 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
} }
var includedValues = [] let includedValues = []
for(var i in this.values) { for(let i in this.values) {
var value = this.executedValues[i] let value = this.executedValues[i]
if(domain instanceof Range) { if(domain instanceof Range) {
if(domain.begin.execute() == value && !domain.openBegin) { if(domain.begin.execute() === value && !domain.openBegin) {
domain.openBegin = false domain.openBegin = false
} }
if(domain.end.execute() == value && !domain.openEnd) { if(domain.end.execute() === value && !domain.openEnd) {
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))
} }
@ -415,7 +412,7 @@ class DomainSet extends SpecialDomain {
/** /**
* Domain representing the union between two domains. * Domain representing the union between two domains.
*/ */
class UnionDomain extends Domain { export class UnionDomain extends Domain {
constructor(dom1, dom2) { constructor(dom1, dom2) {
super() super()
this.dom1 = dom1 this.dom1 = dom1
@ -450,10 +447,10 @@ class UnionDomain extends Domain {
} }
static import(frm) { static import(frm) {
var 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
var dom2 = parseDomain(domains.pop()) let dom2 = parseDomain(domains.pop())
var dom1 = parseDomain(domains.join('')) let dom1 = parseDomain(domains.join(''))
return dom1.union(dom2) return dom1.union(dom2)
} }
} }
@ -461,7 +458,7 @@ class UnionDomain extends Domain {
/** /**
* Domain representing the intersection between two domains. * Domain representing the intersection between two domains.
*/ */
class IntersectionDomain extends Domain { export class IntersectionDomain extends Domain {
constructor(dom1, dom2) { constructor(dom1, dom2) {
super() super()
this.dom1 = dom1 this.dom1 = dom1
@ -496,9 +493,9 @@ class IntersectionDomain extends Domain {
} }
static import(frm) { static import(frm) {
var domains = frm.trim().split("∩") let domains = frm.trim().split("∩")
var dom1 = parseDomain(domains.pop()) let dom1 = parseDomain(domains.pop())
var dom2 = parseDomain(domains.join('∩')) let dom2 = parseDomain(domains.join('∩'))
return dom1.intersection(dom2) return dom1.intersection(dom2)
} }
} }
@ -506,7 +503,7 @@ class IntersectionDomain extends Domain {
/** /**
* Domain representing the minus between two domains. * Domain representing the minus between two domains.
*/ */
class MinusDomain extends Domain { export class MinusDomain extends Domain {
constructor(dom1, dom2) { constructor(dom1, dom2) {
super() super()
this.dom1 = dom1 this.dom1 = dom1
@ -524,10 +521,10 @@ class MinusDomain extends Domain {
} }
static import(frm) { static import(frm) {
var 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
var dom1 = parseDomain(domains.shift()) let dom1 = parseDomain(domains.shift())
var dom2 = parseDomain(domains.join('')) let dom2 = parseDomain(domains.join(''))
return new MinusDomain(dom1, dom2) return new MinusDomain(dom1, dom2)
} }
} }
@ -551,53 +548,53 @@ 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) {
var 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) {
var 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}"
var refedDomains = [] let refedDomains = []
/** /**
* Parses a domain, that can use parenthesises. * Parses a domain, that can use parentheses.
* e.g (N [-1;0[) (Z \ {0;3}) * e.g (N [-1;0[) (Z \ {0;3})
* @param {string} domain - string of the domain to be parsed. * @param {string} domain - string of the domain to be parsed.
* @returns {Domain} Parsed domain. * @returns {Domain} Parsed domain.
*/ */
function parseDomain(domain) { export function parseDomain(domain) {
if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain) if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain)
var domStr let domStr
while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) { while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) {
var 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)
} }
@ -605,20 +602,20 @@ function parseDomain(domain) {
} }
/** /**
* Parses a domain, without parenthesises. * Parses a domain, without parentheses.
* e.g N [-1;0[, Z \ {0;3}, N+*... * e.g N [-1;0[, Z \ {0;3}, N+*...
* @param {string} domain - string of the domain to be parsed. * @param {string} domain - string of the domain to be parsed.
* @returns {Domain} Parsed domain. * @returns {Domain} Parsed domain.
*/ */
function parseDomainSimple(domain) { export function parseDomainSimple(domain) {
domain = domain.trim() domain = domain.trim()
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.substr(1))]
return new EmptySet() return new EmptySet()
} }

View file

@ -16,21 +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/>.
*/ */
.pragma library
.import "common.js" as C import Latex from "latex.mjs"
.import "latex.js" as Latex import * as Utils from "../utils.mjs"
.import "../utils.js" as Utils
/** /**
* Represents any kind of x-based or non variable based expression. * Represents any kind of x-based or non variable based expression.
*/ */
class Expression { export class Expression {
constructor(expr) { constructor(expr) {
if(!Runtime.ExprParser)
throw new Error('Expression parser not initialized.')
if(!Runtime.Objects)
throw new Error('Objects API not initialized.')
this.expr = Utils.exponentsToExpression(expr) this.expr = Utils.exponentsToExpression(expr)
this.calc = C.parser.parse(this.expr).simplify() this.calc = Runtime.ExprParser.parse(this.expr).simplify()
this.cached = this.isConstant() this.cached = this.isConstant()
this.cachedValue = this.cached && this.allRequirementsFullfilled() ? this.calc.evaluate(C.currentObjectsByName) : null this.cachedValue = null
if(this.cached && this.allRequirementsFullfilled())
this.cachedValue = this.calc.evaluate(Runtime.Objects.currentObjectsByName)
this.latexMarkup = Latex.expression(this.calc.tokens) this.latexMarkup = Latex.expression(this.calc.tokens)
} }
@ -40,37 +44,37 @@ class Expression {
} }
requiredObjects() { requiredObjects() {
return this.calc.variables().filter(objName => objName != "x" && objName != "n") return this.calc.variables().filter(objName => objName !== "x" && objName !== "n")
} }
allRequirementsFullfilled() { allRequirementsFullfilled() {
return this.requiredObjects().every(objName => objName in C.currentObjectsByName) return this.requiredObjects().every(objName => objName in Runtime.Objects.currentObjectsByName)
} }
undefinedVariables() { undefinedVariables() {
return this.requiredObjects().filter(objName => !(objName in C.currentObjectsByName)) return this.requiredObjects().filter(objName => !(objName in Runtime.Objects.currentObjectsByName))
} }
recache() { recache() {
if(this.cached) if(this.cached)
this.cachedValue = this.calc.evaluate(C.currentObjectsByName) this.cachedValue = this.calc.evaluate(Runtime.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(C.currentObjectsByName) this.cachedValue = this.calc.evaluate(Runtime.Objects.currentObjectsByName)
return this.cachedValue return this.cachedValue
} }
C.currentVars = Object.assign({'x': x}, C.currentObjectsByName) Runtime.ExprParser.currentVars = Object.assign({'x': x}, Runtime.Objects.currentObjectsByName)
return this.calc.evaluate(C.currentVars) return this.calc.evaluate(Runtime.ExprParser.currentVars)
} }
simplify(x) { simplify(x) {
var expr = this.calc.substitute('x', x).simplify() let expr = this.calc.substitute('x', x).simplify()
if(expr.evaluate() == 0) return '0' if(expr.evaluate() === 0) return '0'
var str = Utils.makeExpressionReadable(expr.toString()); let str = Utils.makeExpressionReadable(expr.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();
@ -89,11 +93,11 @@ class Expression {
toString(forceSign=false) { toString(forceSign=false) {
let str = Utils.makeExpressionReadable(this.calc.toString()) let str = Utils.makeExpressionReadable(this.calc.toString())
if(str[0] != '-' && forceSign) str = '+' + str if(str[0] !== '-' && forceSign) str = '+' + str
return str return str
} }
} }
function executeExpression(expr){ export function executeExpression(expr){
return (new Expression(expr.toString())).execute() return (new Expression(expr.toString())).execute()
} }

View file

@ -1,282 +0,0 @@
/**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
.pragma library
.import "../expr-eval.js" as ExprEval
/**
* true if latex has been enabled by the user, false otherwise.
*/
var enabled = false
/**
* LaTeX python backend QObject.
*/
var Renderer = null
/**
* Default window color used to render LaTeX formulas.
*/
var defaultColor = "black"
/**
* Puts element within parenthesis.
*
* @param {string} elem - element to put within parenthesis.
* @returns {string}
*/
function par(elem) {
return '(' + elem + ')'
}
/**
* Checks if the element contains at least one of the elements of
* the string array contents, but not at the first position of the string,
* and returns the parenthesis version if so.
*
* @param {string} elem - element to put within parenthesis.
* @param {Array} contents - Array of elements to put within parenthesis.
* @returns {string}
*/
function parif(elem, contents) {
elem = elem.toString()
if(elem[0] != "(" && elem[elem.length-1] != ")" && contents.some(x => elem.indexOf(x) > 0))
return par(elem)
if(elem[0] == "(" && elem[elem.length-1] == ")")
return elem.substr(1, elem.length-2)
return elem
}
/**
* Creates a latex expression for a function.
*
* @param {string} f - Function to convert
* @param {Array} args - Arguments of the function
* @returns {string}
*/
function functionToLatex(f, args) {
switch(f) {
case "derivative":
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}';
else
return '\\frac{d' + args[0] + '}{dx}(x)';
break;
case "integral":
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);
else
return '\\int\\limits_{' + args[0] + '}^{' + args[1] + '}' + args[2] + '(t) dt';
break;
case "sqrt":
return '\\sqrt\\left(' + args.join(', ') + '\\right)';
break;
case "abs":
return '\\left|' + args.join(', ') + '\\right|';
break;
case "floor":
return '\\left\\lfloor' + args.join(', ') + '\\right\\rfloor';
break;
case "ceil":
return '\\left\\lceil' + args.join(', ') + '\\right\\rceil';
break;
default:
return '\\mathrm{' + f + '}\\left(' + args.join(', ') + '\\right)';
break;
}
}
/**
* Creates a latex variable from a variable.
*
* @param {string} vari - variable text to convert
* @param {bool} wrapIn$ - checks whether the escaped chars should be escaped
* @returns {string}
*/
function variable(vari, wrapIn$ = false) {
let unicodechars = ["α","β","γ","δ","ε","ζ","η",
"π","θ","κ","λ","μ","ξ","ρ",
"ς","σ","τ","φ","χ","ψ","ω",
"Γ","Δ","Θ","Λ","Ξ","Π","Σ",
"Φ","Ψ","Ω","ₐ","ₑ","ₒ","ₓ",
"ₕ","ₖ","ₗ","ₘ","ₙ","ₚ","ₛ",
"ₜ","¹","²","³","⁴","⁵","⁶",
"⁷","⁸","⁹","⁰","₁","₂","₃",
"₄","₅","₆","₇","₈","₉","₀",
"pi", "∞"]
let equivalchars = ["\\alpha","\\beta","\\gamma","\\delta","\\epsilon","\\zeta","\\eta",
"\\pi","\\theta","\\kappa","\\lambda","\\mu","\\xi","\\rho",
"\\sigma","\\sigma","\\tau","\\phi","\\chi","\\psi","\\omega",
"\\Gamma","\\Delta","\\Theta","\\Lambda","\\Xi","\\Pi","\\Sigma",
"\\Phy","\\Psi","\\Omega","{}_{a}","{}_{e}","{}_{o}","{}_{x}",
"{}_{h}","{}_{k}","{}_{l}","{}_{m}","{}_{n}","{}_{p}","{}_{s}",
"{}_{t}","{}^{1}","{}^{2}","{}^{3}","{}^{4}","{}^{5}","{}^{6}",
"{}^{7}","{}^{8}","{}^{9}","{}^{0}","{}_{1}","{}_{2}","{}_{3}",
"{}_{4}","{}_{5}","{}_{6}","{}_{7}","{}_{8}","{}_{9}","{}_{0}",
"\\pi", "\\infty"]
if(wrapIn$)
for(let i = 0; i < unicodechars.length; i++) {
if(vari.includes(unicodechars[i]))
vari = vari.replace(new RegExp(unicodechars[i], 'g'), '$'+equivalchars[i]+'$')
}
else
for(let i = 0; i < unicodechars.length; i++) {
if(vari.includes(unicodechars[i]))
vari = vari.replace(new RegExp(unicodechars[i], 'g'), equivalchars[i])
}
return vari;
}
/**
* Converts expr-eval tokens to a latex string.
*
* @param {Array} tokens - expr-eval tokens list
* @returns {string}
*/
function expression(tokens) {
var nstack = [];
var n1, n2, n3;
var f, args, argCount;
for (var i = 0; i < tokens.length; i++) {
var item = tokens[i];
var type = item.type;
switch(type) {
case ExprEval.INUMBER:
if(item.value == Infinity) {
nstack.push("\\infty")
} else if(typeof item.value === 'number' && item.value < 0) {
nstack.push(par(item.value));
} else if(Array.isArray(item.value)) {
nstack.push('[' + item.value.map(ExprEval.escapeValue).join(', ') + ']');
} else {
nstack.push(ExprEval.escapeValue(item.value));
}
break;
case ExprEval.IOP2:
n2 = nstack.pop();
n1 = nstack.pop();
f = item.value;
switch(f) {
case '-':
case '+':
nstack.push(n1 + f + n2);
break;
case '||':
case 'or':
case '&&':
case 'and':
case '==':
case '!=':
nstack.push(par(n1) + f + par(n2));
break;
case '*':
if(n2 == "\\pi" || n2 == "e" || n2 == "x" || n2 == "n")
nstack.push(parif(n1,['+','-']) + n2)
else
nstack.push(parif(n1,['+','-']) + " \\times " + parif(n2,['+','-']));
break;
case '/':
nstack.push("\\frac{" + n1 + "}{" + n2 + "}");
break;
case '^':
nstack.push(parif(n1,['+','-','*','/','!']) + "^{" + n2 + "}");
break;
case '%':
nstack.push(parif(n1,['+','-','*','/','!','^']) + " \\mathrm{mod} " + parif(n2,['+','-','*','/','!','^']));
break;
case '[':
nstack.push(n1 + '[' + n2 + ']');
break;
default:
throw new EvalError("Unknown operator " + ope + ".");
}
break;
case ExprEval.IOP3: // Thirdiary operator
n3 = nstack.pop();
n2 = nstack.pop();
n1 = nstack.pop();
f = item.value;
if (f === '?') {
nstack.push('(' + n1 + ' ? ' + n2 + ' : ' + n3 + ')');
} else {
throw new EvalError('Unknown operator ' + ope + '.');
}
break;
case ExprEval.IVAR:
case ExprEval.IVARNAME:
nstack.push(variable(item.value.toString()));
break;
case ExprEval.IOP1: // Unary operator
n1 = nstack.pop();
f = item.value;
switch(f) {
case '-':
case '+':
nstack.push(par(f + n1));
break;
case '!':
nstack.push(parif(n1,['+','-','*','/','^']) + '!');
break;
default:
nstack.push(f + parif(n1,['+','-','*','/','^']));
break;
}
break;
case ExprEval.IFUNCALL:
argCount = item.value;
args = [];
while (argCount-- > 0) {
args.unshift(nstack.pop());
}
f = nstack.pop();
// Handling various functions
nstack.push(functionToLatex(f, args))
break;
case ExprEval.IFUNDEF:
nstack.push(par(n1 + '(' + args.join(', ') + ') = ' + n2));
break;
case ExprEval.IMEMBER:
n1 = nstack.pop();
nstack.push(n1 + '.' + item.value);
break;
case ExprEval.IARRAY:
argCount = item.value;
args = [];
while (argCount-- > 0) {
args.unshift(nstack.pop());
}
nstack.push('[' + args.join(', ') + ']');
break;
case ExprEval.IEXPR:
nstack.push('(' + expression(item.value) + ')');
break;
case ExprEval.IENDSTATEMENT:
break;
default:
throw new EvalError('invalid Expression');
break;
}
}
if (nstack.length > 1) {
nstack = [ nstack.join(';') ]
}
return String(nstack[0]);
}

View file

@ -0,0 +1,288 @@
/**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { RuntimeAPI } from '../runtime.mjs'
const unicodechars = ["α","β","γ","δ","ε","ζ","η",
"π","θ","κ","λ","μ","ξ","ρ",
"ς","σ","τ","φ","χ","ψ","ω",
"Γ","Δ","Θ","Λ","Ξ","Π","Σ",
"Φ","Ψ","Ω","ₐ","ₑ","ₒ","ₓ",
"ₕ","ₖ","ₗ","ₘ","ₙ","ₚ","ₛ",
"ₜ","¹","²","³","⁴","⁵","⁶",
"⁷","⁸","⁹","⁰","₁","₂","₃",
"₄","₅","₆","₇","₈","₉","₀",
"pi", "∞"]
const equivalchars = ["\\alpha","\\beta","\\gamma","\\delta","\\epsilon","\\zeta","\\eta",
"\\pi","\\theta","\\kappa","\\lambda","\\mu","\\xi","\\rho",
"\\sigma","\\sigma","\\tau","\\phi","\\chi","\\psi","\\omega",
"\\Gamma","\\Delta","\\Theta","\\Lambda","\\Xi","\\Pi","\\Sigma",
"\\Phy","\\Psi","\\Omega","{}_{a}","{}_{e}","{}_{o}","{}_{x}",
"{}_{h}","{}_{k}","{}_{l}","{}_{m}","{}_{n}","{}_{p}","{}_{s}",
"{}_{t}","{}^{1}","{}^{2}","{}^{3}","{}^{4}","{}^{5}","{}^{6}",
"{}^{7}","{}^{8}","{}^{9}","{}^{0}","{}_{1}","{}_{2}","{}_{3}",
"{}_{4}","{}_{5}","{}_{6}","{}_{7}","{}_{8}","{}_{9}","{}_{0}",
"\\pi", "\\infty"]
console.log(Runtime.ExprParser)
class LatexAPI extends RuntimeAPI {
constructor() {
super('Latex', [
/** @type {ExprParserAPI} */
Runtime.ExprParser
])
/**
* true if latex has been enabled by the user, false otherwise.
*/
this.enabled = false
/**
* LaTeX python backend QObject.
*/
this.Renderer = null
}
/**
* Puts element within parenthesis.
*
* @param {string} elem - element to put within parenthesis.
* @returns {string}
*/
par(elem) {
return '(' + elem + ')'
}
/**
* Checks if the element contains at least one of the elements of
* the string array contents, but not at the first position of the string,
* and returns the parenthesis version if so.
*
* @param {string} elem - element to put within parenthesis.
* @param {Array} contents - Array of elements to put within parenthesis.
* @returns {string}
*/
parif(elem, contents) {
elem = elem.toString()
if(elem[0] !== "(" && elem[elem.length-1] !== ")" && contents.some(x => elem.indexOf(x) > 0))
return this.par(elem)
if(elem[0] === "(" && elem[elem.length-1] === ")")
return elem.substr(1, elem.length-2)
return elem
}
/**
* Creates a latex expression for a function.
*
* @param {string} f - Function to convert
* @param {(number,string)[]} args - Arguments of the function
* @returns {string}
*/
functionToLatex(f, args) {
switch(f) {
case "derivative":
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}';
else
return '\\frac{d' + args[0] + '}{dx}(x)';
break;
case "integral":
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);
else
return '\\int\\limits_{' + args[0] + '}^{' + args[1] + '}' + args[2] + '(t) dt';
break;
case "sqrt":
return '\\sqrt\\left(' + args.join(', ') + '\\right)';
break;
case "abs":
return '\\left|' + args.join(', ') + '\\right|';
break;
case "floor":
return '\\left\\lfloor' + args.join(', ') + '\\right\\rfloor';
break;
case "ceil":
return '\\left\\lceil' + args.join(', ') + '\\right\\rceil';
break;
default:
return '\\mathrm{' + f + '}\\left(' + args.join(', ') + '\\right)';
break;
}
}
/**
* Creates a latex variable from a variable.
*
* @param {string} vari - variable text to convert
* @param {boolean} wrapIn$ - checks whether the escaped chars should be escaped
* @returns {string}
*/
variable(vari, wrapIn$ = false) {
if(wrapIn$)
for(let i = 0; i < unicodechars.length; i++) {
if(vari.includes(unicodechars[i]))
vari = vari.replace(new RegExp(unicodechars[i], 'g'), '$'+equivalchars[i]+'$')
}
else
for(let i = 0; i < unicodechars.length; i++) {
if(vari.includes(unicodechars[i]))
vari = vari.replace(new RegExp(unicodechars[i], 'g'), equivalchars[i])
}
return vari;
}
/**
* Converts expr-eval tokens to a latex string.
*
* @param {Array} tokens - expr-eval tokens list
* @returns {string}
*/
expression(tokens) {
let nstack = []
let n1, n2, n3
let f, args, argCount
for (let i = 0; i < tokens.length; i++) {
let item = tokens[i]
let type = item.type
switch(type) {
case Runtime.ExprParser.Internals.INUMBER:
if(item.value === Infinity) {
nstack.push("\\infty")
} else if(typeof item.value === 'number' && item.value < 0) {
nstack.push(this.par(item.value));
} else if(Array.isArray(item.value)) {
nstack.push('[' + item.value.map(Runtime.ExprParser.Internals.escapeValue).join(', ') + ']');
} else {
nstack.push(Runtime.ExprParser.Internals.escapeValue(item.value));
}
break;
case Runtime.ExprParser.Internals.IOP2:
n2 = nstack.pop();
n1 = nstack.pop();
f = item.value;
switch(f) {
case '-':
case '+':
nstack.push(n1 + f + n2);
break;
case '||':
case 'or':
case '&&':
case 'and':
case '==':
case '!=':
nstack.push(this.par(n1) + f + this.par(n2));
break;
case '*':
if(n2 == "\\pi" || n2 == "e" || n2 == "x" || n2 == "n")
nstack.push(this.parif(n1,['+','-']) + n2)
else
nstack.push(this.parif(n1,['+','-']) + " \\times " + this.parif(n2,['+','-']));
break;
case '/':
nstack.push("\\frac{" + n1 + "}{" + n2 + "}");
break;
case '^':
nstack.push(this.parif(n1,['+','-','*','/','!']) + "^{" + n2 + "}");
break;
case '%':
nstack.push(this.parif(n1,['+','-','*','/','!','^']) + " \\mathrm{mod} " + parif(n2,['+','-','*','/','!','^']));
break;
case '[':
nstack.push(n1 + '[' + n2 + ']');
break;
default:
throw new EvalError("Unknown operator " + ope + ".");
}
break;
case Runtime.ExprParser.Internals.IOP3: // Thirdiary operator
n3 = nstack.pop();
n2 = nstack.pop();
n1 = nstack.pop();
f = item.value;
if (f === '?') {
nstack.push('(' + n1 + ' ? ' + n2 + ' : ' + n3 + ')');
} else {
throw new EvalError('Unknown operator ' + ope + '.');
}
break;
case Runtime.ExprParser.Internals.IVAR:
case Runtime.ExprParser.Internals.IVARNAME:
nstack.push(this.variable(item.value.toString()));
break;
case Runtime.ExprParser.Internals.IOP1: // Unary operator
n1 = nstack.pop();
f = item.value;
switch(f) {
case '-':
case '+':
nstack.push(this.par(f + n1));
break;
case '!':
nstack.push(this.parif(n1,['+','-','*','/','^']) + '!');
break;
default:
nstack.push(f + this.parif(n1,['+','-','*','/','^']));
break;
}
break;
case Runtime.ExprParser.Internals.IFUNCALL:
argCount = item.value;
args = [];
while (argCount-- > 0) {
args.unshift(nstack.pop());
}
f = nstack.pop();
// Handling various functions
nstack.push(this.functionToLatex(f, args))
break;
case Runtime.ExprParser.Internals.IFUNDEF:
nstack.push(this.par(n1 + '(' + args.join(', ') + ') = ' + n2));
break;
case Runtime.ExprParser.Internals.IMEMBER:
n1 = nstack.pop();
nstack.push(n1 + '.' + item.value);
break;
case Runtime.ExprParser.Internals.IARRAY:
argCount = item.value;
args = [];
while (argCount-- > 0) {
args.unshift(nstack.pop());
}
nstack.push('[' + args.join(', ') + ']');
break;
case Runtime.ExprParser.Internals.IEXPR:
nstack.push('(' + this.expression(item.value) + ')');
break;
case Runtime.ExprParser.Internals.IENDSTATEMENT:
break;
default:
throw new EvalError('invalid Expression');
}
}
if (nstack.length > 1) {
nstack = [ nstack.join(';') ]
}
return String(nstack[0]);
}
}
/** @type {LatexAPI} */
Runtime.Latex = Runtime.Latex || new LatexAPI()
export default Runtime.Latex

View file

@ -16,18 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import * as Expr from "expression.mjs"
import * as Utils from "../utils.mjs"
.import "common.js" as C import Latex from "../math/latex.mjs"
.import "expression.js" as Expr
.import "../utils.js" as Utils
.import "../math/latex.js" as Latex
/** /**
* Represents mathematical object for sequences. * Represents mathematical object for sequences.
*/ */
class Sequence extends Expr.Expression { export class Sequence extends Expr.Expression {
constructor(name, baseValues = {}, valuePlus = 1, expr = "") { constructor(name, baseValues = {}, valuePlus = 1, expr = "") {
// u[n+valuePlus] = expr // u[n+valuePlus] = expr
super(expr) super(expr)
@ -35,9 +31,9 @@ class Sequence extends Expr.Expression {
this.baseValues = baseValues this.baseValues = baseValues
this.calcValues = Object.assign({}, baseValues) this.calcValues = Object.assign({}, baseValues)
this.latexValues = Object.assign({}, baseValues) this.latexValues = Object.assign({}, baseValues)
for(var n in this.calcValues) for(let n in this.calcValues)
if(['string', 'number'].includes(typeof this.calcValues[n])) { if(['string', 'number'].includes(typeof this.calcValues[n])) {
let parsed = C.parser.parse(this.calcValues[n].toString()).simplify() let parsed = Runtime.ExprParser.parse(this.calcValues[n].toString()).simplify()
this.latexValues[n] = Latex.expression(parsed.tokens) this.latexValues[n] = Latex.expression(parsed.tokens)
this.calcValues[n] = parsed.evaluate() this.calcValues[n] = parsed.evaluate()
} }
@ -45,7 +41,7 @@ class Sequence extends Expr.Expression {
} }
isConstant() { isConstant() {
return this.expr.indexOf("n") == -1 return this.expr.indexOf("n") === -1
} }
execute(n = 1) { execute(n = 1) {
@ -63,25 +59,25 @@ class Sequence extends Expr.Expression {
} }
cache(n = 1) { cache(n = 1) {
var str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString()) let str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString())
var expr = C.parser.parse(str).simplify() let expr = Runtime.ExprParser.parse(str).simplify()
// Chache values required for this one. // Cache values required for this one.
if(!this.calcValues[n-this.valuePlus] && n-this.valuePlus > 0) if(!this.calcValues[n-this.valuePlus] && n-this.valuePlus > 0)
this.cache(n-this.valuePlus) this.cache(n-this.valuePlus)
// Setting current variables // Setting current variables
C.currentVars = Object.assign( Runtime.ExprParser.currentVars = Object.assign(
{'n': n-this.valuePlus}, // Just in case, add n (for custom functions) {'n': n-this.valuePlus}, // Just in case, add n (for custom functions)
C.currentObjectsByName, Runtime.Objects.currentObjectsByName,
{[this.name]: this.calcValues} {[this.name]: this.calcValues}
) )
this.calcValues[n] = expr.evaluate(C.currentVars) this.calcValues[n] = expr.evaluate(Runtime.ExprParser.currentVars)
} }
toString(forceSign=false) { toString(forceSign=false) {
var str = Utils.makeExpressionReadable(this.calc.toString()) let str = Utils.makeExpressionReadable(this.calc.toString())
if(str[0] != '-' && forceSign) str = '+' + str if(str[0] !== '-' && forceSign) str = '+' + str
var subtxt = this.valuePlus == 0 ? 'ₙ' : Utils.textsub('n+' + this.valuePlus) let subtxt = this.valuePlus === 0 ? 'ₙ' : Utils.textsub('n+' + this.valuePlus)
var ret = `${this.name}${subtxt} = ${str}${this.baseValues.length == 0 ? '' : "\n"}` let ret = `${this.name}${subtxt} = ${str}${this.baseValues.length === 0 ? '' : "\n"}`
ret += Object.keys(this.baseValues).map( ret += Object.keys(this.baseValues).map(
n => `${this.name}${Utils.textsub(n)} = ${this.baseValues[n]}` n => `${this.name}${Utils.textsub(n)} = ${this.baseValues[n]}`
).join('; ') ).join('; ')
@ -89,10 +85,10 @@ class Sequence extends Expr.Expression {
} }
toLatexString(forceSign=false) { toLatexString(forceSign=false) {
var str = this.latexMarkup let str = this.latexMarkup
if(str[0] != '-' && forceSign) str = '+' + str if(str[0] !== '-' && forceSign) str = '+' + str
var subtxt = '_{n' + (this.valuePlus == 0 ? '' : '+' + this.valuePlus) + '}' let subtxt = '_{n' + (this.valuePlus === 0 ? '' : '+' + this.valuePlus) + '}'
var ret = `\\begin{array}{l}${Latex.variable(this.name)}${subtxt} = ${str}${this.latexValues.length == 0 ? '' : "\n"}\\\\` let ret = `\\begin{array}{l}${Latex.variable(this.name)}${subtxt} = ${str}${this.latexValues.length === 0 ? '' : "\n"}\\\\`
ret += Object.keys(this.latexValues).map( ret += Object.keys(this.latexValues).map(
n => `${this.name}_{${n}} = ${this.latexValues[n]}` n => `${this.name}_{${n}} = ${this.latexValues[n]}`
).join('; ') + "\\end{array}" ).join('; ') + "\\end{array}"

View file

@ -16,26 +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/>.
*/ */
.pragma library
.import "math/expression.js" as Expr import * as Expr from "math/expression.mjs"
.import "math/sequence.js" as Seq import * as Seq from "math/sequence.mjs"
import * as Dom from "math/domain.mjs"
.import "math/domain.js" as Dom
var Expression = Expr.Expression export const Expression = Expr.Expression
var executeExpression = Expr.executeExpression export const executeExpression = Expr.executeExpression
var Sequence = Seq.Sequence export const Sequence = Seq.Sequence
// Domains // Domains
var Domain = Dom.Domain export const Domain = Dom.Domain
var EmptySet = Dom.EmptySet export const EmptySet = Dom.EmptySet
var Range = Dom.Range export const Range = Dom.Range
var SpecialDomain = Dom.SpecialDomain export const SpecialDomain = Dom.SpecialDomain
var DomainSet = Dom.DomainSet export const DomainSet = Dom.DomainSet
var UnionDomain = Dom.UnionDomain export const UnionDomain = Dom.UnionDomain
var IntersectionDomain = Dom.IntersectionDomain export const IntersectionDomain = Dom.IntersectionDomain
var MinusDomain = Dom.MinusDomain export const MinusDomain = Dom.MinusDomain
var parseDomain = Dom.parseDomain export const parseDomain = Dom.parseDomain
var parseDomainSimple = Dom.parseDomainSimple export const parseDomainSimple = Dom.parseDomainSimple

View file

@ -0,0 +1,7 @@
// Loading modules in order
.import "objects.mjs" as Objects
.import "lib/expr-eval/integration.js" as ExprParser
.import "objs/autoload.mjs" as Autoload
.import "math/latex.mjs" as Latex
.import "history/common.mjs" as HistoryCommon

View file

@ -1,96 +0,0 @@
/**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
.pragma library
.import "utils.js" as Utils
.import "math/common.js" as MathCommons
.import "parameters.js" as P
var types = {}
var currentObjects = {}
var currentObjectsByName = {}
MathCommons.currentObjectsByName = currentObjectsByName // Required for using objects in variables.
function renameObject(oldName, newName) {
/**
* Renames an object from its old name to the new one.
* @param {string} oldName - Current name of the object.
* @param {string} newName - Name to rename the object to.
*/
let obj = currentObjectsByName[oldName]
delete currentObjectsByName[oldName]
currentObjectsByName[newName] = obj
obj.name = newName
}
function deleteObject(objName) {
/**
* Deletes an object by its given name.
* @param {string} objName - Current name of the object.
*/
let obj = currentObjectsByName[objName]
currentObjects[obj.type].splice(currentObjects[obj.type].indexOf(obj),1)
obj.delete()
delete currentObjectsByName[objName]
}
function getObjectsName(objType) {
/**
* Gets a list of all names of a certain object type.
* @param {string} objType - Type of the object to query. Can be ExecutableObject for all ExecutableObjects.
* @return {array} List of names of the objects.
*/
if(objType == "ExecutableObject") {
// NOTE: QMLJS does not support flatMap.
// return getExecutableTypes().flatMap(elemType => currentObjects[elemType].map(obj => obj.name))
let types = getExecutableTypes()
let elementNames = ['']
for(let elemType of types)
elementNames = elementNames.concat(currentObjects[elemType].map(obj => obj.name))
return elementNames
}
if(currentObjects[objType] == undefined) return []
return currentObjects[objType].map(obj => obj.name)
}
function getExecutableTypes() {
/**
* Returns a list of all object types which are executable objects.
* @return {array} List of all object types which are executable objects.
*/
return Object.keys(currentObjects).filter(objType => types[objType].executable())
}
function createNewRegisteredObject(objType, args=[]) {
/**
* Creates and register an object in the database.
* @param {string} objType - Type of the object to create.
* @param {string} args - List of arguments for the objects (can be empty).
* @return {[objType]} Newly created object.
*/
if(Object.keys(types).indexOf(objType) == -1) return null // Object type does not exist.
var newobj = new types[objType](...args)
if(Object.keys(currentObjects).indexOf(objType) == -1) {
currentObjects[objType] = []
}
currentObjects[objType].push(newobj)
currentObjectsByName[newobj.name] = newobj
return newobj
}

View file

@ -0,0 +1,112 @@
/**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { RuntimeAPI } from './runtime.mjs'
class ObjectsAPI extends RuntimeAPI {
constructor() {
super('Objects')
this.types = {}
/**
* List of objects for each type of object.
* @type {Object.<string,DrawableObject[]>}
*/
this.currentObjects = {}
/**
* List of objects matched by their name.
* @type {Object.<string,DrawableObject>}
*/
this.currentObjectsByName = {}
}
/**
* Renames an object from its old name to the new one.
* @param {string} oldName - Current name of the object.
* @param {string} newName - Name to rename the object to.
*/
renameObject(oldName, newName) {
let obj = this.currentObjectsByName[oldName]
delete this.currentObjectsByName[oldName]
this.currentObjectsByName[newName] = obj
obj.name = newName
}
/**
* Deletes an object by its given name.
* @param {string} objName - Current name of the object.
*/
deleteObject(objName) {
let obj = this.currentObjectsByName[objName]
if(obj !== undefined) {
this.currentObjects[obj.type].splice(this.currentObjects[obj.type].indexOf(obj),1)
obj.delete()
delete this.currentObjectsByName[objName]
}
}
/**
* Gets a list of all names of a certain object type.
* @param {string} objType - Type of the object to query. Can be ExecutableObject for all ExecutableObjects.
* @returns {string[]} List of names of the objects.
*/
getObjectsName(objType) {
if(objType === "ExecutableObject") {
// NOTE: QMLJS does not support flatMap.
// return getExecutableTypes().flatMap(elemType => 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 []
return this.currentObjects[objType].map(obj => obj.name)
}
/**
* Returns a list of all object types which are executable objects.
* @return {string[]} List of all object types which are executable objects.
*/
getExecutableTypes() {
return Object.keys(this.currentObjects).filter(objType => this.types[objType].executable())
}
/**
* Creates and register an object in the database.
* @param {string} objType - Type of the object to create.
* @param {string[]} args - List of arguments for the objects (can be empty).
* @return {DrawableObject<objType>} Newly created object.
*/
createNewRegisteredObject(objType, args= []) {
if(Object.keys(this.types).indexOf(objType) === -1) return null // Object type does not exist.
let newobj = new this.types[objType](...args)
if(Object.keys(this.currentObjects).indexOf(objType) === -1) {
this.currentObjects[objType] = []
}
this.currentObjects[objType].push(newobj)
this.currentObjectsByName[newobj.name] = newobj
return newobj
}
}
/** @type {ObjectsAPI} */
Runtime.Objects = Runtime.Objects || new ObjectsAPI()
export default Runtime.Objects

View file

@ -0,0 +1,42 @@
/**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { API as ObjectsCommonAPI } from "common.mjs"
import Point from "point.mjs"
import Text from "text.mjs"
import Function from "function.mjs"
import GainBode from "gainbode.mjs"
import PhaseBode from "phasebode.mjs"
import SommeGainsBode from "sommegainsbode.mjs"
import SommePhasesBode from "sommephasesbode.mjs"
import XCursor from "xcursor.mjs"
import Sequence from "sequence.mjs"
import RepartitionFunction from "repartition.mjs"
if(Object.keys(Runtime.Objects.types).length === 0) {
ObjectsCommonAPI.registerObject(Point)
ObjectsCommonAPI.registerObject(Text)
ObjectsCommonAPI.registerObject(Function)
ObjectsCommonAPI.registerObject(GainBode)
ObjectsCommonAPI.registerObject(PhaseBode)
ObjectsCommonAPI.registerObject(SommeGainsBode)
ObjectsCommonAPI.registerObject(SommePhasesBode)
ObjectsCommonAPI.registerObject(XCursor)
ObjectsCommonAPI.registerObject(Sequence)
ObjectsCommonAPI.registerObject(RepartitionFunction)
}

View file

@ -16,42 +16,71 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import { getRandomColor, textsub } from "../utils.mjs"
import Objects from "../objects.mjs"
.import "../utils.js" as Utils import Latex from "../math/latex.mjs"
.import "../objects.js" as Objects import {RuntimeAPI} from "../runtime.mjs"
.import "../math/latex.js" as Latex
.import "../parameters.js" as P
.import "../math/common.js" as C
// 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
/** class ObjectsCommonAPI extends RuntimeAPI {
* Creates a new name for an object, based on the \c allowedLetters.
* If variables with each of the allowedLetters is created, a subscript constructor() {
* number is added to the name. super('ObjectsCommon', [
* @param {string} prefix - Prefix to the name. Runtime.Objects,
* @return {string} New unused name for a new object. Runtime.ExprParser,
*/ Runtime.Latex
function getNewName(allowedLetters, prefix='') { ])
// Allows to get a new name, based on the allowed letters, }
// as well as adding a sub number when needs be.
var newid = 0 /**
var ret * Creates a new name for an object, based on the \c allowedLetters.
do { * If variables with each of the allowedLetters is created, a subscript
var letter = allowedLetters[newid % allowedLetters.length] * number is added to the name.
var num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length) * @param {string} allowedLetters
ret = prefix + letter + (num > 0 ? Utils.textsub(num-1) : '') * @param {string} prefix - Prefix to the name.
newid += 1 * @return {string} New unused name for a new object.
} while(ret in Objects.currentObjectsByName) */
return ret getNewName(allowedLetters, prefix='') {
// Allows to get a new name, based on the allowed letters,
// as well as adding a sub number when needs be.
let newid = 0
let ret
do {
let letter = allowedLetters[newid % allowedLetters.length]
let num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length)
ret = prefix + letter + (num > 0 ? textsub(num-1) : '')
newid += 1
} while(ret in Objects.currentObjectsByName)
return ret
}
/**
* Registers the object \c obj in the object list.
* @param {DrawableObject} obj - Object to be registered.
*/
registerObject(obj) {
// Registers an object to be used in LogarithmPlotter.
// This function is called from autoload.mjs
if(obj.prototype instanceof DrawableObject) {
if(!Objects.types[obj.type()])
Objects.types[obj.type()] = obj
} else {
console.error("Could not register object " + (obj.type()) + ", as it isn't a DrawableObject.")
}
}
} }
/** @type {ObjectsCommonAPI} */
Runtime.ObjectsCommon = Runtime.ObjectsCommon || new ObjectsCommonAPI()
export const API = Runtime.ObjectsCommon
/** /**
* Class to extend for every type of object that * Class to extend for every type of object that
* can be drawn on the canvas. * can be drawn on the canvas.
*/ */
class DrawableObject { 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.
@ -73,7 +102,7 @@ class DrawableObject {
/** /**
* 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 {bool} * @return {boolean}
*/ */
static createable() {return true} static createable() {return true}
@ -89,30 +118,30 @@ class DrawableObject {
* 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.js and * Enums that are translated should be indexed in parameters.mjs and
* then be linked directly here. * then be linked directly here.
* *
* @return {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 {bool} * @return {boolean}
*/ */
static executable() {return false} static executable() {return false}
/** /**
* 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 {bool} visible - true if the object is visible, false otherwise. * @param {boolean} visible - true if the object is visible, false otherwise.
* @param {color} color - Color of the object (can be string or QColor) * @param {color|string} color - Color of the object (can be string or QColor)
* @param {Enum} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label. * @param {Enum} 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 = Utils.getRandomColor() if(color == null) color = getRandomColor()
this.type = this.constructor.type() this.type = this.constructor.type()
this.name = name this.name = name
this.visible = visible this.visible = visible
@ -145,7 +174,7 @@ class DrawableObject {
* 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.js for helper methods. * See ../latex.mjs for helper methods.
* @return {string} * @return {string}
*/ */
getLatexString() { getLatexString() {
@ -172,7 +201,7 @@ class DrawableObject {
* Latex markup string content of the label depending on the value of the \c latexContent. * Latex markup string content of the label depending on the value of the \c latexContent.
* Every non latin character should be passed as latex symbols and formulas * Every non latin character should be passed as latex symbols and formulas
* should be in latex form. * should be in latex form.
* See ../latex.js for helper methods. * See ../latex.mjs for helper methods.
* @return {string} * @return {string}
*/ */
getLatexLabel() { getLatexLabel() {
@ -204,25 +233,26 @@ class DrawableObject {
update() { update() {
// Refreshing dependencies. // Refreshing dependencies.
for(let obj of this.requires) for(let obj of this.requires)
obj.requiredBy = obj.requiredBy.filter(dep => dep != this) obj.requiredBy = obj.requiredBy.filter(dep => dep !== this)
// Checking objects this one depends on // Checking objects this one depends on
this.requires = [] this.requires = []
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 C.currentObjectsByName && !this.requires.includes(C.currentObjectsByName[objName])) { if(objName in currentObjectsByName && !this.requires.includes(currentObjectsByName[objName])) {
this.requires.push(C.currentObjectsByName[objName]) this.requires.push(currentObjectsByName[objName])
C.currentObjectsByName[objName].requiredBy.push(this) currentObjectsByName[objName].requiredBy.push(this)
} }
} }
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)
@ -239,12 +269,16 @@ class DrawableObject {
for(let toRemove of this.requiredBy) { // Normally, there should be none here, but better leave nothing just in case. for(let toRemove of this.requiredBy) { // Normally, there should be none here, but better leave nothing just in case.
Objects.deleteObject(toRemove.name) Objects.deleteObject(toRemove.name)
} }
console.log(this.requires)
for(let toRemoveFrom of this.requires) {
toRemoveFrom.requiredBy = toRemoveFrom.requiredBy.filter(o => o !== this)
}
} }
/** /**
* Abstract method. Draw the object onto the \c canvas with the 2D context \c ctx. * Abstract method. Draw the object onto the \c canvas with the 2D context \c ctx.
* @param {Canvas} canvas * @param {Canvas} canvas
* @param {Context2D} ctx * @param {CanvasRenderingContext2D} ctx
*/ */
draw(canvas, ctx) {} draw(canvas, ctx) {}
@ -256,7 +290,7 @@ class DrawableObject {
* *
* @param {string|Enum} labelPosition - Position of the label relative to the marked position * @param {string|Enum} labelPosition - Position of the label relative to the marked position
* @param {number} offset - Margin between the position and the object to be drawn * @param {number} offset - Margin between the position and the object to be drawn
* @param {Dictionary} size - Size of the label item, containing two properties, "width" and "height" * @param {Object.<string, int>} 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
@ -310,17 +344,17 @@ class DrawableObject {
* \c drawFunctionText (x,y,text) depending on whether to use latex. * \c drawFunctionText (x,y,text) depending on whether to use latex.
* *
* @param {Canvas} canvas * @param {Canvas} canvas
* @param {Context2D} ctx * @param {CanvasRenderingContext2D} ctx
* @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 {bool} 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, ctx, labelPosition, posX, posY, forceText = false, drawLabel(canvas, ctx, 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)
@ -333,7 +367,7 @@ class DrawableObject {
drawFunctionText = (x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, y+textSize.height) // Positioned from left bottom drawFunctionText = (x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, y+textSize.height) // Positioned from left bottom
// Drawing the label // Drawing the label
let offset let offset
if(!forceText && Latex.enabled) { // TODO: Check for user setting with Latex. if(!forceText && Latex.enabled) {
// With latex // With latex
let drawLblCb = function(canvas, ctx, ltxImg) { let drawLblCb = function(canvas, ctx, ltxImg) {
this.drawPositionDivergence(labelPosition, 8+ctx.lineWidth/2, ltxImg, posX, posY, (x,y) => drawFunctionLatex(x,y,ltxImg)) this.drawPositionDivergence(labelPosition, 8+ctx.lineWidth/2, ltxImg, posX, posY, (x,y) => drawFunctionLatex(x,y,ltxImg))
@ -355,6 +389,7 @@ class DrawableObject {
} }
} }
/** /**
* 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.
@ -362,7 +397,7 @@ class DrawableObject {
* return null. However, theses same x values will * return null. However, theses same x values will
* return false when passed to canExecute. * return false when passed to canExecute.
*/ */
class ExecutableObject extends DrawableObject { 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
@ -376,38 +411,25 @@ class ExecutableObject extends DrawableObject {
* 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 {bool} * @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 {bool} * @returns {string}
*/ */
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 {bool} * @return {boolean}
*/ */
static executable() {return true} static executable() {return true}
} }
/**
* Registers the object \c obj in the object list.
* @param {DrawableObject} obj - Object to be registered.
*/
function registerObject(obj) {
// Registers an object to be used in LogarithmPlotter.
// This function is called from autoload.js
if(obj.prototype instanceof DrawableObject) {
Objects.types[obj.type()] = obj
} else {
console.error("Could not register object " + (obj.type()) + ", as it isn't a DrawableObject.")
}
}

View file

@ -16,16 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import { textsub } from "../utils.mjs"
import { API as Common, ExecutableObject } from "common.mjs"
.import "common.js" as Common import { parseDomain, Expression, SpecialDomain } from "../mathlib.mjs"
.import "../utils.js" as Utils import * as P from "../parameters.mjs"
.import "../mathlib.js" as MathLib import Latex from "../math/latex.mjs"
.import "../parameters.js" as P
.import "../math/latex.js" as Latex
class Function extends Common.ExecutableObject { export default class Function extends ExecutableObject {
static type(){return 'Function'} static type(){return 'Function'}
static displayType(){return qsTr('Function')} static displayType(){return qsTr('Function')}
static displayTypeMultiple(){return qsTr('Functions')} static displayTypeMultiple(){return qsTr('Functions')}
@ -54,11 +52,11 @@ class Function extends Common.ExecutableObject {
drawPoints = true, drawDashedLines = true) { drawPoints = true, drawDashedLines = true) {
if(name == null) name = Common.getNewName('fghjqlmnopqrstuvwabcde') if(name == null) name = Common.getNewName('fghjqlmnopqrstuvwabcde')
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
if(typeof expression == 'number' || typeof expression == 'string') expression = new MathLib.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 = MathLib.parseDomain(definitionDomain) if(typeof definitionDomain == 'string') definitionDomain = parseDomain(definitionDomain)
this.definitionDomain = definitionDomain this.definitionDomain = definitionDomain
if(typeof destinationDomain == 'string') destinationDomain = MathLib.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
@ -68,15 +66,15 @@ class Function extends Common.ExecutableObject {
} }
getReadableString() { getReadableString() {
if(this.displayMode == 'application') { if(this.displayMode === 'application') {
return `${this.name}: ${this.definitionDomain}${this.destinationDomain}\n ${' '.repeat(this.name.length)}x ⟼ ${this.expression.toString()}` return `${this.name}: ${this.definitionDomain}${this.destinationDomain}\n ${' '.repeat(this.name.length)}x ⟼ ${this.expression.toString()}`
} else { } else {
return `${this.name}(x) = ${this.expression.toString()}\nD${Utils.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 {
@ -111,14 +109,16 @@ class Function extends Common.ExecutableObject {
// Label // Label
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
} }
/**
* Reusable in other objects.
* Drawing small traits every few pixels
*/
static drawFunction(canvas, ctx, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) { static drawFunction(canvas, ctx, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) {
// Reusable in other objects. let pxprecision = 10
// Drawing small traits every 0.2px let previousX = canvas.px2x(0)
var pxprecision = 10 let previousY = null;
var previousX = canvas.px2x(0) if(definitionDomain instanceof SpecialDomain && definitionDomain.moveSupported) {
var previousY = null;
if(definitionDomain instanceof MathLib.SpecialDomain && definitionDomain.moveSupported) {
// Point based functions. // Point based functions.
previousX = definitionDomain.next(previousX) previousX = definitionDomain.next(previousX)
if(previousX === null) previousX = definitionDomain.next(canvas.px2x(0)) if(previousX === null) previousX = definitionDomain.next(canvas.px2x(0))
@ -126,8 +126,8 @@ class Function extends Common.ExecutableObject {
if(!drawPoints && !drawDash) return if(!drawPoints && !drawDash) return
while(previousX !== null && canvas.x2px(previousX) < canvas.canvasSize.width) { while(previousX !== null && canvas.x2px(previousX) < canvas.canvasSize.width) {
// Reconverted for canvas to fix for logarithmic scales. // Reconverted for canvas to fix for logarithmic scales.
var currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX)+pxprecision)); let currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX)+pxprecision));
var 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))) {
@ -173,7 +173,7 @@ class Function extends Common.ExecutableObject {
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.canvasSize.height)) let maxvariation = (canvas.px2y(0)-canvas.px2y(canvas.canvasSize.height))

View file

@ -16,19 +16,19 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import { parseDomain, executeExpression, Expression, EmptySet, Domain } from "../mathlib.mjs"
import * as P from "../parameters.mjs"
import Objects from "../objects.mjs"
import Latex from "../math/latex.mjs"
.import "common.js" as Common import { API as Common, ExecutableObject } from "common.mjs"
.import "function.js" as F import Function from "function.mjs"
.import "../objects.js" as Objects
.import "../utils.js" as Utils import { API as HistoryAPI } from "../history/common.mjs"
.import "../mathlib.js" as MathLib import { CreateNewObject } from "../historylib.mjs"
.import "../historylib.js" as HistoryLib
.import "../parameters.js" as P
.import "../math/latex.js" as Latex
class GainBode extends Common.ExecutableObject { export default class GainBode extends ExecutableObject {
static type(){return 'Gain Bode'} static type(){return 'Gain Bode'}
static displayType(){return qsTr('Bode Magnitude')} static displayType(){return qsTr('Bode Magnitude')}
static displayTypeMultiple(){return qsTr('Bode Magnitudes')} static displayTypeMultiple(){return qsTr('Bode Magnitudes')}
@ -44,7 +44,7 @@ class GainBode extends Common.ExecutableObject {
constructor(name = null, visible = true, color = null, labelContent = 'name + value', constructor(name = null, visible = true, color = null, labelContent = 'name + value',
om_0 = '', pass = 'high', gain = '20', labelPosition = 'above', labelX = 1, omGraduation = false) { om_0 = '', pass = 'high', gain = '20', labelPosition = 'above', labelX = 1, omGraduation = false) {
if(name == null) name = Common.getNewName('G') if(name == null) name = Common.getNewName('G')
if(name == 'G') name = 'G₀' // G is reserved for sum of BODE magnitudes (Somme gains Bode). 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
@ -52,7 +52,7 @@ class GainBode extends Common.ExecutableObject {
if(om_0 == null) { if(om_0 == null) {
// Create new point // Create new point
om_0 = Objects.createNewRegisteredObject('Point', [Common.getNewName('ω'), true, this.color, 'name']) om_0 = Objects.createNewRegisteredObject('Point', [Common.getNewName('ω'), true, this.color, 'name'])
HistoryLib.history.addToHistory(new HistoryLib.CreateNewObject(om_0.name, 'Point', om_0.export())) HistoryAPI.history.addToHistory(new CreateNewObject(om_0.name, 'Point', om_0.export()))
om_0.update() om_0.update()
labelPosition = 'below' labelPosition = 'below'
} }
@ -60,7 +60,7 @@ class GainBode extends Common.ExecutableObject {
} }
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 MathLib.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
@ -68,12 +68,12 @@ class GainBode extends Common.ExecutableObject {
} }
getReadableString() { getReadableString() {
let pass = this.pass == "low" ? qsTr("low-pass") : qsTr("high-pass"); let pass = this.pass === "low" ? qsTr("low-pass") : qsTr("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" ? qsTr("low-pass") : qsTr("high-pass"); let pass = this.pass === "low" ? qsTr("low-pass") : qsTr("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}
@ -86,9 +86,9 @@ class GainBode extends Common.ExecutableObject {
} }
execute(x=1) { execute(x=1) {
if(typeof x == 'string') x = MathLib.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)) {
var dbfn = new MathLib.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()
@ -96,10 +96,10 @@ class GainBode extends Common.ExecutableObject {
} }
simplify(x = 1) { simplify(x = 1) {
var xval = x let xval = x
if(typeof x == 'string') xval = MathLib.executeExpression(x) if(typeof x == 'string') xval = executeExpression(x)
if((this.pass == 'high' && xval < this.om_0.x) || (this.pass == 'low' && xval > this.om_0.x)) { if((this.pass === 'high' && xval < this.om_0.x) || (this.pass === 'low' && xval > this.om_0.x)) {
var dbfn = new MathLib.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()
@ -111,20 +111,20 @@ class GainBode extends Common.ExecutableObject {
} }
draw(canvas, ctx) { draw(canvas, ctx) {
var base = [canvas.x2px(this.om_0.x), canvas.y2px(this.om_0.y)] let base = [canvas.x2px(this.om_0.x), canvas.y2px(this.om_0.y)]
var dbfn = new MathLib.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}`)
var inDrawDom = new MathLib.EmptySet() let inDrawDom = new EmptySet()
if(this.pass == 'high') { if(this.pass === 'high') {
// High pass, linear line from begining, then constant to the end. // High pass, linear line from beginning, then constant to the end.
canvas.drawLine(ctx, base[0], base[1], canvas.canvasSize.width, base[1]) canvas.drawLine(ctx, base[0], base[1], canvas.canvasSize.width, base[1])
inDrawDom = MathLib.parseDomain(`]-inf;${this.om_0.x}[`) inDrawDom = parseDomain(`]-inf;${this.om_0.x}[`)
} else { } else {
// Low pass, constant from the beginning, linear line to the end. // Low pass, constant from the beginning, linear line to the end.
canvas.drawLine(ctx, base[0], base[1], 0, base[1]) canvas.drawLine(ctx, base[0], base[1], 0, base[1])
inDrawDom = MathLib.parseDomain(`]${this.om_0.x};+inf[`) inDrawDom = parseDomain(`]${this.om_0.x};+inf[`)
} }
F.Function.drawFunction(canvas, ctx, dbfn, inDrawDom, MathLib.Domain.R) Function.drawFunction(canvas, ctx, dbfn, inDrawDom, Domain.R)
// Dashed line representing break in function // Dashed line representing break in function
var xpos = canvas.x2px(this.om_0.x.execute()) var xpos = canvas.x2px(this.om_0.x.execute())
var dashPxSize = 10 var dashPxSize = 10
@ -137,7 +137,7 @@ class GainBode extends Common.ExecutableObject {
update() { update() {
super.update() super.update()
if(Objects.currentObjects['Somme gains Bode'] != undefined && Objects.currentObjects['Somme gains Bode'].length > 0) { if(Objects.currentObjects['Somme gains Bode'] !== undefined && Objects.currentObjects['Somme gains Bode'].length > 0) {
Objects.currentObjects['Somme gains Bode'][0].recalculateCache() Objects.currentObjects['Somme gains Bode'][0].recalculateCache()
} else { } else {
Objects.createNewRegisteredObject('Somme gains Bode') Objects.createNewRegisteredObject('Somme gains Bode')

View file

@ -16,17 +16,17 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import { executeExpression, Expression } from "../mathlib.mjs"
import * as P from "../parameters.mjs"
import Objects from "../objects.mjs"
import Latex from "../math/latex.mjs"
.import "common.js" as Common import { API as Common, ExecutableObject } from "common.mjs"
.import "../objects.js" as Objects import { API as HistoryAPI } from "../history/common.mjs"
.import "../mathlib.js" as MathLib import { CreateNewObject } from "../historylib.mjs"
.import "../historylib.js" as HistoryLib
.import "../parameters.js" as P
.import "../math/latex.js" as Latex
class PhaseBode extends Common.ExecutableObject { export default class PhaseBode extends ExecutableObject {
static type(){return 'Phase Bode'} static type(){return 'Phase Bode'}
static displayType(){return qsTr('Bode Phase')} static displayType(){return qsTr('Bode Phase')}
static displayTypeMultiple(){return qsTr('Bode Phases')} static displayTypeMultiple(){return qsTr('Bode Phases')}
@ -41,9 +41,9 @@ class PhaseBode extends Common.ExecutableObject {
constructor(name = null, visible = true, color = null, labelContent = 'name + value', constructor(name = null, visible = true, color = null, labelContent = 'name + value',
om_0 = '', phase = 90, unit = '°', labelPosition = 'above', labelX = 1) { om_0 = '', phase = 90, unit = '°', labelPosition = 'above', labelX = 1) {
if(name == null) name = Common.getNewName('φ') if(name == null) name = Common.getNewName('φ')
if(name == 'φ') name = 'φ₀' // φ is reserved for sum of BODE phases (Somme phases Bode). if(name === 'φ') name = 'φ₀' // φ is reserved for sum of BODE phases (Somme phases Bode).
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
if(typeof phase == 'number' || typeof phase == 'string') phase = new MathLib.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
@ -52,7 +52,7 @@ class PhaseBode extends Common.ExecutableObject {
// Create new point // Create new point
om_0 = Objects.createNewRegisteredObject('Point', [Common.getNewName('ω'), this.color, 'name']) om_0 = Objects.createNewRegisteredObject('Point', [Common.getNewName('ω'), this.color, 'name'])
om_0.labelPosition = this.phase.execute() >= 0 ? 'above' : 'below' om_0.labelPosition = this.phase.execute() >= 0 ? 'above' : 'below'
HistoryLib.history.addToHistory(new HistoryLib.CreateNewObject(om_0.name, 'Point', om_0.export())) HistoryAPI.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)
@ -77,7 +77,7 @@ class PhaseBode extends Common.ExecutableObject {
} }
execute(x=1) { execute(x=1) {
if(typeof x == 'string') x = MathLib.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 {
@ -86,13 +86,13 @@ class PhaseBode extends Common.ExecutableObject {
} }
simplify(x = 1) { simplify(x = 1) {
var xval = x let xval = x
if(typeof x == 'string') xval = MathLib.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 {
var newExp = this.om_0.y.toEditableString() + ' + ' + this.phase.toEditableString() let newExp = this.om_0.y.toEditableString() + ' + ' + this.phase.toEditableString()
return (new MathLib.Expression(newExp)).toString() return (new Expression(newExp)).toString()
} }
} }
@ -101,11 +101,11 @@ class PhaseBode extends Common.ExecutableObject {
} }
draw(canvas, ctx) { draw(canvas, ctx) {
var baseX = canvas.x2px(this.om_0.x.execute()) let baseX = canvas.x2px(this.om_0.x.execute())
var omy = this.om_0.y.execute() let omy = this.om_0.y.execute()
var augmt = this.phase.execute() let augmt = this.phase.execute()
var baseY = canvas.y2px(omy) let baseY = canvas.y2px(omy)
var augmtY = canvas.y2px(omy+augmt) let augmtY = canvas.y2px(omy+augmt)
// Before change line. // Before change line.
canvas.drawLine(ctx, 0, baseY, Math.min(baseX, canvas.canvasSize.height), baseY) canvas.drawLine(ctx, 0, baseY, Math.min(baseX, canvas.canvasSize.height), baseY)
// Transition line. // Transition line.
@ -118,7 +118,7 @@ class PhaseBode extends Common.ExecutableObject {
} }
update() { update() {
if(Objects.currentObjects['Somme phases Bode'] != undefined && Objects.currentObjects['Somme phases Bode'].length > 0) { if(Objects.currentObjects['Somme phases Bode'] !== undefined && Objects.currentObjects['Somme phases Bode'].length > 0) {
Objects.currentObjects['Somme phases Bode'][0].recalculateCache() Objects.currentObjects['Somme phases Bode'][0].recalculateCache()
} else { } else {
Objects.createNewRegisteredObject('Somme phases Bode') Objects.createNewRegisteredObject('Somme phases Bode')

View file

@ -16,15 +16,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import { Expression } from "../mathlib.mjs"
import * as P from "../parameters.mjs"
.import "common.js" as Common import Latex from "../math/latex.mjs"
.import "../mathlib.js" as MathLib import { API as Common, DrawableObject } from "common.mjs"
.import "../parameters.js" as P
.import "../math/latex.js" as Latex
class Point extends Common.DrawableObject { export default class Point extends DrawableObject {
static type(){return 'Point'} static type(){return 'Point'}
static displayType(){return qsTr('Point')} static displayType(){return qsTr('Point')}
static displayTypeMultiple(){return qsTr('Points')} static displayTypeMultiple(){return qsTr('Points')}
@ -40,9 +38,9 @@ class Point extends Common.DrawableObject {
x = 1, y = 0, labelPosition = 'above', pointStyle = '●') { x = 1, y = 0, labelPosition = 'above', pointStyle = '●') {
if(name == null) name = Common.getNewName('ABCDEFJKLMNOPQRSTUVW') if(name == null) name = Common.getNewName('ABCDEFJKLMNOPQRSTUVW')
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
if(typeof x == 'number' || typeof x == 'string') x = new MathLib.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 MathLib.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

View file

@ -16,14 +16,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import { API as Common, ExecutableObject } from "common.mjs"
import * as P from "../parameters.mjs"
.import "common.js" as Common import Latex from "../math/latex.mjs"
.import "../parameters.js" as P
.import "../math/latex.js" as Latex
class RepartitionFunction extends Common.ExecutableObject { export default class RepartitionFunction extends ExecutableObject {
static type(){return 'Repartition'} static type(){return 'Repartition'}
static displayType(){return qsTr('Repartition')} static displayType(){return qsTr('Repartition')}
static displayTypeMultiple(){return qsTr('Repartition functions')} static displayTypeMultiple(){return qsTr('Repartition functions')}
@ -34,7 +32,7 @@ class RepartitionFunction extends Common.ExecutableObject {
'comment', 'comment',
'Note: Specify the probability for each value.' 'Note: Specify the probability for each value.'
), ),
[QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d\.,]+$/, 'P({name_} = ', ') = '), [QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d.,]+$/, 'P({name_} = ', ') = '),
}} }}
constructor(name = null, visible = true, color = null, labelContent = 'name + value', constructor(name = null, visible = true, color = null, labelContent = 'name + value',
@ -69,7 +67,7 @@ class RepartitionFunction extends Common.ExecutableObject {
} }
execute(x = 1) { execute(x = 1) {
var 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, '.'))
}) })
@ -77,6 +75,7 @@ class RepartitionFunction extends Common.ExecutableObject {
} }
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) return this.execute(x)
@ -94,8 +93,8 @@ class RepartitionFunction extends Common.ExecutableObject {
} }
draw(canvas, ctx) { draw(canvas, ctx) {
var currentY = 0; let currentY = 0;
var 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(ctx, canvas.drawLine(ctx,
0, 0,
@ -109,8 +108,8 @@ class RepartitionFunction extends Common.ExecutableObject {
ctx.stroke(); ctx.stroke();
} }
} }
for(var i = 0; i < keys.length-1; i++) { for(let i = 0; i < keys.length-1; i++) {
var 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(ctx, canvas.drawLine(ctx,

View file

@ -16,16 +16,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import { Sequence as MathSequence, Domain } from "../mathlib.mjs"
import * as P from "../parameters.mjs"
import Latex from "../math/latex.mjs"
.import "common.js" as Common import { API as Common, ExecutableObject } from "common.mjs"
.import "function.js" as F import Function from "function.mjs"
.import "../mathlib.js" as MathLib
.import "../parameters.js" as P
.import "../math/latex.js" as Latex
class Sequence extends Common.ExecutableObject { export default class Sequence extends ExecutableObject {
static type(){return 'Sequence'} static type(){return 'Sequence'}
static displayType(){return qsTr('Sequence')} static displayType(){return qsTr('Sequence')}
static displayTypeMultiple(){return qsTr('Sequences')} static displayTypeMultiple(){return qsTr('Sequences')}
@ -59,18 +58,21 @@ class Sequence extends Common.ExecutableObject {
export() { export() {
return [this.name, this.visible, this.color.toString(), this.labelContent, return [this.name, this.visible, this.color.toString(), this.labelContent,
this.drawPoints, this.drawDashedLines, this.defaultExpression, this.baseValues, this.labelPosition, this.labelX] this.drawPoints, this.drawDashedLines, this.defaultExpression, this.baseValues,
this.labelPosition, this.labelX]
} }
update() { update() {
console.log('Updating sequence', this.sequence)
console.trace()
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 !== Object.keys(this.defaultExpression)[0]
) )
this.sequence = new MathLib.Sequence( this.sequence = new MathSequence(
this.name, this.baseValues, this.name, this.baseValues,
Object.keys(this.defaultExpression)[0], Object.keys(this.defaultExpression)[0],
Object.values(this.defaultExpression)[0] Object.values(this.defaultExpression)[0]
@ -86,15 +88,15 @@ class Sequence extends Common.ExecutableObject {
} }
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
} }
@ -122,7 +124,7 @@ class Sequence extends Common.ExecutableObject {
} }
draw(canvas, ctx) { draw(canvas, ctx) {
F.Function.drawFunction(canvas, ctx, this.sequence, canvas.logscalex ? MathLib.Domain.NE : MathLib.Domain.N, MathLib.Domain.R, this.drawPoints, this.drawDashedLines) Function.drawFunction(canvas, ctx, this.sequence, canvas.logscalex ? Domain.NE : Domain.N, Domain.R, this.drawPoints, this.drawDashedLines)
// Label // Label
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))

View file

@ -16,17 +16,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import { parseDomain, Expression, Domain } from "../mathlib.mjs"
import * as P from "../parameters.mjs"
import Objects from "../objects.mjs"
import Latex from "../math/latex.mjs"
.import "common.js" as Common import { ExecutableObject } from "common.mjs"
.import "function.js" as F import Function from "function.mjs"
.import "../objects.js" as Objects
.import "../mathlib.js" as MathLib
.import "../parameters.js" as P
.import "../math/latex.js" as Latex
class SommeGainsBode extends Common.ExecutableObject { export default class SommeGainsBode extends ExecutableObject {
static type(){return 'Somme gains Bode'} static type(){return 'Somme gains Bode'}
static displayType(){return qsTr('Bode Magnitudes Sum')} static displayType(){return qsTr('Bode Magnitudes Sum')}
static displayTypeMultiple(){return qsTr('Bode Magnitudes Sum')} static displayTypeMultiple(){return qsTr('Bode Magnitudes Sum')}
@ -58,7 +57,7 @@ class SommeGainsBode extends Common.ExecutableObject {
} }
execute(x = 0) { execute(x = 0) {
for(var [dbfn, inDrawDom] of this.cachedParts) { for(let [dbfn, inDrawDom] of this.cachedParts) {
if(inDrawDom.includes(x)) { if(inDrawDom.includes(x)) {
return dbfn.execute(x) return dbfn.execute(x)
} }
@ -71,7 +70,7 @@ class SommeGainsBode extends Common.ExecutableObject {
} }
simplify(x = 1) { simplify(x = 1) {
for(var [dbfn, inDrawDom] of this.cachedParts) { for(let [dbfn, inDrawDom] of this.cachedParts) {
if(inDrawDom.includes(x)) { if(inDrawDom.includes(x)) {
return dbfn.simplify(x) return dbfn.simplify(x)
} }
@ -82,33 +81,33 @@ class SommeGainsBode extends Common.ExecutableObject {
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.
if(Objects.currentObjects['Gain Bode'] != undefined) { if(Objects.currentObjects['Gain Bode'] !== undefined) {
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.
var drawMin = 0.001 let drawMin = 0.001
var baseY = 0 let baseY = 0
var om0xGains = {1000000000: 0} // To draw the last part let om0xGains = {1000000000: 0} // To draw the last part
var om0xPass = {1000000000: 'high'} // To draw the last part let om0xPass = {1000000000: 'high'} // To draw the last part
Objects.currentObjects['Gain Bode'].forEach(function(gainObj) { // Sorting by their om_0 position. Objects.currentObjects['Gain Bode'].forEach(function(gainObj) { // Sorting by their om_0 position.
var om0x = gainObj.om_0.x.execute() let om0x = gainObj.om_0.x.execute()
if(om0xGains[om0x] == undefined) { if(om0xGains[om0x] === undefined) {
om0xGains[om0x] = gainObj.gain.execute() om0xGains[om0x] = gainObj.gain.execute()
om0xPass[om0x] = gainObj.pass == 'high' om0xPass[om0x] = gainObj.pass === 'high'
} else { } else {
om0xGains[om0x+0.001] = gainObj.gain.execute() om0xGains[om0x+0.001] = gainObj.gain.execute()
om0xPass[om0x+0.001] = gainObj.pass == 'high' om0xPass[om0x+0.001] = gainObj.pass === 'high'
} }
baseY += gainObj.execute(drawMin) baseY += gainObj.execute(drawMin)
}) })
// Sorting the om_0x // Sorting the om_0x
var om0xList = Object.keys(om0xGains).map(x => parseFloat(x)) // THEY WERE CONVERTED TO STRINGS... let om0xList = Object.keys(om0xGains).map(x => parseFloat(x)) // THEY WERE CONVERTED TO STRINGS...
om0xList.sort((a,b) => a - b) om0xList.sort((a,b) => a - b)
// Adding the total gains. // Adding the total gains.
var gainsBeforeP = [] let gainsBeforeP = []
var gainsAfterP = [] let gainsAfterP = []
var gainTotal = 0 let gainTotal = 0
for(var om0x of om0xList){ for(let om0x of om0xList){
if(om0xPass[om0x]) { // High-pass if(om0xPass[om0x]) { // High-pass
gainsBeforeP.push(om0xGains[om0x]) gainsBeforeP.push(om0xGains[om0x])
gainsAfterP.push(0) gainsAfterP.push(0)
@ -119,10 +118,10 @@ class SommeGainsBode extends Common.ExecutableObject {
} }
} }
// Calculating parts // Calculating parts
var previousPallier = drawMin let previousPallier = drawMin
for(var pallier = 0; pallier < om0xList.length; pallier++) { for(let pallier = 0; pallier < om0xList.length; pallier++) {
var dbfn = new MathLib.Expression(`${gainTotal}*(ln(x)-ln(${previousPallier}))/ln(10)+${baseY}`) let dbfn = new Expression(`${gainTotal}*(ln(x)-ln(${previousPallier}))/ln(10)+${baseY}`)
var inDrawDom = MathLib.parseDomain(`]${previousPallier};${om0xList[pallier]}]`) let inDrawDom = parseDomain(`]${previousPallier};${om0xList[pallier]}]`)
this.cachedParts.push([dbfn, inDrawDom]) this.cachedParts.push([dbfn, inDrawDom])
previousPallier = om0xList[pallier] previousPallier = om0xList[pallier]
baseY = dbfn.execute(om0xList[pallier]) baseY = dbfn.execute(om0xList[pallier])
@ -133,8 +132,8 @@ class SommeGainsBode extends Common.ExecutableObject {
draw(canvas, ctx) { draw(canvas, ctx) {
if(this.cachedParts.length > 0) { if(this.cachedParts.length > 0) {
for(var [dbfn, inDrawDom] of this.cachedParts) { for(let [dbfn, inDrawDom] of this.cachedParts) {
F.Function.drawFunction(canvas, ctx, dbfn, inDrawDom, MathLib.Domain.R) Function.drawFunction(canvas, ctx, dbfn, inDrawDom, Domain.R)
if(inDrawDom.includes(this.labelX)) { if(inDrawDom.includes(this.labelX)) {
// Label // Label
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))

View file

@ -16,16 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import { executeExpression, Expression } from "../mathlib.mjs"
import * as P from "../parameters.mjs"
import Objects from "../objects.mjs"
import Latex from "../math/latex.mjs"
.import "common.js" as Common import { ExecutableObject } from "common.mjs"
.import "../objects.js" as Objects
.import "../mathlib.js" as MathLib
.import "../parameters.js" as P
.import "../math/latex.js" as Latex
export default class SommePhasesBode extends ExecutableObject {
class SommePhasesBode extends Common.ExecutableObject {
static type(){return 'Somme phases Bode'} static type(){return 'Somme phases Bode'}
static displayType(){return qsTr('Bode Phases Sum')} static displayType(){return qsTr('Bode Phases Sum')}
static displayTypeMultiple(){return qsTr('Bode Phases Sum')} static displayTypeMultiple(){return qsTr('Bode Phases Sum')}
@ -57,18 +55,18 @@ class SommePhasesBode extends Common.ExecutableObject {
} }
execute(x=1) { execute(x=1) {
if(typeof x == 'string') x = MathLib.executeExpression(x) if(typeof x == 'string') x = executeExpression(x)
for(var 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) {
var xval = x let xval = x
if(typeof x == 'string') xval = MathLib.executeExpression(x) if(typeof x == 'string') xval = executeExpression(x)
for(var 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 MathLib.Expression(this.phasesExprList[i])).simplify() return (new Expression(this.phasesExprList[i])).simplify()
} }
} }
return '0' return '0'
@ -80,20 +78,20 @@ class SommePhasesBode extends Common.ExecutableObject {
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.
var drawMin = 0.001 let drawMin = 0.001
var drawMax = 100000 let drawMax = 100000
this.om0xList = [drawMin, drawMax] this.om0xList = [drawMin, drawMax]
this.phasesList = [0] this.phasesList = [0]
this.phasesExprList = ['0'] this.phasesExprList = ['0']
var phasesDict = {} let phasesDict = {}
var phasesExprDict = {} let phasesExprDict = {}
phasesDict[drawMax] = 0 phasesDict[drawMax] = 0
if(Objects.currentObjects['Phase Bode'] != undefined) { if(Objects.currentObjects['Phase Bode'] !== undefined) {
console.log('Recalculating cache phase') console.log('Recalculating cache phase')
for(var obj of Objects.currentObjects['Phase Bode']) { for(let obj of Objects.currentObjects['Phase Bode']) {
this.om0xList.push(obj.om_0.x.execute()) this.om0xList.push(obj.om_0.x.execute())
if(phasesDict[obj.om_0.x.execute()] == undefined) { if(phasesDict[obj.om_0.x.execute()] === undefined) {
phasesDict[obj.om_0.x.execute()] = obj.phase.execute() phasesDict[obj.om_0.x.execute()] = obj.phase.execute()
phasesExprDict[obj.om_0.x.execute()] = obj.phase.toEditableString() phasesExprDict[obj.om_0.x.execute()] = obj.phase.toEditableString()
} else { } else {
@ -104,8 +102,8 @@ class SommePhasesBode extends Common.ExecutableObject {
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)
var totalAdded = this.phasesList[0] let totalAdded = this.phasesList[0]
for(var i = 1; i < this.om0xList.length; i++) { for(let i = 1; i < this.om0xList.length; i++) {
this.phasesList[i] = this.phasesList[i-1] + phasesDict[this.om0xList[i]] this.phasesList[i] = this.phasesList[i-1] + phasesDict[this.om0xList[i]]
this.phasesExprList[i] = this.phasesExprList[i-1] + '+' + phasesDict[this.om0xList[i]] this.phasesExprList[i] = this.phasesExprList[i-1] + '+' + phasesDict[this.om0xList[i]]
} }
@ -113,11 +111,11 @@ class SommePhasesBode extends Common.ExecutableObject {
} }
draw(canvas, ctx) { draw(canvas, ctx) {
for(var i = 0; i < this.om0xList.length-1; i++) { for(let i = 0; i < this.om0xList.length-1; i++) {
var om0xBegin = canvas.x2px(this.om0xList[i]) let om0xBegin = canvas.x2px(this.om0xList[i])
var om0xEnd = canvas.x2px(this.om0xList[i+1]) let om0xEnd = canvas.x2px(this.om0xList[i+1])
var baseY = canvas.y2px(this.phasesList[i]) let baseY = canvas.y2px(this.phasesList[i])
var nextY = canvas.y2px(this.phasesList[i+1]) let nextY = canvas.y2px(this.phasesList[i+1])
canvas.drawLine(ctx, om0xBegin, baseY, om0xEnd, baseY) canvas.drawLine(ctx, om0xBegin, baseY, om0xEnd, baseY)
canvas.drawLine(ctx, om0xEnd, baseY, om0xEnd, nextY) canvas.drawLine(ctx, om0xEnd, baseY, om0xEnd, nextY)
} }

View file

@ -16,16 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import { Expression } from "../mathlib.mjs"
import * as P from "../parameters.mjs"
import Latex from "../math/latex.mjs"
.import "common.js" as Common import { API as Common, DrawableObject } from "common.mjs"
.import "../mathlib.js" as MathLib
.import "../parameters.js" as P
.import "../math/latex.js" as Latex
export default class Text extends DrawableObject {
class Text extends Common.DrawableObject {
static type(){return 'Text'} static type(){return 'Text'}
static displayType(){return qsTr('Text')} static displayType(){return qsTr('Text')}
static displayTypeMultiple(){return qsTr('Texts')} static displayTypeMultiple(){return qsTr('Texts')}
@ -45,9 +43,9 @@ class Text extends Common.DrawableObject {
x = 1, y = 0, labelPosition = 'center', text = 'New text', disableLatex = false) { x = 1, y = 0, labelPosition = 'center', text = 'New text', disableLatex = false) {
if(name == null) name = Common.getNewName('t') if(name == null) name = Common.getNewName('t')
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
if(typeof x == 'number' || typeof x == 'string') x = new MathLib.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 MathLib.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

View file

@ -16,17 +16,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import { Expression } from "../mathlib.mjs"
import * as P from "../parameters.mjs"
import Latex from "../math/latex.mjs"
import Objects from "../objects.mjs"
.import "common.js" as Common import { API as Common, DrawableObject } from "common.mjs"
.import "../objects.js" as Objects
.import "../mathlib.js" as MathLib
.import "../parameters.js" as P
.import "../math/latex.js" as Latex
export default class XCursor extends DrawableObject {
class XCursor extends Common.DrawableObject {
static type(){return 'X Cursor'} static type(){return 'X Cursor'}
static displayType(){return qsTr('X Cursor')} static displayType(){return qsTr('X Cursor')}
static displayTypeMultiple(){return qsTr('X Cursors')} static displayTypeMultiple(){return qsTr('X Cursors')}
@ -51,7 +49,7 @@ class XCursor extends Common.DrawableObject {
super(name, visible, color, labelContent) super(name, visible, color, labelContent)
this.approximate = approximate this.approximate = approximate
this.rounding = rounding this.rounding = rounding
if(typeof x == 'number' || typeof x == 'string') x = new MathLib.Expression(x.toString()) if(typeof x == 'number' || typeof x == 'string') x = new Expression(x.toString())
this.x = x this.x = x
this.targetElement = targetElement this.targetElement = targetElement
if(typeof targetElement == "string") { if(typeof targetElement == "string") {
@ -171,7 +169,7 @@ class XCursor extends Common.DrawableObject {
(x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, textSize.height+5)) (x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, textSize.height+5))
// Drawing label at the position of the target element. // Drawing label at the position of the target element.
if(this.targetValuePosition == 'Next to target' && this.targetElement != null) { if(this.targetValuePosition === 'Next to target' && this.targetElement != null) {
let ypos = canvas.y2px(this.targetElement.execute(this.x.execute())) let ypos = canvas.y2px(this.targetElement.execute(this.x.execute()))
this.drawLabel(canvas, ctx, this.labelPosition, xpos, ypos, false, this.drawLabel(canvas, ctx, this.labelPosition, xpos, ypos, false,
this.getTargetValueLatexLabel.bind(this), this.getTargetValueLabel.bind(this), this.getTargetValueLatexLabel.bind(this), this.getTargetValueLabel.bind(this),

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/>.
*/ */
class Expression { export class Expression {
constructor(...variables) { constructor(...variables) {
this.type = 'Expression' this.type = 'Expression'
this.variables = variables this.variables = variables
@ -27,7 +27,7 @@ class Expression {
} }
} }
class Enum { export class Enum {
constructor(...values) { constructor(...values) {
this.type = 'Enum' this.type = 'Enum'
this.values = values this.values = values
@ -39,7 +39,7 @@ class Enum {
} }
} }
class ObjectType { export class ObjectType {
constructor(objType) { constructor(objType) {
this.type = 'ObjectType' this.type = 'ObjectType'
this.objType = objType this.objType = objType
@ -50,7 +50,7 @@ class ObjectType {
} }
} }
class List { export class List {
constructor(type, format = /^.+$/, label = '', forbidAdding = false) { constructor(type, format = /^.+$/, label = '', forbidAdding = false) {
// type can be string, int and double. // type can be string, int and double.
this.type = 'List' this.type = 'List'
@ -65,7 +65,7 @@ class List {
} }
} }
class Dictionary { export class Dictionary {
constructor(type, keyType = 'string', format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = '', postKeyLabel = ': ', forbidAdding = false) { constructor(type, keyType = 'string', format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = '', postKeyLabel = ': ', forbidAdding = false) {
// type & keyType can be string, int and double. // type & keyType can be string, int and double.
this.type = 'Dict' this.type = 'Dict'

View file

@ -2,4 +2,4 @@
Here lies the potential new, abandoned, cleaner implementation of the parsing system that was supposed to replace expr-eval.js, but never came to be because it's unfinished. If somebody has the will to finish this, you're welcome to try, as I won't. Here lies the potential new, abandoned, cleaner implementation of the parsing system that was supposed to replace expr-eval.js, but never came to be because it's unfinished. If somebody has the will to finish this, you're welcome to try, as I won't.
Currently, the tokenizer is complete in use to provide tokens for the syntax highlighting. Currently, the tokenizer is complete in use to provide tokens for the syntax highlighting, and the reference to provide usage.

View file

@ -16,9 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library
class InputExpression { export default class InputExpression {
constructor(expression) { constructor(expression) {
this.position = 0; this.position = 0;
this.input = expression; this.input = expression;
@ -33,7 +32,7 @@ class InputExpression {
} }
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);

View file

@ -16,19 +16,17 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import * as Reference from "reference.mjs"
import * as T from "./tokenizer.mjs"
import InputExpression from "common.mjs"
.import "reference.js" as Reference export const Input = InputExpression
.import "tokenizer.js" as TK export const TokenType = T.TokenType
.import "common.js" as Common export const Token = T.Token
export const Tokenizer = T.ExpressionTokenizer
var Input = Common.InputExpression export const FUNCTIONS_LIST = Reference.FUNCTIONS_LIST
var TokenType = TK.TokenType export const FUNCTIONS = Reference.FUNCTIONS
var Token = TK.Token export const FUNCTIONS_USAGE = Reference.FUNCTIONS_USAGE
var Tokenizer = TK.ExpressionTokenizer export const CONSTANTS_LIST = Reference.CONSTANTS_LIST
export const CONSTANTS = Reference.CONSTANTS
var FUNCTIONS_LIST = Reference.FUNCTIONS_LIST
var FUNCTIONS = Reference.FUNCTIONS
var FUNCTIONS_USAGE = Reference.FUNCTIONS_USAGE
var CONSTANTS_LIST = Reference.CONSTANTS_LIST
var CONSTANTS = Reference.CONSTANTS

View file

@ -17,10 +17,7 @@
*/ */
// Contains polyfill math functions used for reference. // Contains polyfill math functions used for reference.
export function factorial(x) {
.pragma library
function factorial(x) {
if (x < 0) // Integrating by less than 0 if (x < 0) // Integrating by less than 0
if(isFinite(n)) if(isFinite(n))
return Infinity return Infinity
@ -43,7 +40,7 @@ let GAMMA_P = [
0.36899182659531622704e-5 0.36899182659531622704e-5
] ]
function gamma(n) { export function gamma(n) {
if(n <= 0) // Integrating by less than 0 if(n <= 0) // Integrating by less than 0
if(isFinite(n)) if(isFinite(n))
return Infinity return Infinity
@ -91,7 +88,7 @@ function gamma(n) {
return Math.sqrt(2 * Math.PI) * Math.pow(t, n + 0.5) * Math.exp(-t) * x return Math.sqrt(2 * Math.PI) * Math.pow(t, n + 0.5) * Math.exp(-t) * x
} }
function arrayMap(f, arr) { export function arrayMap(f, arr) {
if (typeof f != 'function') if (typeof f != 'function')
throw new EvalError(qsTranslate('error', 'First argument to map is not a function.')) throw new EvalError(qsTranslate('error', 'First argument to map is not a function.'))
if (!Array.isArray(arr)) if (!Array.isArray(arr))
@ -99,7 +96,7 @@ function arrayMap(f, arr) {
return arr.map(f) return arr.map(f)
} }
function arrayFold(f, init, arr) { export function arrayFold(f, init, arr) {
if (typeof f != 'function') if (typeof f != 'function')
throw new EvalError(qsTranslate('error', 'First argument to fold is not a function.')) throw new EvalError(qsTranslate('error', 'First argument to fold is not a function.'))
if (!Array.isArray(arr)) if (!Array.isArray(arr))
@ -107,7 +104,7 @@ function arrayFold(f, init, arr) {
return arr.reduce(f, init) return arr.reduce(f, init)
} }
function arrayFilter(f, arr) { export function arrayFilter(f, arr) {
if (typeof f != 'function') if (typeof f != 'function')
throw new EvalError(qsTranslate('error', 'First argument to filter is not a function.')) throw new EvalError(qsTranslate('error', 'First argument to filter is not a function.'))
if (!Array.isArray(arr)) if (!Array.isArray(arr))
@ -115,21 +112,13 @@ function arrayFilter(f, arr) {
return arr.filter(f) return arr.filter(f)
} }
function arrayFilter(f, arr) { export function arrayJoin(sep, arr) {
if (typeof f != 'function')
throw new EvalError(qsTranslate('error', 'First argument to filter is not a function.'))
if (!Array.isArray(arr))
throw new EvalError(qsTranslate('error', 'Second argument to filter is not an array.'))
return arr.filter(f)
}
function arrayJoin(sep, arr) {
if (!Array.isArray(arr)) if (!Array.isArray(arr))
throw new Error(qsTranslate('error', 'Second argument to join is not an array.')) throw new Error(qsTranslate('error', 'Second argument to join is not an array.'))
return arr.join(sep) return arr.join(sep)
} }
function indexOf(target, s) { export function indexOf(target, s) {
if (!(Array.isArray(s) || typeof s === 'string')) if (!(Array.isArray(s) || typeof s === 'string'))
throw new Error(qsTranslate('error', 'Second argument to indexOf is not a string or array.')) throw new Error(qsTranslate('error', 'Second argument to indexOf is not a string or array.'))
return s.indexOf(target) return s.indexOf(target)

View file

@ -16,12 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library import * as Polyfill from "polyfill.mjs"
.import "polyfill.js" as Polyfill export const CONSTANTS = {
const CONSTANTS = {
"π": Math.PI, "π": Math.PI,
"pi": Math.PI, "pi": Math.PI,
"inf": Infinity, "inf": Infinity,
@ -29,9 +26,9 @@ const CONSTANTS = {
"∞": Infinity, "∞": Infinity,
"e": Math.E "e": Math.E
}; };
const CONSTANTS_LIST = Object.keys(CONSTANTS); export const CONSTANTS_LIST = Object.keys(CONSTANTS);
const FUNCTIONS = { export const FUNCTIONS = {
// The functions commented are the one either not implemented // The functions commented are the one either not implemented
// in the parser, or not to be used for autocompletion. // in the parser, or not to be used for autocompletion.
// Unary operators // Unary operators
@ -94,9 +91,9 @@ const FUNCTIONS = {
'integral': () => 0, // TODO: Implement 'integral': () => 0, // TODO: Implement
'derivative': () => 0, 'derivative': () => 0,
} }
const FUNCTIONS_LIST = Object.keys(FUNCTIONS); export const FUNCTIONS_LIST = Object.keys(FUNCTIONS);
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
@ -107,7 +104,7 @@ class P {
toString() { toString() {
let base_string = this.type let base_string = this.type
if(this.name != '') if(this.name !== '')
base_string = `${this.name}: ${base_string}` base_string = `${this.name}: ${base_string}`
if(this.multipleAllowed) if(this.multipleAllowed)
base_string += '...' base_string += '...'
@ -119,12 +116,12 @@ class P {
} }
} }
let string = new P('string') export let string = new P('string')
let bool = new P('bool') export let bool = new P('bool')
let number = new P('number') export let number = new P('number')
let array = new P('array') export let array = new P('array')
const FUNCTIONS_USAGE = { export const FUNCTIONS_USAGE = {
'length': [string], 'length': [string],
'not': [bool], 'not': [bool],
// Math functions // Math functions

View file

@ -16,18 +16,17 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
.pragma library
.import "reference.js" as Reference import * as Reference from "reference.mjs"
const WHITESPACES = " \t\n\r" const WHITESPACES = " \t\n\r"
const STRING_LIMITORS = '"\'`'; const STRING_LIMITERS = '"\'`';
const OPERATORS = "+-*/^%?:=!><"; const OPERATORS = "+-*/^%?:=!><";
const PUNCTUTATION = "()[]{},."; const PUNCTUATION = "()[]{},.";
const NUMBER_CHARS = "0123456789" const NUMBER_CHARS = "0123456789"
const IDENTIFIER_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_₀₁₂₃₄₅₆₇₈₉αβγδεζηθκλμξρςστφχψωₐₑₒₓₔₕₖₗₘₙₚₛₜ" const IDENTIFIER_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_₀₁₂₃₄₅₆₇₈₉αβγδεζηθκλμξρςστφχψωₐₑₒₓₔₕₖₗₘₙₚₛₜ"
var TokenType = { export const TokenType = {
// Expression type // Expression type
"WHITESPACE": "WHITESPACE", "WHITESPACE": "WHITESPACE",
"VARIABLE": "VARIABLE", "VARIABLE": "VARIABLE",
@ -40,7 +39,7 @@ var TokenType = {
"UNKNOWN": "UNKNOWN" "UNKNOWN": "UNKNOWN"
} }
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;
@ -48,7 +47,13 @@ class Token {
} }
} }
class ExpressionTokenizer { export class ExpressionTokenizer {
/**
*
* @param {InputExpression} input
* @param {boolean} tokenizeWhitespaces
* @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;
@ -71,12 +76,12 @@ class ExpressionTokenizer {
readString() { readString() {
let delimitation = this.input.peek(); let delimitation = this.input.peek();
if(STRING_LIMITORS.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_LIMITORS.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();
} }
@ -92,8 +97,8 @@ class ExpressionTokenizer {
readNumber() { readNumber() {
let included = ""; let included = "";
let hasDot = false; let hasDot = false;
while(!this.input.atEnd() && (NUMBER_CHARS.includes(this.input.peek()) || this.input.peek() == '.')) { while(!this.input.atEnd() && (NUMBER_CHARS.includes(this.input.peek()) || this.input.peek() === '.')) {
if(this.input.peek() == ".") { if(this.input.peek() === ".") {
if(hasDot) this.input.raise("Unexpected '.'. Expected digit") if(hasDot) this.input.raise("Unexpected '.'. Expected digit")
hasDot = true; hasDot = true;
} }
@ -130,14 +135,14 @@ class ExpressionTokenizer {
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_LIMITORS.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(PUNCTUTATION.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.throw("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);
} }
@ -163,7 +168,7 @@ class ExpressionTokenizer {
skip(type) { skip(type) {
let next = this.next(); let next = this.next();
if(next.type != type) if(next.type !== type)
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

@ -0,0 +1,45 @@
/**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Base class for global APIs in runtime.
*/
export class RuntimeAPI {
/**
*
* @param {string} name - Name of the API
* @param {(RuntimeAPI|undefined)[]} requires - List of APIs required to initialize this one.
*/
constructor(name, requires = []) {
console.log(`Loading module ${name}...`)
this.__check_requirements(requires, name)
}
/**
* Checks if all requirements are defined.
* @param {(RuntimeAPI|undefined)[]} requires
* @param {string} name
*/
__check_requirements(requires, name) {
for(let requirement of requires) {
if(requirement === undefined)
throw new Error(`Requirement ${requires.indexOf(requirement)} of ${name} has not been initialized.`)
}
}
}

View file

@ -1,24 +1,22 @@
/** /**
* 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/>.
*/ */
.pragma library const powerpos = {
var powerpos = {
"-": "⁻", "-": "⁻",
"+": "⁺", "+": "⁺",
"=": "⁼", "=": "⁼",
@ -62,12 +60,13 @@ var powerpos = {
"z": "ᶻ" "z": "ᶻ"
} }
var exponents = [ const exponents = [
"⁰","¹","²","³","⁴","⁵","⁶","⁷","⁸","⁹" "⁰","¹","²","³","⁴","⁵","⁶","⁷","⁸","⁹"
] ]
var exponentReg = new RegExp('(['+exponents.join('')+']+)', 'g')
var indicepos = { const exponentReg = new RegExp('(['+exponents.join('')+']+)', 'g')
const indicepos = {
"-": "₋", "-": "₋",
"+": "₊", "+": "₊",
"=": "₌", "=": "₌",
@ -103,10 +102,10 @@ var indicepos = {
"x": "ₓ", "x": "ₓ",
} }
// Put a text in sup position // Put a text in sup position
function textsup(text) { export function textsup(text) {
var ret = "" let ret = ""
text = text.toString() text = text.toString()
for (var i = 0; i < text.length; i++) { for (let i = 0; i < text.length; i++) {
if(Object.keys(powerpos).indexOf(text[i]) >= 0) { if(Object.keys(powerpos).indexOf(text[i]) >= 0) {
ret += powerpos[text[i]] ret += powerpos[text[i]]
} else { } else {
@ -117,10 +116,10 @@ function textsup(text) {
} }
// Put a text in sub position // Put a text in sub position
function textsub(text) { export function textsub(text) {
var ret = "" let ret = ""
text = text.toString() text = text.toString()
for (var i = 0; i < text.length; i++) { for (let i = 0; i < text.length; i++) {
if(Object.keys(indicepos).indexOf(text[i]) >= 0) { if(Object.keys(indicepos).indexOf(text[i]) >= 0) {
ret += indicepos[text[i]] ret += indicepos[text[i]]
} else { } else {
@ -130,8 +129,13 @@ function textsub(text) {
return ret return ret
} }
function simplifyExpression(str) { /**
var replacements = [ * Simplifies (mathematically) a mathematical expression.
* @param {string} str - Expression to parse
* @returns {string}
*/
export function simplifyExpression(str) {
let replacements = [
// Operations not done by parser. // Operations not done by parser.
// [// Decomposition way 2 // [// Decomposition way 2
// /(^|[+-] |\()([-.\d\w]+) ([*/]) \((([-.\d\w] [*/] )?[-\d\w.]+) ([+\-]) (([-.\d\w] [*/] )?[\d\w.+]+)\)($| [+-]|\))/g, // /(^|[+-] |\()([-.\d\w]+) ([*/]) \((([-.\d\w] [*/] )?[-\d\w.]+) ([+\-]) (([-.\d\w] [*/] )?[\d\w.+]+)\)($| [+-]|\))/g,
@ -149,16 +153,16 @@ function simplifyExpression(str) {
// n1 & n3 are multiplied, opeM is the main operation (- or +). // n1 & n3 are multiplied, opeM is the main operation (- or +).
// Putting all n in form of number // Putting all n in form of number
//n2 = n2 == undefined ? 1 : parseFloat(n) //n2 = n2 == undefined ? 1 : parseFloat(n)
n1 = m1 == undefined ? 1 : eval(m1 + '1') n1 = m1 === undefined ? 1 : eval(m1 + '1')
n2 = m2 == undefined ? 1 : eval('1' + m2) n2 = m2 === undefined ? 1 : eval('1' + m2)
n3 = m3 == undefined ? 1 : eval(m3 + '1') n3 = m3 === undefined ? 1 : eval(m3 + '1')
n4 = m4 == undefined ? 1 : eval('1' + m4) n4 = m4 === undefined ? 1 : eval('1' + m4)
//var [n1, n2, n3, n4] = [n1, n2, n3, n4].map(n => n == undefined ? 1 : parseFloat(n)) //let [n1, n2, n3, n4] = [n1, n2, n3, n4].map(n => n == undefined ? 1 : parseFloat(n))
// Falling back to * in case it does not exist (the corresponding n would be 1) // Falling back to * in case it does not exist (the corresponding n would be 1)
var [ope2, ope4] = [ope2, ope4].map(ope => ope == '/' ? '/' : '*') [ope2, ope4] = [ope2, ope4].map(ope => ope === '/' ? '/' : '*')
var coeff1 = n1*n2 let coeff1 = n1*n2
var coeff2 = n3*n4 let coeff2 = n3*n4
var coefficient = coeff1+coeff2-(opeM == '-' ? 2*coeff2 : 0) let coefficient = coeff1+coeff2-(opeM === '-' ? 2*coeff2 : 0)
return `${coefficient} * π` return `${coefficient} * π`
} }
@ -172,14 +176,14 @@ function simplifyExpression(str) {
function(match, b4, middle, after) {return `${b4}${middle}${after}`} function(match, b4, middle, after) {return `${b4}${middle}${after}`}
], ],
[ // Removing parenthesis when content is only multiplied. [ // Removing parenthesis when content is only multiplied.
/(^|[*\/-+] |\()\(([^)(+-]+)\)($| [*\/]|\))/g, /(^|[*\/+-] |\()\(([^)(+-]+)\)($| [*\/]|\))/g,
function(match, b4, middle, after) {return `${b4}${middle}${after}`} function(match, b4, middle, after) {return `${b4}${middle}${after}`}
], ],
[// Simplification additions/substractions. [// Simplification additions/subtractions.
/(^|[^*\/] |\()([-.\d]+) (\+|\-) (\([^)(]+\)|[^)(]+) (\+|\-) ([-.\d]+)($| [^*\/]|\))/g, /(^|[^*\/] |\()([-.\d]+) [+-] (\([^)(]+\)|[^)(]+) [+-] ([-.\d]+)($| [^*\/]|\))/g,
function(match, b4, n1, op1, middle, op2, n2, after) { function(match, b4, n1, op1, middle, op2, n2, after) {
var total let total
if(op2 == '+') { if(op2 === '+') {
total = parseFloat(n1) + parseFloat(n2) total = parseFloat(n1) + parseFloat(n2)
} else { } else {
total = parseFloat(n1) - parseFloat(n2) total = parseFloat(n1) - parseFloat(n2)
@ -188,14 +192,14 @@ function simplifyExpression(str) {
} }
], ],
[// Simplification multiplications/divisions. [// Simplification multiplications/divisions.
/([-.\d]+) (\*|\/) (\([^)(]+\)|[^)(+-]+) (\*|\/) ([-.\d]+)/g, /([-.\d]+) [*\/] (\([^)(]+\)|[^)(+-]+) [*\/] ([-.\d]+)/g,
function(match, n1, op1, middle, op2, n2) { function(match, n1, op1, middle, op2, n2) {
if(parseInt(n1) == n1 && parseInt(n2) == n2 && op2 == '/' && if(parseInt(n1) === n1 && parseInt(n2) === n2 && op2 === '/' &&
(parseInt(n1) / parseInt(n2)) % 1 != 0) { (parseInt(n1) / parseInt(n2)) % 1 !== 0) {
// Non int result for int division. // Non int result for int division.
return `(${n1} / ${n2}) ${op1} ${middle}` return `(${n1} / ${n2}) ${op1} ${middle}`
} else { } else {
if(op2 == '*') { if(op2 === '*') {
return `${parseFloat(n1) * parseFloat(n2)} ${op1} ${middle}` return `${parseFloat(n1) * parseFloat(n2)} ${op1} ${middle}`
} else { } else {
return `${parseFloat(n1) / parseFloat(n2)} ${op1} ${middle}` return `${parseFloat(n1) / parseFloat(n2)} ${op1} ${middle}`
@ -206,7 +210,7 @@ function simplifyExpression(str) {
[// Starting & ending parenthesis if not needed. [// Starting & ending parenthesis if not needed.
/^\s*\((.*)\)\s*$/g, /^\s*\((.*)\)\s*$/g,
function(match, middle) { function(match, middle) {
var str = middle let str = middle
// Replace all groups // Replace all groups
while(/\([^)(]+\)/g.test(str)) while(/\([^)(]+\)/g.test(str))
str = str.replace(/\([^)(]+\)/g, '') str = str.replace(/\([^)(]+\)/g, '')
@ -225,19 +229,19 @@ function simplifyExpression(str) {
// [/(\s|^|\()0(\.0+)? \* ([^)(+-]+)/g, '$10'], // [/(\s|^|\()0(\.0+)? \* ([^)(+-]+)/g, '$10'],
// [/(\([^)(]\)) \* 0(\.0+)?(\s|$|\))/g, '0$3'], // [/(\([^)(]\)) \* 0(\.0+)?(\s|$|\))/g, '0$3'],
// [/([^)(+-]) \* 0(\.0+)?(\s|$|\))/g, '0$3'], // [/([^)(+-]) \* 0(\.0+)?(\s|$|\))/g, '0$3'],
// [/(\s|^|\()1(\.0+)? (\*|\/) /g, '$1'], // [/(\s|^|\()1(\.0+)? [\*\/] /g, '$1'],
// [/(\s|^|\()0(\.0+)? (\+|\-) /g, '$1'], // [/(\s|^|\()0(\.0+)? (\+|\-) /g, '$1'],
// [/ (\*|\/) 1(\.0+)?(\s|$|\))/g, '$3'], // [/ [\*\/] 1(\.0+)?(\s|$|\))/g, '$3'],
// [/ (\+|\-) 0(\.0+)?(\s|$|\))/g, '$3'], // [/ (\+|\-) 0(\.0+)?(\s|$|\))/g, '$3'],
// [/(^| |\() /g, '$1'], // [/(^| |\() /g, '$1'],
// [/ ($|\))/g, '$1'], // [/ ($|\))/g, '$1'],
] ]
// Replacements // Replacements
var found let found
do { do {
found = false found = false
for(var replacement of replacements) for(let replacement of replacements)
while(replacement[0].test(str)) { while(replacement[0].test(str)) {
found = true found = true
str = str.replace(replacement[0], replacement[1]) str = str.replace(replacement[0], replacement[1])
@ -247,9 +251,15 @@ function simplifyExpression(str) {
} }
function makeExpressionReadable(str) { /**
var replacements = [ * Transforms a mathematical expression to make it readable by humans.
// variables * NOTE: Will break parsing of expression.
* @param {string} str - Expression to parse.
* @returns {string}
*/
export function makeExpressionReadable(str) {
let replacements = [
// letiables
[/pi/g, 'π'], [/pi/g, 'π'],
[/Infinity/g, '∞'], [/Infinity/g, '∞'],
[/inf/g, '∞'], [/inf/g, '∞'],
@ -264,28 +274,35 @@ function makeExpressionReadable(str) {
[/(\d|\))×/g, '$1'], [/(\d|\))×/g, '$1'],
//[/×(\d|\()/g, '$1'], //[/×(\d|\()/g, '$1'],
[/([^a-z])\(([^)(+.\/-]+)\)/g, "$1×$2"], [/([^a-z])\(([^)(+.\/-]+)\)/g, "$1×$2"],
[/integral\((.+),\s?(.+),\s?("|')(.+)("|'),\s?("|')(.+)("|')\)/g, function(match, a, b, p1, body, p2, p3, by, p4) { [/integral\((.+),\s?(.+),\s?["'](.+)["'],\s?["'](.+)["']\)/g, function(match, a, b, p1, body, p2, p3, by, p4) {
if(a.length < b.length) { if(a.length < b.length) {
return `${textsub(a)}${textsup(b)} ${body} d${by}` return `${textsub(a)}${textsup(b)} ${body} d${by}`
} else { } else {
return `${textsup(b)}${textsub(a)} ${body} d${by}` return `${textsup(b)}${textsub(a)} ${body} d${by}`
} }
}], }],
[/derivative\(?("|')(.+)("|'), ?("|')(.+)("|'), ?(.+)\)?/g, function(match, p1, body, p2, p3, by, p4, x) { [/derivative\(?["'](.+)["'], ?["'](.+)["'], ?(.+)\)?/g, function(match, p1, body, p2, p3, by, p4, x) {
return `d(${body.replace(new RegExp(by, 'g'), 'x')})/dx` return `d(${body.replace(new RegExp(by, 'g'), 'x')})/dx`
}] }]
] ]
// str = simplifyExpression(str) // str = simplifyExpression(str)
// Replacements // Replacements
for(var replacement of replacements) for(let replacement of replacements)
while(replacement[0].test(str)) while(replacement[0].test(str))
str = str.replace(replacement[0], replacement[1]) str = str.replace(replacement[0], replacement[1])
return str return str
} }
function parseName(str, removeUnallowed = true) { /**
var replacements = [ * Parses a variable name to make it readable by humans.
*
* @param {string} str - Variable name to parse
* @param {boolean} removeUnallowed - Remove domain symbols disallowed in name.
* @returns {string} - The parsed name
*/
export function parseName(str, removeUnallowed = true) {
let replacements = [
// Greek letters // Greek letters
[/([^a-z]|^)al(pha)?([^a-z]|$)/g, '$1α$3'], [/([^a-z]|^)al(pha)?([^a-z]|$)/g, '$1α$3'],
[/([^a-z]|^)be(ta)?([^a-z]|$)/g, '$1β$3'], [/([^a-z]|^)be(ta)?([^a-z]|$)/g, '$1β$3'],
@ -330,7 +347,7 @@ function parseName(str, removeUnallowed = true) {
] ]
if(!removeUnallowed) replacements.pop() if(!removeUnallowed) replacements.pop()
// Replacements // Replacements
for(var replacement of replacements) for(let replacement of replacements)
str = str.replace(replacement[0], replacement[1]) str = str.replace(replacement[0], replacement[1])
return str return str
} }
@ -339,21 +356,36 @@ String.prototype.toLatinUppercase = function() {
return this.replace(/[a-z]/g, function(match){return match.toUpperCase()}) return this.replace(/[a-z]/g, function(match){return match.toUpperCase()})
} }
function camelCase2readable(label) { /**
var parsed = parseName(label, false) * Transforms camel case strings to a space separated one.
*
* @param {string} label - Camel case to parse
* @returns {string} Parsed label.
*/
export function camelCase2readable(label) {
let parsed = parseName(label, false)
return parsed.charAt(0).toLatinUppercase() + parsed.slice(1).replace(/([A-Z])/g," $1") return parsed.charAt(0).toLatinUppercase() + parsed.slice(1).replace(/([A-Z])/g," $1")
} }
function getRandomColor() { /**
var clrs = '0123456789ABCDEF'; * Creates a randomized color string.
var color = '#'; * @returns {string}
for(var i = 0; i < 6; i++) { */
color += clrs[Math.floor(Math.random() * (16-5*(i%2==0)))]; export function getRandomColor() {
let clrs = '0123456789ABCDEF';
let color = '#';
for(let i = 0; i < 6; i++) {
color += clrs[Math.floor(Math.random() * (16-5*(i%2===0)))];
} }
return color; return color;
} }
function escapeHTML(str) { /**
* Escapes text to html entities.
* @param {string} str
* @returns {string}
*/
export function escapeHTML(str) {
return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') ; return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') ;
} }
@ -364,6 +396,6 @@ function escapeHTML(str) {
* @param {string} expression - The expression to replace in. * @param {string} expression - The expression to replace in.
* @return {string} The parsed expression * @return {string} The parsed expression
*/ */
function exponentsToExpression(expression) { export function exponentsToExpression(expression) {
return expression.replace(exponentReg, (m, exp) => '^' + exp.split('').map((x) => exponents.indexOf(x)).join('')) return expression.replace(exponentReg, (m, exp) => '^' + exp.split('').map((x) => exponents.indexOf(x)).join(''))
} }