Converting as many JS libraries to ECMAScript modules.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
a6fcf6da19
commit
08fea34366
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
|
@ -1,2 +1,2 @@
|
|||
#!/bin/bash
|
||||
lupdate -extensions js,qs,qml,py -recursive .. -ts lp_*.ts
|
||||
lupdate -extensions mjs,js,qs,qml,py -recursive .. -ts lp_*.ts
|
||||
|
|
|
@ -112,6 +112,7 @@ def run():
|
|||
global tmpfile
|
||||
helper = Helper(pwd, tmpfile)
|
||||
latex = Latex(tempdir)
|
||||
engine.globalObject().setProperty('Runtime', engine.newObject())
|
||||
engine.rootContext().setContextProperty("Helper", helper)
|
||||
engine.rootContext().setContextProperty("Latex", latex)
|
||||
engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv)
|
||||
|
|
|
@ -20,9 +20,7 @@ import QtQuick
|
|||
import Qt.labs.platform as Native
|
||||
//import QtQuick.Controls 2.15
|
||||
import eu.ad5001.MixedMenu 1.1
|
||||
import "js/objects.js" as Objects
|
||||
import "js/historylib.js" as HistoryLib
|
||||
import "js/math/latex.js" as LatexJS
|
||||
import "js/historylib.mjs" as HistoryLib
|
||||
|
||||
|
||||
/*!
|
||||
|
@ -105,17 +103,17 @@ MenuBar {
|
|||
title: qsTr("&Create")
|
||||
// Services repeater
|
||||
Repeater {
|
||||
model: Object.keys(Objects.types)
|
||||
model: Object.keys(Runtime.Objects.types)
|
||||
|
||||
MenuItem {
|
||||
text: Objects.types[modelData].displayType()
|
||||
visible: Objects.types[modelData].createable()
|
||||
text: Runtime.Objects.types[modelData].displayType()
|
||||
visible: Runtime.Objects.types[modelData].createable()
|
||||
height: visible ? implicitHeight : 0
|
||||
icon.name: modelData
|
||||
icon.source: './icons/objects/' + modelData + '.svg'
|
||||
icon.color: sysPalette.buttonText
|
||||
onTriggered: {
|
||||
var newObj = Objects.createNewRegisteredObject(modelData)
|
||||
var newObj = Runtime.Objects.createNewRegisteredObject(modelData)
|
||||
history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export()))
|
||||
objectLists.update()
|
||||
}
|
||||
|
@ -153,7 +151,7 @@ MenuBar {
|
|||
checked: Helper.getSettingBool("enable_latex")
|
||||
onTriggered: {
|
||||
Helper.setSettingBool("enable_latex", checked)
|
||||
LatexJS.enabled = checked
|
||||
Runtime.Latex.enabled = checked
|
||||
drawCanvas.requestPaint()
|
||||
}
|
||||
icon.name: 'Expression'
|
||||
|
|
|
@ -19,9 +19,7 @@
|
|||
import QtQuick
|
||||
import QtQml
|
||||
import QtQuick.Window
|
||||
import "../js/objects.js" as Objects
|
||||
import "../js/historylib.js" as HistoryLib
|
||||
import "../js/history/common.js" as HistoryCommon
|
||||
import "../js/historylib.mjs" as HistoryLib
|
||||
|
||||
/*!
|
||||
\qmltype History
|
||||
|
@ -214,8 +212,8 @@ Item {
|
|||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
HistoryLib.history = historyObj
|
||||
HistoryCommon.themeTextColor = sysPalette.windowText
|
||||
HistoryCommon.imageDepth = Screen.devicePixelRatio
|
||||
Runtime.History.history = historyObj
|
||||
Runtime.History.themeTextColor = sysPalette.windowText
|
||||
Runtime.History.imageDepth = Screen.devicePixelRatio
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
import QtQuick.Controls
|
||||
import QtQuick
|
||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||
import "../js/utils.js" as Utils
|
||||
import "../js/utils.mjs" as Utils
|
||||
|
||||
|
||||
/*!
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
import QtQuick.Controls
|
||||
import QtQuick
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import "../js/utils.js" as Utils
|
||||
import "../js/utils.mjs" as Utils
|
||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||
|
||||
|
||||
|
|
|
@ -18,9 +18,8 @@
|
|||
|
||||
import QtQuick
|
||||
import Qt.labs.platform as Native
|
||||
import "js/objects.js" as Objects
|
||||
import "js/utils.js" as Utils
|
||||
import "js/mathlib.js" as MathLib
|
||||
import "js/utils.mjs" as Utils
|
||||
import "js/mathlib.mjs" as MathLib
|
||||
|
||||
/*!
|
||||
\qmltype LogGraphCanvas
|
||||
|
@ -190,8 +189,8 @@ Canvas {
|
|||
drawAxises(ctx)
|
||||
drawLabels(ctx)
|
||||
ctx.lineWidth = linewidth
|
||||
for(var objType in Objects.currentObjects) {
|
||||
for(var obj of Objects.currentObjects[objType]){
|
||||
for(var objType in Runtime.Objects.currentObjects) {
|
||||
for(var obj of Runtime.Objects.currentObjects[objType]){
|
||||
ctx.strokeStyle = obj.color
|
||||
ctx.fillStyle = obj.color
|
||||
if(obj.visible)
|
||||
|
|
|
@ -21,11 +21,10 @@ import QtQuick.Controls
|
|||
import eu.ad5001.MixedMenu 1.1
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick
|
||||
// Auto loading all objects.
|
||||
import "js/objs/autoload.js" as ALObjects
|
||||
|
||||
import "js/objects.js" as Objects
|
||||
import "js/math/latex.js" as LatexJS
|
||||
// Auto loading all modules.
|
||||
import "js/modules.js" as Modules
|
||||
|
||||
import eu.ad5001.LogarithmPlotter.History 1.0
|
||||
import eu.ad5001.LogarithmPlotter.ObjectLists 1.0
|
||||
import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup
|
||||
|
@ -50,9 +49,9 @@ ApplicationWindow {
|
|||
|
||||
Component.onCompleted: {
|
||||
// LatexJS initialization.
|
||||
LatexJS.enabled = Helper.getSettingBool("enable_latex")
|
||||
LatexJS.Renderer = Latex
|
||||
LatexJS.defaultColor = sysPalette.windowText
|
||||
Runtime.Latex.enabled = Helper.getSettingBool("enable_latex")
|
||||
Runtime.Latex.Renderer = Latex
|
||||
Runtime.Latex.defaultColor = sysPalette.windowText
|
||||
}
|
||||
}
|
||||
SystemPalette { id: sysPaletteIn; colorGroup: SystemPalette.Disabled }
|
||||
|
@ -201,9 +200,9 @@ ApplicationWindow {
|
|||
filename += '.lpf'
|
||||
settings.saveFilename = filename
|
||||
var objs = {}
|
||||
for(var objType in Objects.currentObjects){
|
||||
for(var objType in Runtime.Objects.currentObjects){
|
||||
objs[objType] = []
|
||||
for(var obj of Objects.currentObjects[objType]) {
|
||||
for(var obj of Runtime.Objects.currentObjects[objType]) {
|
||||
objs[objType].push(obj.export())
|
||||
}
|
||||
}
|
||||
|
@ -265,19 +264,19 @@ ApplicationWindow {
|
|||
root.width = data["width"]
|
||||
|
||||
// Importing objects
|
||||
Objects.currentObjects = {}
|
||||
Object.keys(Objects.currentObjectsByName).forEach(key => {
|
||||
delete Objects.currentObjectsByName[key];
|
||||
Runtime.Objects.currentObjects = {}
|
||||
Runtime.Object.keys(Objects.currentObjectsByName).forEach(key => {
|
||||
delete Runtime.Objects.currentObjectsByName[key];
|
||||
// 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.
|
||||
})
|
||||
for(let objType in data['objects']) {
|
||||
if(Object.keys(Objects.types).indexOf(objType) > -1) {
|
||||
Objects.currentObjects[objType] = []
|
||||
if(Object.keys(Runtime.Objects.types).indexOf(objType) > -1) {
|
||||
Runtime.Objects.currentObjects[objType] = []
|
||||
for(let objData of data['objects'][objType]) {
|
||||
let obj = new Objects.types[objType](...objData)
|
||||
Objects.currentObjects[objType].push(obj)
|
||||
Objects.currentObjectsByName[obj.name] = obj
|
||||
let obj = new Runtime.Objects.types[objType](...objData)
|
||||
Runtime.Objects.currentObjects[objType].push(obj)
|
||||
Runtime.Objects.currentObjectsByName[obj.name] = obj
|
||||
}
|
||||
} else {
|
||||
error += qsTr("Unknown object type: %1.").arg(objType) + "\n";
|
||||
|
@ -285,8 +284,8 @@ ApplicationWindow {
|
|||
}
|
||||
|
||||
// Updating object dependencies.
|
||||
for(let objName in Objects.currentObjectsByName)
|
||||
Objects.currentObjectsByName[objName].update()
|
||||
for(let objName in Runtime.Objects.currentObjectsByName)
|
||||
Runtime.Objects.currentObjectsByName[objName].update()
|
||||
|
||||
// Importing history
|
||||
if("history" in data)
|
||||
|
|
|
@ -20,10 +20,9 @@ import QtQuick
|
|||
import QtQuick.Controls
|
||||
import Qt.labs.platform as Native
|
||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||
import "../../js/objects.js" as Objects
|
||||
import "../../js/historylib.js" as HistoryLib
|
||||
import "../../js/utils.js" as Utils
|
||||
import "../../js/mathlib.js" as MathLib
|
||||
import "../../js/historylib.mjs" as HistoryLib
|
||||
import "../../js/utils.mjs" as Utils
|
||||
import "../../js/mathlib.mjs" as MathLib
|
||||
|
||||
/*!
|
||||
\qmltype CustomPropertyList
|
||||
|
@ -188,8 +187,8 @@ Repeater {
|
|||
|
||||
// Base, untranslated version of the model.
|
||||
property var baseModel: selectObjMode ?
|
||||
Objects.getObjectsName(propertyType.objType).concat(
|
||||
isRealObject ? [qsTr("+ Create new %1").arg(Objects.types[propertyType.objType].displayType())] : [])
|
||||
Runtime.Objects.getObjectsName(propertyType.objType).concat(
|
||||
isRealObject ? [qsTr("+ Create new %1").arg(Runtime.Objects.types[propertyType.objType].displayType())] : [])
|
||||
: propertyType.values
|
||||
// Translated version of the model.
|
||||
model: selectObjMode ? baseModel : propertyType.translatedValues
|
||||
|
@ -199,20 +198,20 @@ Repeater {
|
|||
if(selectObjMode) {
|
||||
// This is only done when what we're selecting are Objects.
|
||||
// Setting object property.
|
||||
var selectedObj = Objects.currentObjectsByName[baseModel[newIndex]]
|
||||
var selectedObj = Runtime.Objects.currentObjectsByName[baseModel[newIndex]]
|
||||
if(newIndex != 0) {
|
||||
// Make sure we don't set the object to null.
|
||||
if(selectedObj == null) {
|
||||
// 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()))
|
||||
baseModel = Objects.getObjectsName(propertyType.objType).concat(
|
||||
isRealObject ? [qsTr("+ Create new %1").arg(Objects.types[propertyType.objType].displayType())] :
|
||||
baseModel = Runtime.Objects.getObjectsName(propertyType.objType).concat(
|
||||
isRealObject ? [qsTr("+ Create new %1").arg(Runtime.Objects.types[propertyType.objType].displayType())] :
|
||||
[])
|
||||
currentIndex = baseModel.indexOf(selectedObj.name)
|
||||
}
|
||||
selectedObj.requiredBy.push(Objects.currentObjects[objType][objIndex])
|
||||
//Objects.currentObjects[objType][objIndex].requiredBy = obj[propertyName].filter((obj) => obj.name != obj.name)
|
||||
selectedObj.requiredBy.push(Runtime.Objects.currentObjects[objType][objIndex])
|
||||
//Runtime.Objects.currentObjects[objType][objIndex].requiredBy = obj[propertyName].filter((obj) => obj.name != obj.name)
|
||||
}
|
||||
obj.requiredBy = obj.requiredBy.filter((obj) => obj.name != obj.name)
|
||||
history.addToHistory(new HistoryLib.EditedProperty(
|
||||
|
@ -256,7 +255,7 @@ Repeater {
|
|||
obj.name, objType, propertyName,
|
||||
obj[propertyName], exported
|
||||
))
|
||||
//Objects.currentObjects[objType][objIndex][propertyName] = exported
|
||||
//Runtime.Objects.currentObjects[objType][objIndex][propertyName] = exported
|
||||
obj[propertyName] = exported
|
||||
root.changed()
|
||||
}
|
||||
|
|
|
@ -22,11 +22,9 @@ import QtQuick.Dialogs as D
|
|||
import Qt.labs.platform as Native
|
||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||
import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup
|
||||
import "../../js/objects.js" as Objects
|
||||
import "../../js/objs/common.js" as ObjectsCommons
|
||||
import "../../js/historylib.js" as HistoryLib
|
||||
import "../../js/utils.js" as Utils
|
||||
import "../../js/mathlib.js" as MathLib
|
||||
import "../../js/historylib.mjs" as HistoryLib
|
||||
import "../../js/utils.mjs" as Utils
|
||||
import "../../js/mathlib.mjs" as MathLib
|
||||
|
||||
/*!
|
||||
\qmltype Dialog
|
||||
|
@ -54,7 +52,7 @@ Popup.BaseDialog {
|
|||
\qmlproperty var EditorDialog::obj
|
||||
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
|
||||
Reference to the global PositionPicker QML object.
|
||||
|
@ -87,7 +85,7 @@ Popup.BaseDialog {
|
|||
Label {
|
||||
id: dlgTitle
|
||||
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
|
||||
color: sysPalette.windowText
|
||||
}
|
||||
|
@ -113,14 +111,14 @@ Popup.BaseDialog {
|
|||
onChanged: function(newValue) {
|
||||
let newName = Utils.parseName(newValue)
|
||||
if(newName != '' && objEditor.obj.name != newName) {
|
||||
if(newName in Objects.currentObjectsByName) {
|
||||
if(newName in Runtime.Objects.currentObjectsByName) {
|
||||
invalidNameDialog.showDialog(newName)
|
||||
} else {
|
||||
history.addToHistory(new HistoryLib.NameChanged(
|
||||
objEditor.obj.name, objEditor.objType, newName
|
||||
))
|
||||
Objects.renameObject(obj.name, newName)
|
||||
objEditor.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex]
|
||||
Runtime.Objects.renameObject(obj.name, newName)
|
||||
objEditor.obj = Runtime.Objects.currentObjects[objEditor.objType][objEditor.objIndex]
|
||||
objectListList.update()
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +163,7 @@ Popup.BaseDialog {
|
|||
*/
|
||||
function open() {
|
||||
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.
|
||||
objEditor.show()
|
||||
}
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import "../js/objects.js" as Objects
|
||||
import "../js/historylib.js" as HistoryLib
|
||||
import "../js/historylib.mjs" as HistoryLib
|
||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||
|
||||
|
||||
|
@ -44,7 +43,7 @@ Column {
|
|||
// Open editor
|
||||
objectEditor.obj = obj
|
||||
objectEditor.objType = obj.type
|
||||
objectEditor.objIndex = Objects.currentObjects[obj.type].indexOf(obj)
|
||||
objectEditor.objIndex = Runtime.Objects.currentObjects[obj.type].indexOf(obj)
|
||||
objectEditor.open()
|
||||
// Disconnect potential link
|
||||
posPicker.picked.disconnect(openEditorDialog)
|
||||
|
@ -61,12 +60,12 @@ Column {
|
|||
width: parent.width
|
||||
columns: 3
|
||||
Repeater {
|
||||
model: Object.keys(Objects.types)
|
||||
model: Object.keys(Runtime.Objects.types)
|
||||
|
||||
Button {
|
||||
id: createBtn
|
||||
width: 96
|
||||
visible: Objects.types[modelData].createable()
|
||||
visible: Runtime.Objects.types[modelData].createable()
|
||||
height: visible ? width*0.8 : 0
|
||||
// The KDE SDK is kinda buggy, so it respects neither specified color nor display propreties.
|
||||
//display: AbstractButton.TextUnderIcon
|
||||
|
@ -94,7 +93,7 @@ Column {
|
|||
anchors.rightMargin: 4
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 14
|
||||
text: Objects.types[modelData].displayType()
|
||||
text: Runtime.Objects.types[modelData].displayType()
|
||||
wrapMode: Text.WordWrap
|
||||
clip: true
|
||||
}
|
||||
|
@ -104,7 +103,7 @@ Column {
|
|||
ToolTip.text: label.text
|
||||
|
||||
onClicked: {
|
||||
let newObj = Objects.createNewRegisteredObject(modelData)
|
||||
let newObj = Runtime.Objects.createNewRegisteredObject(modelData)
|
||||
history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export()))
|
||||
objectLists.update()
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import QtQuick
|
|||
import QtQuick.Controls
|
||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||
import eu.ad5001.LogarithmPlotter.ObjectLists.Editor 1.0 as Editor
|
||||
import "../js/objects.js" as Objects
|
||||
|
||||
/*!
|
||||
\qmltype ObjectLists
|
||||
|
@ -47,7 +46,7 @@ ScrollView {
|
|||
|
||||
ListView {
|
||||
id: objectsListView
|
||||
model: Object.keys(Objects.types)
|
||||
model: Object.keys(Runtime.Objects.types)
|
||||
//width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0)
|
||||
implicitHeight: contentItem.childrenRect.height + footerItem.height + 10
|
||||
|
||||
|
@ -55,7 +54,7 @@ ScrollView {
|
|||
id: objTypeList
|
||||
property string objType: objectsListView.model[index]
|
||||
property var editingRows: []
|
||||
model: Objects.currentObjects[objType]
|
||||
model: Runtime.Objects.currentObjects[objType]
|
||||
width: objectsListView.width
|
||||
implicitHeight: contentItem.childrenRect.height
|
||||
visible: model != undefined && model.length > 0
|
||||
|
@ -70,21 +69,23 @@ ScrollView {
|
|||
|
||||
CheckBox {
|
||||
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: {
|
||||
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
|
||||
objectListList.changed()
|
||||
}
|
||||
|
||||
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 {
|
||||
id: typeHeaderText
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -92,11 +93,11 @@ ScrollView {
|
|||
delegate: ObjectRow {
|
||||
id: controlRow
|
||||
width: objTypeList.width
|
||||
obj: Objects.currentObjects[objType][index]
|
||||
obj: Runtime.Objects.currentObjects[objType][index]
|
||||
posPicker: positionPicker
|
||||
|
||||
onChanged: {
|
||||
obj = Objects.currentObjects[objType][index]
|
||||
obj = Runtime.Objects.currentObjects[objType][index]
|
||||
objectListList.update()
|
||||
}
|
||||
|
||||
|
@ -128,7 +129,7 @@ ScrollView {
|
|||
function update() {
|
||||
objectListList.changed()
|
||||
for(var objType in objectListList.listViews) {
|
||||
objectListList.listViews[objType].model = Objects.currentObjects[objType]
|
||||
objectListList.listViews[objType].model = Runtime.Objects.currentObjects[objType]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,7 @@ import QtQuick.Dialogs
|
|||
import QtQuick.Controls
|
||||
import QtQuick.Window
|
||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||
import "../js/objects.js" as Objects
|
||||
import "../js/historylib.js" as HistoryLib
|
||||
import "../js/math/latex.js" as LatexJS
|
||||
import "../js/historylib.mjs" as HistoryLib
|
||||
|
||||
|
||||
/*!
|
||||
|
@ -91,16 +89,16 @@ Item {
|
|||
id: objDescription
|
||||
anchors.left: objVisibilityCheckBox.right
|
||||
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
|
||||
text: LatexJS.enabled ? "" : obj.getReadableString()
|
||||
text: Runtime.Latex.enabled ? "" : obj.getReadableString()
|
||||
font.pixelSize: 14
|
||||
|
||||
Image {
|
||||
id: latexDescription
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
visible: LatexJS.enabled
|
||||
visible: Runtime.Latex.enabled
|
||||
property double depth: Screen.devicePixelRatio
|
||||
property var ltxInfo: visible ? Latex.render(obj.getLatexString(), depth*(parent.font.pixelSize+2), parent.color).split(",") : ["","0","0"]
|
||||
source: visible ? ltxInfo[0] : ""
|
||||
|
@ -111,7 +109,7 @@ Item {
|
|||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
objEditor.obj = Objects.currentObjects[obj.type][index]
|
||||
objEditor.obj = Runtime.Objects.currentObjects[obj.type][index]
|
||||
objEditor.objType = obj.type
|
||||
objEditor.objIndex = index
|
||||
//objEditor.editingRow = objectRow
|
||||
|
@ -213,10 +211,14 @@ Item {
|
|||
function deleteRecursively(object) {
|
||||
for(let toRemove of object.requiredBy)
|
||||
deleteRecursively(toRemove)
|
||||
object.requiredBy = []
|
||||
history.addToHistory(new HistoryLib.DeleteObject(
|
||||
object.name, object.type, object.export()
|
||||
))
|
||||
Objects.deleteObject(object.name)
|
||||
if(Runtime.Objects.currentObjectsByName[object.name] != undefined) {
|
||||
// Object still exists
|
||||
// Temporary fix for objects require not being propertly updated.
|
||||
object.requiredBy = []
|
||||
history.addToHistory(new HistoryLib.DeleteObject(
|
||||
object.name, object.type, object.export()
|
||||
))
|
||||
Runtime.Objects.deleteObject(object.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,8 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||
import "js/objects.js" as Objects
|
||||
import "js/mathlib.js" as MathLib
|
||||
import "js/historylib.js" as HistoryLib
|
||||
import "js/mathlib.mjs" as MathLib
|
||||
import "js/historylib.mjs" as HistoryLib
|
||||
|
||||
/*!
|
||||
\qmltype PickLocationOverlay
|
||||
|
@ -114,7 +113,7 @@ Item {
|
|||
if(mouse.button == Qt.LeftButton) { // Validate
|
||||
let newValueX = !parent.userPickX ? null : parseValue(picked.mouseX.toString(), objType, propertyX)
|
||||
let newValueY = !parent.userPickY ? null : parseValue(picked.mouseY.toString(), objType, propertyY)
|
||||
let obj = Objects.currentObjectsByName[objName]
|
||||
let obj = Runtime.Objects.currentObjectsByName[objName]
|
||||
// Set values
|
||||
if(parent.userPickX && parent.userPickY) {
|
||||
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.
|
||||
*/
|
||||
function parseValue(value, objType, propertyName) {
|
||||
if(Objects.types[objType].properties()[propertyName] == 'number')
|
||||
if(Runtime.Objects.types[objType].properties()[propertyName] == 'number')
|
||||
return parseFloat(value)
|
||||
else
|
||||
return new MathLib.Expression(value)
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import "../js/math/latex.js" as Latex
|
||||
|
||||
/*!
|
||||
\qmltype GreetScreen
|
||||
|
|
|
@ -20,10 +20,9 @@ import QtQuick.Controls
|
|||
import QtQuick
|
||||
import Qt.labs.platform as Native
|
||||
import eu.ad5001.LogarithmPlotter.Popup 1.0 as P
|
||||
import "../js/mathlib.js" as MathLib
|
||||
import "../js/utils.js" as Utils
|
||||
import "../js/objects.js" as Objects
|
||||
import "../js/parsing/parsing.js" as Parsing
|
||||
import "../js/mathlib.mjs" as MathLib
|
||||
import "../js/utils.mjs" as Utils
|
||||
import "../js/parsing/parsing.mjs" as Parsing
|
||||
|
||||
|
||||
/*!
|
||||
|
@ -392,9 +391,9 @@ Item {
|
|||
property string objectName: isEnteringProperty ?
|
||||
(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 ?
|
||||
Objects.currentObjectsByName[objectName].constructor.properties() :
|
||||
Runtime.Objects.currentObjectsByName[objectName].constructor.properties() :
|
||||
{}
|
||||
categoryItems: Object.keys(objectProperties)
|
||||
autocompleteGenerator: (item) => {
|
||||
|
@ -461,9 +460,9 @@ Item {
|
|||
visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot
|
||||
itemStartIndex: functionsList.itemStartIndex + functionsList.model.length
|
||||
itemSelected: parent.itemSelected
|
||||
categoryItems: Objects.getObjectsName("ExecutableObject").filter(obj => obj != self)
|
||||
categoryItems: Runtime.Objects.getObjectsName("ExecutableObject").filter(obj => obj != self)
|
||||
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
|
||||
}}
|
||||
baseText: parent.visible ? parent.currentToken.value : ""
|
||||
|
@ -476,9 +475,9 @@ Item {
|
|||
visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot
|
||||
itemStartIndex: executableObjectsList.itemStartIndex + executableObjectsList.model.length
|
||||
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 {
|
||||
'text': item, 'annotation': `${Objects.currentObjectsByName[item].constructor.displayType()}`,
|
||||
'text': item, 'annotation': `${Runtime.Objects.currentObjectsByName[item].constructor.displayType()}`,
|
||||
'autocomplete': item+'.', 'cursorFinalOffset': 0
|
||||
}}
|
||||
baseText: parent.visible ? parent.currentToken.value : ""
|
||||
|
@ -538,8 +537,8 @@ Item {
|
|||
throw new Error(qsTranslate('error', 'Object cannot be dependent on itself.'))
|
||||
// Recursive dependencies
|
||||
let dependentOnSelfObjects = expr.requiredObjects().filter(
|
||||
(obj) => Objects.currentObjectsByName[obj].getDependenciesList()
|
||||
.includes(Objects.currentObjectsByName[control.self])
|
||||
(obj) => Runtime.Objects.currentObjectsByName[obj].getDependenciesList()
|
||||
.includes(Runtime.Objects.currentObjectsByName[control.self])
|
||||
)
|
||||
if(dependentOnSelfObjects.length == 1)
|
||||
throw new Error(qsTranslate('error', 'Circular dependency detected. Object %1 depends on %2.').arg(dependentOnSelfObjects[0].toString()).arg(control.self))
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick
|
||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||
import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup
|
||||
import "js/utils.js" as Utils
|
||||
import "js/utils.mjs" as Utils
|
||||
|
||||
/*!
|
||||
\qmltype Settings
|
||||
|
|
|
@ -19,9 +19,8 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||
import "js/objects.js" as Objects
|
||||
import "js/mathlib.js" as MathLib
|
||||
import "js/historylib.js" as HistoryLib
|
||||
import "js/mathlib.mjs" as MathLib
|
||||
import "js/historylib.mjs" as HistoryLib
|
||||
|
||||
/*!
|
||||
\qmltype ViewPositionChangeOverlay
|
||||
|
|
|
@ -16,13 +16,10 @@
|
|||
* 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 ColorChanged extends EP.EditedProperty {
|
||||
export default class ColorChanged extends EditedProperty {
|
||||
// Action used everytime when an object's color is changed
|
||||
type(){return 'ColorChanged'}
|
||||
|
|
@ -16,16 +16,29 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "../math/latex.js" as Latex
|
||||
|
||||
var themeTextColor;
|
||||
var imageDepth = 2;
|
||||
var fontSize = 14;
|
||||
import { RuntimeAPI } from "../runtime.mjs"
|
||||
import Latex from "../math/latex.mjs"
|
||||
|
||||
|
||||
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.
|
||||
*
|
||||
|
@ -48,15 +61,11 @@ class Action {
|
|||
|
||||
/**
|
||||
* Undoes the action.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
undo() {}
|
||||
|
||||
/**
|
||||
* Redoes the action.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
redo() {}
|
||||
|
||||
|
@ -64,7 +73,7 @@ class Action {
|
|||
* Export the action to a serializable format.
|
||||
* NOTE: These arguments will be reinputed in the constructor in this order.
|
||||
*
|
||||
* @returns {string}
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export() {
|
||||
return [this.targetName, this.targetType]
|
||||
|
@ -86,7 +95,7 @@ class Action {
|
|||
* @returns {string}
|
||||
*/
|
||||
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) {
|
||||
if(!Latex.enabled)
|
||||
throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.")
|
||||
let latexInfo = Latex.Renderer.render(latexString, imageDepth*(fontSize+2), themeTextColor).split(",")
|
||||
return `<img src="${latexInfo[0]}" width="${parseInt(latexInfo[1])/imageDepth}" height="${parseInt(latexInfo[2])/imageDepth}" style="vertical-align: middle"></img>`
|
||||
let imgDepth = Runtime.History.imageDepth
|
||||
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"/>`
|
||||
}
|
||||
|
||||
/**
|
|
@ -16,12 +16,10 @@
|
|||
* 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
|
||||
.import "common.js" as C
|
||||
|
||||
class CreateNewObject extends C.Action {
|
||||
export default class CreateNewObject extends Action {
|
||||
// Action used for the creation of an object
|
||||
type(){return 'CreateNewObject'}
|
||||
|
||||
|
@ -37,10 +35,6 @@ class CreateNewObject extends C.Action {
|
|||
|
||||
undo() {
|
||||
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() {
|
|
@ -16,14 +16,14 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "../objects.js" as Objects
|
||||
.import "create.js" as Create
|
||||
import Objects from "../objects.mjs"
|
||||
import CreateNewObject from "create.mjs"
|
||||
|
||||
|
||||
class DeleteObject extends Create.CreateNewObject {
|
||||
// Action used at the deletion of an object. Basicly the same thing as creating a new object, except Redo & Undo are reversed.
|
||||
export default class DeleteObject extends CreateNewObject {
|
||||
/**
|
||||
* Action used at the deletion of an object. Basically the same thing as creating a new object, except Redo & Undo are reversed.
|
||||
*/
|
||||
type(){return 'DeleteObject'}
|
||||
|
||||
icon(){return 'delete'}
|
|
@ -16,15 +16,13 @@
|
|||
* 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
|
||||
.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 {
|
||||
export default class EditedProperty extends Action {
|
||||
// Action used everytime an object's property has been changed
|
||||
type(){return 'EditedProperty'}
|
||||
|
||||
|
@ -33,7 +31,16 @@ class EditedProperty extends C.Action {
|
|||
color(darkVer=false){
|
||||
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) {
|
||||
super(targetName, targetType)
|
||||
this.targetProperty = targetProperty
|
||||
|
@ -42,10 +49,10 @@ class EditedProperty extends C.Action {
|
|||
this.newValue = newValue
|
||||
this.propertyType = Objects.types[targetType].properties()[targetProperty]
|
||||
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.newValue = new MathLib.Expression(this.newValue);
|
||||
} else if(this.propertyType == "Domain") {
|
||||
} else if(this.propertyType === "Domain") {
|
||||
this.previousValue = MathLib.parseDomain(this.previousValue);
|
||||
this.newValue = MathLib.parseDomain(this.newValue);
|
||||
} else {
|
||||
|
@ -70,7 +77,7 @@ class EditedProperty extends C.Action {
|
|||
export() {
|
||||
if(this.previousValue instanceof MathLib.Expression) {
|
||||
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]
|
||||
} else {
|
||||
return [this.targetName, this.targetType, this.targetProperty, this.previousValue, this.newValue, false]
|
||||
|
@ -110,7 +117,7 @@ class EditedProperty extends C.Action {
|
|||
// HTML
|
||||
this.prevHTML = '<tt style="background: rgba(128,128,128,0.1);"> '+this.prevString+' </tt>'
|
||||
this.nextHTML = '<tt style="background: rgba(128,128,128,0.1);"> '+this.nextString+' </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.nextHTML= this.renderLatexAsHtml(this.newValue.latexMarkup)
|
||||
}
|
|
@ -16,19 +16,16 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "editproperty.js" as EP
|
||||
.import "../objects.js" as Objects
|
||||
import EditedProperty from "editproperty.mjs"
|
||||
import Objects from "../objects.mjs"
|
||||
|
||||
|
||||
class NameChanged extends EP.EditedProperty {
|
||||
export default class NameChanged extends EditedProperty {
|
||||
// Action used everytime an object's property has been changed
|
||||
type(){return 'NameChanged'}
|
||||
|
||||
icon(){return 'name'}
|
||||
|
||||
|
||||
|
||||
color(darkVer=false){return darkVer ? 'darkorange' : 'orange'}
|
||||
|
||||
constructor(targetName = "", targetType = "Point", newName = "") {
|
||||
|
@ -45,10 +42,6 @@ class NameChanged extends EP.EditedProperty {
|
|||
|
||||
redo() {
|
||||
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() {
|
|
@ -16,16 +16,14 @@
|
|||
* 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
|
||||
.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 {
|
||||
export default class EditedPosition extends Action {
|
||||
// Action used for objects that have a X and Y expression properties (points, texts...)
|
||||
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.nextHTML = this.renderLatexAsHtml(`\\left(${this.newXValue.latexMarkup},${this.newYValue.latexMarkup}\\right)`)
|
||||
} else {
|
||||
this.prevHTML = '<tt style="background: rgba(128,128,128,0.1);"> '+Utils.escapeHTML(this.prevString)+' </tt>'
|
||||
this.nextHTML = '<tt style="background: rgba(128,128,128,0.1);"> '+Utils.escapeHTML(this.nextString)+' </tt>'
|
||||
this.prevHTML = '<tt style="background: rgba(128,128,128,0.1);"> '+escapeHTML(this.prevString)+' </tt>'
|
||||
this.nextHTML = '<tt style="background: rgba(128,128,128,0.1);"> '+escapeHTML(this.nextString)+' </tt>'
|
||||
}
|
||||
|
||||
}
|
|
@ -16,13 +16,11 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "editproperty.js" as EP
|
||||
.import "../objects.js" as Objects
|
||||
import EditedProperty from "editproperty.mjs"
|
||||
import Objects from "../objects.mjs"
|
||||
|
||||
|
||||
class EditedVisibility extends EP.EditedProperty {
|
||||
export default class EditedVisibility extends EditedProperty {
|
||||
// Action used when an object's shown or hidden.
|
||||
type(){return 'EditedVisibility'}
|
||||
|
|
@ -18,30 +18,27 @@
|
|||
|
||||
// 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.
|
||||
.pragma library
|
||||
|
||||
.import "history/common.js" as Common
|
||||
.import "history/create.js" as Create
|
||||
.import "history/delete.js" as Delete
|
||||
.import "history/editproperty.js" as EP
|
||||
.import "history/position.js" as Pos
|
||||
.import "history/visibility.js" as V
|
||||
.import "history/name.js" as Name
|
||||
.import "history/color.js" as Color
|
||||
|
||||
var history = null;
|
||||
import { Action as A } from "history/common.mjs"
|
||||
import Create from "history/create.mjs"
|
||||
import Delete from "history/delete.mjs"
|
||||
import EP from "history/editproperty.mjs"
|
||||
import Pos from "history/position.mjs"
|
||||
import V from "history/visibility.mjs"
|
||||
import Name from "history/name.mjs"
|
||||
import Color from "history/color.mjs"
|
||||
|
||||
|
||||
var Action = Common.Action
|
||||
var CreateNewObject = Create.CreateNewObject
|
||||
var DeleteObject = Delete.DeleteObject
|
||||
var EditedProperty = EP.EditedProperty
|
||||
var EditedPosition = Pos.EditedPosition
|
||||
var EditedVisibility = V.EditedVisibility
|
||||
var NameChanged = Name.NameChanged
|
||||
var ColorChanged = Color.ColorChanged
|
||||
export const Action = A
|
||||
export const CreateNewObject = Create
|
||||
export const DeleteObject = Delete
|
||||
export const EditedProperty = EP
|
||||
export const EditedPosition = Pos
|
||||
export const EditedVisibility = V
|
||||
export const NameChanged = Name
|
||||
export const ColorChanged = Color
|
||||
|
||||
var Actions = {
|
||||
export const Actions = {
|
||||
"Action": Action,
|
||||
"CreateNewObject": CreateNewObject,
|
||||
"DeleteObject": DeleteObject,
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
@ -1,42 +1,30 @@
|
|||
/**
|
||||
* 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
|
||||
// Type polyfills for IDEs.
|
||||
// Never directly imported.
|
||||
|
||||
.import "common.js" as C
|
||||
.import "point.js" as P
|
||||
.import "text.js" as T
|
||||
.import "function.js" as F
|
||||
.import "gainbode.js" as GB
|
||||
.import "phasebode.js" as PB
|
||||
.import "sommegainsbode.js" as SGB
|
||||
.import "sommephasesbode.js" as SPB
|
||||
.import "xcursor.js" as X
|
||||
.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)
|
||||
Runtime = Runtime || {}
|
||||
/** @type {function(string, string): string} */
|
||||
qsTranslate = qsTranslate || function(category, string) { throw new Error('qsTranslate not implemented.'); }
|
||||
/** @type {function(string): string} */
|
||||
qsTr = qsTr || function(string) { throw new Error('qsTr not implemented.'); }
|
||||
/** @type {function(string, string): string} */
|
||||
QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); }
|
||||
/** @type {function(string|boolean|int): string} */
|
||||
String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); }
|
|
@ -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
|
||||
}
|
||||
|
|
@ -16,21 +16,19 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "expression.js" as Expr
|
||||
import { Expression, executeExpression } from "expression.mjs"
|
||||
|
||||
/**
|
||||
* Main abstract domain class
|
||||
* It doesn't represent any kind of domain and is meant to be extended.
|
||||
*/
|
||||
class Domain {
|
||||
export class Domain {
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* Checks whether x is included in the domain.
|
||||
* @param {number} x - The x value.
|
||||
* @return {bool} true if included, false otherwise.
|
||||
* @return {boolean} true if included, false otherwise.
|
||||
*/
|
||||
includes(x) { return false }
|
||||
|
||||
|
@ -166,7 +164,7 @@ class Domain {
|
|||
/**
|
||||
* Represents an empty set.
|
||||
*/
|
||||
class EmptySet extends Domain {
|
||||
export class EmptySet extends Domain {
|
||||
constructor() {
|
||||
super()
|
||||
this.displayName = "∅"
|
||||
|
@ -187,12 +185,12 @@ class EmptySet extends Domain {
|
|||
/**
|
||||
* Domain classes for ranges (e.g ]0;3[, [1;2[ ...)
|
||||
*/
|
||||
class Range extends Domain {
|
||||
export class Range extends Domain {
|
||||
constructor(begin, end, openBegin, openEnd) {
|
||||
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
|
||||
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.openBegin = openBegin
|
||||
this.openEnd = openEnd
|
||||
|
@ -201,7 +199,7 @@ class Range extends Domain {
|
|||
}
|
||||
|
||||
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())) &&
|
||||
((this.openEnd && x < this.end.execute()) || (!this.openEnd && x <= this.end.execute()))
|
||||
}
|
||||
|
@ -229,9 +227,9 @@ class Range extends Domain {
|
|||
}
|
||||
|
||||
static import(frm) {
|
||||
var openBegin = frm.trim().charAt(0) == "]"
|
||||
var openEnd = frm.trim().charAt(frm.length -1) == "["
|
||||
var [begin, end] = frm.substr(1, frm.length-2).split(";")
|
||||
let openBegin = frm.trim().charAt(0) === "]"
|
||||
let openEnd = frm.trim().charAt(frm.length -1) === "["
|
||||
let [begin, end] = frm.substr(1, frm.length-2).split(";")
|
||||
return new Range(begin.trim(), end.trim(), openBegin, openEnd)
|
||||
}
|
||||
}
|
||||
|
@ -239,17 +237,16 @@ class Range extends Domain {
|
|||
/**
|
||||
* Domain classes for special domains (N, Z, ...)
|
||||
*/
|
||||
class SpecialDomain extends Domain {
|
||||
export class SpecialDomain extends Domain {
|
||||
/**
|
||||
* @constructs SpecialDomain
|
||||
* @param {string} displayName
|
||||
* @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} 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 items
|
||||
* @param {boolean} moveSupported - Only true if next and previous functions are valid.
|
||||
*/
|
||||
constructor(displayName, isValid, next = x => true, previous = x => true,
|
||||
constructor(displayName, isValid, next = () => true, previous = () => true,
|
||||
moveSupported = true) {
|
||||
super()
|
||||
this.displayName = displayName
|
||||
|
@ -260,17 +257,17 @@ class SpecialDomain extends Domain {
|
|||
}
|
||||
|
||||
includes(x) {
|
||||
if(typeof x == 'string') x = Expr.executeExpression(x)
|
||||
if(typeof x == 'string') x = executeExpression(x)
|
||||
return this.isValid(x)
|
||||
}
|
||||
|
||||
next(x) {
|
||||
if(typeof x == 'string') x = Expr.executeExpression(x)
|
||||
if(typeof x == 'string') x = executeExpression(x)
|
||||
return this.nextValue(x)
|
||||
}
|
||||
|
||||
previous(x) {
|
||||
if(typeof x == 'string') x = Expr.executeExpression(x)
|
||||
if(typeof x == 'string') x = executeExpression(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} ...)
|
||||
*/
|
||||
class DomainSet extends SpecialDomain {
|
||||
export class DomainSet extends SpecialDomain {
|
||||
constructor(values) {
|
||||
super('', x => true, x => x, true)
|
||||
var newVals = {}
|
||||
let newVals = {}
|
||||
this.executedValues = []
|
||||
for(var value of values) {
|
||||
var expr = new Expr.Expression(value.toString())
|
||||
var ex = expr.execute()
|
||||
for(let value of values) {
|
||||
let expr = new Expression(value.toString())
|
||||
let ex = expr.execute()
|
||||
newVals[ex] = expr
|
||||
this.executedValues.push(ex)
|
||||
}
|
||||
|
@ -318,30 +315,30 @@ class DomainSet extends SpecialDomain {
|
|||
}
|
||||
|
||||
includes(x) {
|
||||
if(typeof x == 'string') x = Expr.executeExpression(x)
|
||||
for(var value of this.values)
|
||||
if(x == value.execute()) return true
|
||||
if(typeof x == 'string') x = executeExpression(x)
|
||||
for(let value of this.values)
|
||||
if(x === value.execute()) return true
|
||||
return false
|
||||
}
|
||||
|
||||
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]
|
||||
for(var i = 1; i < this.values.length; i++) {
|
||||
var prevValue = this.executedValues[i-1]
|
||||
var value = this.executedValues[i]
|
||||
for(let i = 1; i < this.values.length; i++) {
|
||||
let prevValue = this.executedValues[i-1]
|
||||
let value = this.executedValues[i]
|
||||
if(x >= prevValue && x < value) return value
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
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])
|
||||
return this.executedValues[this.executedValues.length-1]
|
||||
for(var i = 1; i < this.values.length; i++) {
|
||||
var prevValue = this.executedValues[i-1]
|
||||
var value = this.executedValues[i]
|
||||
for(let i = 1; i < this.values.length; i++) {
|
||||
let prevValue = this.executedValues[i-1]
|
||||
let value = this.executedValues[i]
|
||||
if(x > prevValue && x <= value) return prevValue
|
||||
}
|
||||
return null
|
||||
|
@ -354,56 +351,56 @@ class DomainSet extends SpecialDomain {
|
|||
union(domain) {
|
||||
if(domain instanceof EmptySet) return this
|
||||
if(domain instanceof DomainSet) {
|
||||
var newValues = []
|
||||
var values = this.values.concat(domain.values).filter(function(val){
|
||||
let newValues = []
|
||||
let values = this.values.concat(domain.values).filter(function(val){
|
||||
newValues.push(val.execute())
|
||||
return newValues.indexOf(val.execute()) == newValues.length - 1
|
||||
return newValues.indexOf(val.execute()) === newValues.length - 1
|
||||
})
|
||||
return new DomainSet(values)
|
||||
}
|
||||
var notIncludedValues = []
|
||||
for(var value in this.values) {
|
||||
var value = this.executedValues[i]
|
||||
let notIncludedValues = []
|
||||
for(let i = 0; i < this.values.length; i++) {
|
||||
let value = this.executedValues[i]
|
||||
if(domain instanceof Range) {
|
||||
if(domain.begin.execute() == value && domain.openBegin) {
|
||||
if(domain.begin.execute() === value && domain.openBegin) {
|
||||
domain.openBegin = false
|
||||
}
|
||||
if(domain.end.execute() == value && domain.openEnd) {
|
||||
if(domain.end.execute() === value && domain.openEnd) {
|
||||
domain.openEnd = false
|
||||
}
|
||||
}
|
||||
if(!domain.includes(value))
|
||||
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))
|
||||
}
|
||||
|
||||
intersection(domain) {
|
||||
if(domain instanceof EmptySet) return domain
|
||||
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){
|
||||
return domValues.indexOf(val.execute()) >= 0
|
||||
})
|
||||
return this
|
||||
}
|
||||
var includedValues = []
|
||||
for(var i in this.values) {
|
||||
var value = this.executedValues[i]
|
||||
let includedValues = []
|
||||
for(let i in this.values) {
|
||||
let value = this.executedValues[i]
|
||||
if(domain instanceof Range) {
|
||||
if(domain.begin.execute() == value && !domain.openBegin) {
|
||||
if(domain.begin.execute() === value && !domain.openBegin) {
|
||||
domain.openBegin = false
|
||||
}
|
||||
if(domain.end.execute() == value && !domain.openEnd) {
|
||||
if(domain.end.execute() === value && !domain.openEnd) {
|
||||
domain.openEnd = false
|
||||
}
|
||||
}
|
||||
if(domain.includes(value))
|
||||
includedValues.push(this.values[i].toEditableString())
|
||||
}
|
||||
if(includedValues.length == 0) return new EmptySet()
|
||||
if(includedValues.length == this.values.length) return this
|
||||
if(includedValues.length === 0) return new EmptySet()
|
||||
if(includedValues.length === this.values.length) return this
|
||||
return new IntersectionDomain(domain, new DomainSet(includedValues))
|
||||
}
|
||||
|
||||
|
@ -415,7 +412,7 @@ class DomainSet extends SpecialDomain {
|
|||
/**
|
||||
* Domain representing the union between two domains.
|
||||
*/
|
||||
class UnionDomain extends Domain {
|
||||
export class UnionDomain extends Domain {
|
||||
constructor(dom1, dom2) {
|
||||
super()
|
||||
this.dom1 = dom1
|
||||
|
@ -450,10 +447,10 @@ class UnionDomain extends Domain {
|
|||
}
|
||||
|
||||
static import(frm) {
|
||||
var domains = frm.trim().split("∪")
|
||||
if(domains.length == 1) domains = frm.trim().split("U") // Fallback
|
||||
var dom2 = parseDomain(domains.pop())
|
||||
var dom1 = parseDomain(domains.join('∪'))
|
||||
let domains = frm.trim().split("∪")
|
||||
if(domains.length === 1) domains = frm.trim().split("U") // Fallback
|
||||
let dom2 = parseDomain(domains.pop())
|
||||
let dom1 = parseDomain(domains.join('∪'))
|
||||
return dom1.union(dom2)
|
||||
}
|
||||
}
|
||||
|
@ -461,7 +458,7 @@ class UnionDomain extends Domain {
|
|||
/**
|
||||
* Domain representing the intersection between two domains.
|
||||
*/
|
||||
class IntersectionDomain extends Domain {
|
||||
export class IntersectionDomain extends Domain {
|
||||
constructor(dom1, dom2) {
|
||||
super()
|
||||
this.dom1 = dom1
|
||||
|
@ -496,9 +493,9 @@ class IntersectionDomain extends Domain {
|
|||
}
|
||||
|
||||
static import(frm) {
|
||||
var domains = frm.trim().split("∩")
|
||||
var dom1 = parseDomain(domains.pop())
|
||||
var dom2 = parseDomain(domains.join('∩'))
|
||||
let domains = frm.trim().split("∩")
|
||||
let dom1 = parseDomain(domains.pop())
|
||||
let dom2 = parseDomain(domains.join('∩'))
|
||||
return dom1.intersection(dom2)
|
||||
}
|
||||
}
|
||||
|
@ -506,7 +503,7 @@ class IntersectionDomain extends Domain {
|
|||
/**
|
||||
* Domain representing the minus between two domains.
|
||||
*/
|
||||
class MinusDomain extends Domain {
|
||||
export class MinusDomain extends Domain {
|
||||
constructor(dom1, dom2) {
|
||||
super()
|
||||
this.dom1 = dom1
|
||||
|
@ -524,10 +521,10 @@ class MinusDomain extends Domain {
|
|||
}
|
||||
|
||||
static import(frm) {
|
||||
var domains = frm.trim().split("∖")
|
||||
if(domains.length == 1) domains = frm.trim().split("\\") // Fallback
|
||||
var dom1 = parseDomain(domains.shift())
|
||||
var dom2 = parseDomain(domains.join('∪'))
|
||||
let domains = frm.trim().split("∖")
|
||||
if(domains.length === 1) domains = frm.trim().split("\\") // Fallback
|
||||
let dom1 = parseDomain(domains.shift())
|
||||
let dom2 = parseDomain(domains.join('∪'))
|
||||
return new MinusDomain(dom1, dom2)
|
||||
}
|
||||
}
|
||||
|
@ -551,53 +548,53 @@ Domain.RPE.latexMarkup = "\\mathbb{R}^{+*}"
|
|||
Domain.RME = new Range(-Infinity,0,true,true)
|
||||
Domain.RME.displayName = "ℝ⁻*"
|
||||
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.ceil(x)-1, 0))
|
||||
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.ceil(x)-1, 1))
|
||||
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.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.ceil(x)-1 == 0 ? Math.ceil(x)-2 : Math.ceil(x)-1)
|
||||
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.ceil(x)-1 === 0 ? Math.ceil(x)-2 : Math.ceil(x)-1)
|
||||
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.ceil(x)-1, 0))
|
||||
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.ceil(x)-1, -1))
|
||||
Domain.ZME.latexMarkup = "\\mathbb{Z}^{-*}"
|
||||
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) {
|
||||
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)
|
||||
},
|
||||
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)
|
||||
})
|
||||
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})
|
||||
* @param {string} domain - string of the domain to be parsed.
|
||||
* @returns {Domain} Parsed domain.
|
||||
*/
|
||||
function parseDomain(domain) {
|
||||
export function parseDomain(domain) {
|
||||
if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain)
|
||||
var domStr
|
||||
let domStr
|
||||
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)
|
||||
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+*...
|
||||
* @param {string} domain - string of the domain to be parsed.
|
||||
* @returns {Domain} Parsed domain.
|
||||
*/
|
||||
function parseDomainSimple(domain) {
|
||||
export function parseDomainSimple(domain) {
|
||||
domain = domain.trim()
|
||||
if(domain.includes("U") || domain.includes("∪")) return UnionDomain.import(domain)
|
||||
if(domain.includes("∩")) return IntersectionDomain.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(["R", "ℝ", "N", "ℕ", "Z", "ℤ"].some(str => domain.toUpperCase().includes(str)))
|
||||
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()
|
||||
}
|
|
@ -16,21 +16,25 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as C
|
||||
.import "latex.js" as Latex
|
||||
.import "../utils.js" as Utils
|
||||
import Latex from "latex.mjs"
|
||||
import * as Utils from "../utils.mjs"
|
||||
|
||||
/**
|
||||
* Represents any kind of x-based or non variable based expression.
|
||||
*/
|
||||
class Expression {
|
||||
export class Expression {
|
||||
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.calc = C.parser.parse(this.expr).simplify()
|
||||
this.calc = Runtime.ExprParser.parse(this.expr).simplify()
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -40,37 +44,37 @@ class Expression {
|
|||
}
|
||||
|
||||
requiredObjects() {
|
||||
return this.calc.variables().filter(objName => objName != "x" && objName != "n")
|
||||
return this.calc.variables().filter(objName => objName !== "x" && objName !== "n")
|
||||
}
|
||||
|
||||
allRequirementsFullfilled() {
|
||||
return this.requiredObjects().every(objName => objName in C.currentObjectsByName)
|
||||
return this.requiredObjects().every(objName => objName in Runtime.Objects.currentObjectsByName)
|
||||
}
|
||||
|
||||
undefinedVariables() {
|
||||
return this.requiredObjects().filter(objName => !(objName in C.currentObjectsByName))
|
||||
return this.requiredObjects().filter(objName => !(objName in Runtime.Objects.currentObjectsByName))
|
||||
}
|
||||
|
||||
recache() {
|
||||
if(this.cached)
|
||||
this.cachedValue = this.calc.evaluate(C.currentObjectsByName)
|
||||
this.cachedValue = this.calc.evaluate(Runtime.Objects.currentObjectsByName)
|
||||
}
|
||||
|
||||
execute(x = 1) {
|
||||
if(this.cached) {
|
||||
if(this.cachedValue == null)
|
||||
this.cachedValue = this.calc.evaluate(C.currentObjectsByName)
|
||||
this.cachedValue = this.calc.evaluate(Runtime.Objects.currentObjectsByName)
|
||||
return this.cachedValue
|
||||
}
|
||||
C.currentVars = Object.assign({'x': x}, C.currentObjectsByName)
|
||||
return this.calc.evaluate(C.currentVars)
|
||||
Runtime.ExprParser.currentVars = Object.assign({'x': x}, Runtime.Objects.currentObjectsByName)
|
||||
return this.calc.evaluate(Runtime.ExprParser.currentVars)
|
||||
}
|
||||
|
||||
simplify(x) {
|
||||
var expr = this.calc.substitute('x', x).simplify()
|
||||
if(expr.evaluate() == 0) return '0'
|
||||
var str = Utils.makeExpressionReadable(expr.toString());
|
||||
if(str != undefined && str.match(/^\d*\.\d+$/)) {
|
||||
let expr = this.calc.substitute('x', x).simplify()
|
||||
if(expr.evaluate() === 0) return '0'
|
||||
let str = Utils.makeExpressionReadable(expr.toString());
|
||||
if(str !== undefined && str.match(/^\d*\.\d+$/)) {
|
||||
if(str.split('.')[1].split('0').length > 7) {
|
||||
// Likely rounding error
|
||||
str = parseFloat(str.substring(0, str.length-1)).toString();
|
||||
|
@ -89,11 +93,11 @@ class Expression {
|
|||
|
||||
toString(forceSign=false) {
|
||||
let str = Utils.makeExpressionReadable(this.calc.toString())
|
||||
if(str[0] != '-' && forceSign) str = '+' + str
|
||||
if(str[0] !== '-' && forceSign) str = '+' + str
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
function executeExpression(expr){
|
||||
export function executeExpression(expr){
|
||||
return (new Expression(expr.toString())).execute()
|
||||
}
|
|
@ -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]);
|
||||
}
|
|
@ -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
|
|
@ -16,18 +16,14 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as C
|
||||
.import "expression.js" as Expr
|
||||
.import "../utils.js" as Utils
|
||||
.import "../math/latex.js" as Latex
|
||||
|
||||
import * as Expr from "expression.mjs"
|
||||
import * as Utils from "../utils.mjs"
|
||||
import Latex from "../math/latex.mjs"
|
||||
|
||||
/**
|
||||
* Represents mathematical object for sequences.
|
||||
*/
|
||||
class Sequence extends Expr.Expression {
|
||||
export class Sequence extends Expr.Expression {
|
||||
constructor(name, baseValues = {}, valuePlus = 1, expr = "") {
|
||||
// u[n+valuePlus] = expr
|
||||
super(expr)
|
||||
|
@ -35,9 +31,9 @@ class Sequence extends Expr.Expression {
|
|||
this.baseValues = baseValues
|
||||
this.calcValues = 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])) {
|
||||
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.calcValues[n] = parsed.evaluate()
|
||||
}
|
||||
|
@ -45,7 +41,7 @@ class Sequence extends Expr.Expression {
|
|||
}
|
||||
|
||||
isConstant() {
|
||||
return this.expr.indexOf("n") == -1
|
||||
return this.expr.indexOf("n") === -1
|
||||
}
|
||||
|
||||
execute(n = 1) {
|
||||
|
@ -63,25 +59,25 @@ class Sequence extends Expr.Expression {
|
|||
}
|
||||
|
||||
cache(n = 1) {
|
||||
var str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString())
|
||||
var expr = C.parser.parse(str).simplify()
|
||||
// Chache values required for this one.
|
||||
let str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString())
|
||||
let expr = Runtime.ExprParser.parse(str).simplify()
|
||||
// Cache values required for this one.
|
||||
if(!this.calcValues[n-this.valuePlus] && n-this.valuePlus > 0)
|
||||
this.cache(n-this.valuePlus)
|
||||
// Setting current variables
|
||||
C.currentVars = Object.assign(
|
||||
Runtime.ExprParser.currentVars = Object.assign(
|
||||
{'n': n-this.valuePlus}, // Just in case, add n (for custom functions)
|
||||
C.currentObjectsByName,
|
||||
Runtime.Objects.currentObjectsByName,
|
||||
{[this.name]: this.calcValues}
|
||||
)
|
||||
this.calcValues[n] = expr.evaluate(C.currentVars)
|
||||
this.calcValues[n] = expr.evaluate(Runtime.ExprParser.currentVars)
|
||||
}
|
||||
|
||||
toString(forceSign=false) {
|
||||
var str = Utils.makeExpressionReadable(this.calc.toString())
|
||||
if(str[0] != '-' && forceSign) str = '+' + str
|
||||
var subtxt = this.valuePlus == 0 ? 'ₙ' : Utils.textsub('n+' + this.valuePlus)
|
||||
var ret = `${this.name}${subtxt} = ${str}${this.baseValues.length == 0 ? '' : "\n"}`
|
||||
let str = Utils.makeExpressionReadable(this.calc.toString())
|
||||
if(str[0] !== '-' && forceSign) str = '+' + str
|
||||
let subtxt = this.valuePlus === 0 ? 'ₙ' : Utils.textsub('n+' + this.valuePlus)
|
||||
let ret = `${this.name}${subtxt} = ${str}${this.baseValues.length === 0 ? '' : "\n"}`
|
||||
ret += Object.keys(this.baseValues).map(
|
||||
n => `${this.name}${Utils.textsub(n)} = ${this.baseValues[n]}`
|
||||
).join('; ')
|
||||
|
@ -89,10 +85,10 @@ class Sequence extends Expr.Expression {
|
|||
}
|
||||
|
||||
toLatexString(forceSign=false) {
|
||||
var str = this.latexMarkup
|
||||
if(str[0] != '-' && forceSign) str = '+' + str
|
||||
var subtxt = '_{n' + (this.valuePlus == 0 ? '' : '+' + this.valuePlus) + '}'
|
||||
var ret = `\\begin{array}{l}${Latex.variable(this.name)}${subtxt} = ${str}${this.latexValues.length == 0 ? '' : "\n"}\\\\`
|
||||
let str = this.latexMarkup
|
||||
if(str[0] !== '-' && forceSign) str = '+' + str
|
||||
let subtxt = '_{n' + (this.valuePlus === 0 ? '' : '+' + this.valuePlus) + '}'
|
||||
let ret = `\\begin{array}{l}${Latex.variable(this.name)}${subtxt} = ${str}${this.latexValues.length === 0 ? '' : "\n"}\\\\`
|
||||
ret += Object.keys(this.latexValues).map(
|
||||
n => `${this.name}_{${n}} = ${this.latexValues[n]}`
|
||||
).join('; ') + "\\end{array}"
|
|
@ -16,26 +16,25 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "math/expression.js" as Expr
|
||||
.import "math/sequence.js" as Seq
|
||||
import * as Expr from "math/expression.mjs"
|
||||
import * as Seq from "math/sequence.mjs"
|
||||
import * as Dom from "math/domain.mjs"
|
||||
|
||||
.import "math/domain.js" as Dom
|
||||
|
||||
var Expression = Expr.Expression
|
||||
var executeExpression = Expr.executeExpression
|
||||
var Sequence = Seq.Sequence
|
||||
export const Expression = Expr.Expression
|
||||
export const executeExpression = Expr.executeExpression
|
||||
export const Sequence = Seq.Sequence
|
||||
|
||||
// Domains
|
||||
var Domain = Dom.Domain
|
||||
var EmptySet = Dom.EmptySet
|
||||
var Range = Dom.Range
|
||||
var SpecialDomain = Dom.SpecialDomain
|
||||
var DomainSet = Dom.DomainSet
|
||||
var UnionDomain = Dom.UnionDomain
|
||||
var IntersectionDomain = Dom.IntersectionDomain
|
||||
var MinusDomain = Dom.MinusDomain
|
||||
export const Domain = Dom.Domain
|
||||
export const EmptySet = Dom.EmptySet
|
||||
export const Range = Dom.Range
|
||||
export const SpecialDomain = Dom.SpecialDomain
|
||||
export const DomainSet = Dom.DomainSet
|
||||
export const UnionDomain = Dom.UnionDomain
|
||||
export const IntersectionDomain = Dom.IntersectionDomain
|
||||
export const MinusDomain = Dom.MinusDomain
|
||||
|
||||
var parseDomain = Dom.parseDomain
|
||||
var parseDomainSimple = Dom.parseDomainSimple
|
||||
export const parseDomain = Dom.parseDomain
|
||||
export const parseDomainSimple = Dom.parseDomainSimple
|
|
@ -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
|
|
@ -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
|
||||
}
|
112
LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.mjs
Normal file
112
LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.mjs
Normal 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
|
|
@ -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)
|
||||
}
|
|
@ -16,42 +16,71 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "../utils.js" as Utils
|
||||
.import "../objects.js" as Objects
|
||||
.import "../math/latex.js" as Latex
|
||||
.import "../parameters.js" as P
|
||||
.import "../math/common.js" as C
|
||||
import { getRandomColor, textsub } from "../utils.mjs"
|
||||
import Objects from "../objects.mjs"
|
||||
import Latex from "../math/latex.mjs"
|
||||
import {RuntimeAPI} from "../runtime.mjs"
|
||||
|
||||
// This file contains the default data to be imported from all other objects
|
||||
|
||||
/**
|
||||
* Creates a new name for an object, based on the \c allowedLetters.
|
||||
* If variables with each of the allowedLetters is created, a subscript
|
||||
* number is added to the name.
|
||||
* @param {string} prefix - Prefix to the name.
|
||||
* @return {string} New unused name for a new object.
|
||||
*/
|
||||
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
|
||||
do {
|
||||
var letter = allowedLetters[newid % allowedLetters.length]
|
||||
var num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length)
|
||||
ret = prefix + letter + (num > 0 ? Utils.textsub(num-1) : '')
|
||||
newid += 1
|
||||
} while(ret in Objects.currentObjectsByName)
|
||||
return ret
|
||||
class ObjectsCommonAPI extends RuntimeAPI {
|
||||
|
||||
constructor() {
|
||||
super('ObjectsCommon', [
|
||||
Runtime.Objects,
|
||||
Runtime.ExprParser,
|
||||
Runtime.Latex
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new name for an object, based on the \c allowedLetters.
|
||||
* If variables with each of the allowedLetters is created, a subscript
|
||||
* number is added to the name.
|
||||
* @param {string} allowedLetters
|
||||
* @param {string} prefix - Prefix to the name.
|
||||
* @return {string} New unused name for a new object.
|
||||
*/
|
||||
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
|
||||
* can be drawn on the canvas.
|
||||
*/
|
||||
class DrawableObject {
|
||||
export class DrawableObject {
|
||||
/**
|
||||
* Base name of the object. Needs to be constant over time.
|
||||
* @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
|
||||
* be instantiated by other objects
|
||||
* @return {bool}
|
||||
* @return {boolean}
|
||||
*/
|
||||
static createable() {return true}
|
||||
|
||||
|
@ -89,30 +118,30 @@ class DrawableObject {
|
|||
* If the property is to be translated, the key should be passed
|
||||
* through the QT_TRANSLATE_NOOP macro in that form:
|
||||
* [QT_TRANSLATE_NOOP('prop','key')]
|
||||
* Enums that are translated should be indexed in parameters.js and
|
||||
* Enums that are translated should be indexed in parameters.mjs and
|
||||
* then be linked directly here.
|
||||
*
|
||||
* @return {Dictionary}
|
||||
* @return {Object.<string,string|Enum|List|ObjectType|Dictionary>}
|
||||
*/
|
||||
static properties() {return {}}
|
||||
|
||||
/**
|
||||
* True if this object can be executed, so that an y value might be computed
|
||||
* for an x value. Only ExecutableObjects have that property set to true.
|
||||
* @return {bool}
|
||||
* @return {boolean}
|
||||
*/
|
||||
static executable() {return false}
|
||||
|
||||
/**
|
||||
* Base constructor for the object.
|
||||
* @param {string} name - Name of the object
|
||||
* @param {bool} visible - true if the object is visible, false otherwise.
|
||||
* @param {color} color - Color of the object (can be string or QColor)
|
||||
* @param {boolean} visible - true if the object is visible, false otherwise.
|
||||
* @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.
|
||||
* @constructor()
|
||||
*/
|
||||
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.name = name
|
||||
this.visible = visible
|
||||
|
@ -145,7 +174,7 @@ class DrawableObject {
|
|||
* Latex markuped version of the readable string.
|
||||
* Every non latin character should be passed as latex symbols and formulas
|
||||
* should be in latex form.
|
||||
* See ../latex.js for helper methods.
|
||||
* See ../latex.mjs for helper methods.
|
||||
* @return {string}
|
||||
*/
|
||||
getLatexString() {
|
||||
|
@ -172,7 +201,7 @@ class DrawableObject {
|
|||
* Latex markup string content of the label depending on the value of the \c latexContent.
|
||||
* Every non latin character should be passed as latex symbols and formulas
|
||||
* should be in latex form.
|
||||
* See ../latex.js for helper methods.
|
||||
* See ../latex.mjs for helper methods.
|
||||
* @return {string}
|
||||
*/
|
||||
getLatexLabel() {
|
||||
|
@ -204,25 +233,26 @@ class DrawableObject {
|
|||
update() {
|
||||
// Refreshing dependencies.
|
||||
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
|
||||
this.requires = []
|
||||
let currentObjectsByName = Objects.currentObjectsByName
|
||||
let properties = this.constructor.properties()
|
||||
for(let property in properties)
|
||||
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
|
||||
for(let objName of this[property].requiredObjects()) {
|
||||
if(objName in C.currentObjectsByName && !this.requires.includes(C.currentObjectsByName[objName])) {
|
||||
this.requires.push(C.currentObjectsByName[objName])
|
||||
C.currentObjectsByName[objName].requiredBy.push(this)
|
||||
if(objName in currentObjectsByName && !this.requires.includes(currentObjectsByName[objName])) {
|
||||
this.requires.push(currentObjectsByName[objName])
|
||||
currentObjectsByName[objName].requiredBy.push(this)
|
||||
}
|
||||
}
|
||||
if(this[property].cached && this[property].requiredObjects().length > 0)
|
||||
// Recalculate
|
||||
this[property].recache()
|
||||
|
||||
} else if(properties[property].type == 'ObjectType' && this[property] != null) {
|
||||
} else if(properties[property].type === 'ObjectType' && this[property] != null) {
|
||||
// Object dependency
|
||||
this.requires.push(this[property])
|
||||
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.
|
||||
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.
|
||||
* @param {Canvas} canvas
|
||||
* @param {Context2D} ctx
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
*/
|
||||
draw(canvas, ctx) {}
|
||||
|
||||
|
@ -256,7 +290,7 @@ class DrawableObject {
|
|||
*
|
||||
* @param {string|Enum} labelPosition - Position of the label relative to the marked position
|
||||
* @param {number} offset - Margin between the position and the object to be drawn
|
||||
* @param {Dictionary} size - Size of the label item, containing two properties, "width" and "height"
|
||||
* @param {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} 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
|
||||
|
@ -310,17 +344,17 @@ class DrawableObject {
|
|||
* \c drawFunctionText (x,y,text) depending on whether to use latex.
|
||||
*
|
||||
* @param {Canvas} canvas
|
||||
* @param {Context2D} ctx
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
* @param {string|Enum} labelPosition - Position of the label relative to the marked position
|
||||
* @param {number} posX - Component of the marked position on the x-axis
|
||||
* @param {number} posY - Component of the marked position on the y-axis
|
||||
* @param {bool} forceText - Force the rendering of the label as text
|
||||
* @param {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} 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} 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) {
|
||||
// Default functions
|
||||
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
|
||||
// Drawing the label
|
||||
let offset
|
||||
if(!forceText && Latex.enabled) { // TODO: Check for user setting with Latex.
|
||||
if(!forceText && Latex.enabled) {
|
||||
// With latex
|
||||
let drawLblCb = function(canvas, ctx, 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
|
||||
* 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 false when passed to canExecute.
|
||||
*/
|
||||
class ExecutableObject extends DrawableObject {
|
||||
export class ExecutableObject extends DrawableObject {
|
||||
/**
|
||||
* Returns the corresponding y value for an x value.
|
||||
* 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.
|
||||
*
|
||||
* @param {number} x
|
||||
* @returns {bool}
|
||||
* @returns {boolean}
|
||||
*/
|
||||
canExecute(x = 1) {return true}
|
||||
/**
|
||||
* Returns the simplified expression string for a given x.
|
||||
*
|
||||
* @param {number} x
|
||||
* @returns {bool}
|
||||
* @returns {string}
|
||||
*/
|
||||
simplify(x = 1) {return '0'}
|
||||
|
||||
|
||||
/**
|
||||
* True if this object can be executed, so that an y value might be computed
|
||||
* for an x value. Only ExecutableObjects have that property set to true.
|
||||
* @return {bool}
|
||||
* @return {boolean}
|
||||
*/
|
||||
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.")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -16,16 +16,14 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as Common
|
||||
.import "../utils.js" as Utils
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
.import "../math/latex.js" as Latex
|
||||
import { textsub } from "../utils.mjs"
|
||||
import { API as Common, ExecutableObject } from "common.mjs"
|
||||
import { parseDomain, Expression, SpecialDomain } from "../mathlib.mjs"
|
||||
import * as P from "../parameters.mjs"
|
||||
import Latex from "../math/latex.mjs"
|
||||
|
||||
|
||||
class Function extends Common.ExecutableObject {
|
||||
export default class Function extends ExecutableObject {
|
||||
static type(){return 'Function'}
|
||||
static displayType(){return qsTr('Function')}
|
||||
static displayTypeMultiple(){return qsTr('Functions')}
|
||||
|
@ -54,11 +52,11 @@ class Function extends Common.ExecutableObject {
|
|||
drawPoints = true, drawDashedLines = true) {
|
||||
if(name == null) name = Common.getNewName('fghjqlmnopqrstuvwabcde')
|
||||
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
|
||||
if(typeof definitionDomain == 'string') definitionDomain = MathLib.parseDomain(definitionDomain)
|
||||
if(typeof definitionDomain == 'string') definitionDomain = parseDomain(definitionDomain)
|
||||
this.definitionDomain = definitionDomain
|
||||
if(typeof destinationDomain == 'string') destinationDomain = MathLib.parseDomain(destinationDomain)
|
||||
if(typeof destinationDomain == 'string') destinationDomain = parseDomain(destinationDomain)
|
||||
this.destinationDomain = destinationDomain
|
||||
this.displayMode = displayMode
|
||||
this.labelPosition = labelPosition
|
||||
|
@ -68,15 +66,15 @@ class Function extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
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()}`
|
||||
} 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() {
|
||||
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}\\\\
|
||||
x\\textrm{ } & \\mapsto & \\textrm{ }${this.expression.latexMarkup}\\end{array}`
|
||||
} else {
|
||||
|
@ -111,14 +109,16 @@ class Function extends Common.ExecutableObject {
|
|||
// Label
|
||||
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) {
|
||||
// Reusable in other objects.
|
||||
// Drawing small traits every 0.2px
|
||||
var pxprecision = 10
|
||||
var previousX = canvas.px2x(0)
|
||||
var previousY = null;
|
||||
if(definitionDomain instanceof MathLib.SpecialDomain && definitionDomain.moveSupported) {
|
||||
let pxprecision = 10
|
||||
let previousX = canvas.px2x(0)
|
||||
let previousY = null;
|
||||
if(definitionDomain instanceof SpecialDomain && definitionDomain.moveSupported) {
|
||||
// Point based functions.
|
||||
previousX = definitionDomain.next(previousX)
|
||||
if(previousX === null) previousX = definitionDomain.next(canvas.px2x(0))
|
||||
|
@ -126,8 +126,8 @@ class Function extends Common.ExecutableObject {
|
|||
if(!drawPoints && !drawDash) return
|
||||
while(previousX !== null && canvas.x2px(previousX) < canvas.canvasSize.width) {
|
||||
// Reconverted for canvas to fix for logarithmic scales.
|
||||
var currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX)+pxprecision));
|
||||
var currentY = expr.execute(currentX)
|
||||
let currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX)+pxprecision));
|
||||
let currentY = expr.execute(currentX)
|
||||
if(currentX === null) break;
|
||||
if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) &&
|
||||
(destinationDomain.includes(currentY) || destinationDomain.includes(previousY))) {
|
||||
|
@ -173,7 +173,7 @@ class Function extends Common.ExecutableObject {
|
|||
do {
|
||||
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...)
|
||||
let maxvariation = (canvas.px2y(0)-canvas.px2y(canvas.canvasSize.height))
|
|
@ -16,19 +16,19 @@
|
|||
* 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 "function.js" as F
|
||||
.import "../objects.js" as Objects
|
||||
.import "../utils.js" as Utils
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../historylib.js" as HistoryLib
|
||||
.import "../parameters.js" as P
|
||||
.import "../math/latex.js" as Latex
|
||||
import { API as Common, ExecutableObject } from "common.mjs"
|
||||
import Function from "function.mjs"
|
||||
|
||||
import { API as HistoryAPI } from "../history/common.mjs"
|
||||
import { CreateNewObject } from "../historylib.mjs"
|
||||
|
||||
|
||||
class GainBode extends Common.ExecutableObject {
|
||||
export default class GainBode extends ExecutableObject {
|
||||
static type(){return 'Gain Bode'}
|
||||
static displayType(){return qsTr('Bode Magnitude')}
|
||||
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',
|
||||
om_0 = '', pass = 'high', gain = '20', labelPosition = 'above', labelX = 1, omGraduation = false) {
|
||||
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)
|
||||
if(typeof om_0 == "string") {
|
||||
// Point name or create one
|
||||
|
@ -52,7 +52,7 @@ class GainBode extends Common.ExecutableObject {
|
|||
if(om_0 == null) {
|
||||
// Create new point
|
||||
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()
|
||||
labelPosition = 'below'
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ class GainBode extends Common.ExecutableObject {
|
|||
}
|
||||
this.om_0 = om_0
|
||||
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.labelPosition = labelPosition
|
||||
this.labelX = labelX
|
||||
|
@ -68,12 +68,12 @@ class GainBode extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
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`
|
||||
}
|
||||
|
||||
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}
|
||||
\\textsf{${pass}};${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup} \\\\
|
||||
${this.gain.latexMarkup}\\textsf{ dB/dec}
|
||||
|
@ -86,9 +86,9 @@ class GainBode extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
execute(x=1) {
|
||||
if(typeof x == 'string') x = MathLib.executeExpression(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}`)
|
||||
if(typeof x == 'string') x = executeExpression(x)
|
||||
if((this.pass === 'high' && x < this.om_0.x) || (this.pass === 'low' && x > this.om_0.x)) {
|
||||
let dbfn = new Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
|
||||
return dbfn.execute(x)
|
||||
} else {
|
||||
return this.om_0.y.execute()
|
||||
|
@ -96,10 +96,10 @@ class GainBode extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
simplify(x = 1) {
|
||||
var xval = x
|
||||
if(typeof x == 'string') xval = MathLib.executeExpression(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 xval = 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)) {
|
||||
let dbfn = new Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
|
||||
return dbfn.simplify(x)
|
||||
} else {
|
||||
return this.om_0.y.toString()
|
||||
|
@ -111,20 +111,20 @@ class GainBode extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
draw(canvas, ctx) {
|
||||
var 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}`)
|
||||
var inDrawDom = new MathLib.EmptySet()
|
||||
let base = [canvas.x2px(this.om_0.x), canvas.y2px(this.om_0.y)]
|
||||
let dbfn = new Expression(`${this.gain.execute()}*(log10(x)-log10(${this.om_0.x}))+${this.om_0.y}`)
|
||||
let inDrawDom = new EmptySet()
|
||||
|
||||
if(this.pass == 'high') {
|
||||
// High pass, linear line from begining, then constant to the end.
|
||||
if(this.pass === 'high') {
|
||||
// High pass, linear line from beginning, then constant to the end.
|
||||
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 {
|
||||
// Low pass, constant from the beginning, linear line to the end.
|
||||
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
|
||||
var xpos = canvas.x2px(this.om_0.x.execute())
|
||||
var dashPxSize = 10
|
||||
|
@ -137,7 +137,7 @@ class GainBode extends Common.ExecutableObject {
|
|||
|
||||
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()
|
||||
} else {
|
||||
Objects.createNewRegisteredObject('Somme gains Bode')
|
|
@ -16,17 +16,17 @@
|
|||
* 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 "../objects.js" as Objects
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../historylib.js" as HistoryLib
|
||||
.import "../parameters.js" as P
|
||||
.import "../math/latex.js" as Latex
|
||||
import { API as Common, ExecutableObject } from "common.mjs"
|
||||
import { API as HistoryAPI } from "../history/common.mjs"
|
||||
import { CreateNewObject } from "../historylib.mjs"
|
||||
|
||||
|
||||
class PhaseBode extends Common.ExecutableObject {
|
||||
export default class PhaseBode extends ExecutableObject {
|
||||
static type(){return 'Phase Bode'}
|
||||
static displayType(){return qsTr('Bode Phase')}
|
||||
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',
|
||||
om_0 = '', phase = 90, unit = '°', labelPosition = 'above', labelX = 1) {
|
||||
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)
|
||||
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
|
||||
if(typeof om_0 == "string") {
|
||||
// Point name or create one
|
||||
|
@ -52,7 +52,7 @@ class PhaseBode extends Common.ExecutableObject {
|
|||
// Create new point
|
||||
om_0 = Objects.createNewRegisteredObject('Point', [Common.getNewName('ω'), this.color, 'name'])
|
||||
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'
|
||||
}
|
||||
om_0.requiredBy.push(this)
|
||||
|
@ -77,7 +77,7 @@ class PhaseBode extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
execute(x=1) {
|
||||
if(typeof x == 'string') x = MathLib.executeExpression(x)
|
||||
if(typeof x == 'string') x = executeExpression(x)
|
||||
if(x < this.om_0.x) {
|
||||
return this.om_0.y.execute()
|
||||
} else {
|
||||
|
@ -86,13 +86,13 @@ class PhaseBode extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
simplify(x = 1) {
|
||||
var xval = x
|
||||
if(typeof x == 'string') xval = MathLib.executeExpression(x)
|
||||
let xval = x
|
||||
if(typeof x == 'string') xval = executeExpression(x)
|
||||
if(xval < this.om_0.x) {
|
||||
return this.om_0.y.toString()
|
||||
} else {
|
||||
var newExp = this.om_0.y.toEditableString() + ' + ' + this.phase.toEditableString()
|
||||
return (new MathLib.Expression(newExp)).toString()
|
||||
let newExp = this.om_0.y.toEditableString() + ' + ' + this.phase.toEditableString()
|
||||
return (new Expression(newExp)).toString()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,11 +101,11 @@ class PhaseBode extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
draw(canvas, ctx) {
|
||||
var baseX = canvas.x2px(this.om_0.x.execute())
|
||||
var omy = this.om_0.y.execute()
|
||||
var augmt = this.phase.execute()
|
||||
var baseY = canvas.y2px(omy)
|
||||
var augmtY = canvas.y2px(omy+augmt)
|
||||
let baseX = canvas.x2px(this.om_0.x.execute())
|
||||
let omy = this.om_0.y.execute()
|
||||
let augmt = this.phase.execute()
|
||||
let baseY = canvas.y2px(omy)
|
||||
let augmtY = canvas.y2px(omy+augmt)
|
||||
// Before change line.
|
||||
canvas.drawLine(ctx, 0, baseY, Math.min(baseX, canvas.canvasSize.height), baseY)
|
||||
// Transition line.
|
||||
|
@ -118,7 +118,7 @@ class PhaseBode extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
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()
|
||||
} else {
|
||||
Objects.createNewRegisteredObject('Somme phases Bode')
|
|
@ -16,15 +16,13 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as Common
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
.import "../math/latex.js" as Latex
|
||||
import { Expression } from "../mathlib.mjs"
|
||||
import * as P from "../parameters.mjs"
|
||||
import Latex from "../math/latex.mjs"
|
||||
import { API as Common, DrawableObject } from "common.mjs"
|
||||
|
||||
|
||||
class Point extends Common.DrawableObject {
|
||||
export default class Point extends DrawableObject {
|
||||
static type(){return 'Point'}
|
||||
static displayType(){return qsTr('Point')}
|
||||
static displayTypeMultiple(){return qsTr('Points')}
|
||||
|
@ -40,9 +38,9 @@ class Point extends Common.DrawableObject {
|
|||
x = 1, y = 0, labelPosition = 'above', pointStyle = '●') {
|
||||
if(name == null) name = Common.getNewName('ABCDEFJKLMNOPQRSTUVW')
|
||||
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
|
||||
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.labelPosition = labelPosition
|
||||
this.pointStyle = pointStyle
|
|
@ -16,14 +16,12 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as Common
|
||||
.import "../parameters.js" as P
|
||||
.import "../math/latex.js" as Latex
|
||||
import { API as Common, ExecutableObject } from "common.mjs"
|
||||
import * as P from "../parameters.mjs"
|
||||
import Latex from "../math/latex.mjs"
|
||||
|
||||
|
||||
class RepartitionFunction extends Common.ExecutableObject {
|
||||
export default class RepartitionFunction extends ExecutableObject {
|
||||
static type(){return 'Repartition'}
|
||||
static displayType(){return qsTr('Repartition')}
|
||||
static displayTypeMultiple(){return qsTr('Repartition functions')}
|
||||
|
@ -34,7 +32,7 @@ class RepartitionFunction extends Common.ExecutableObject {
|
|||
'comment',
|
||||
'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',
|
||||
|
@ -69,7 +67,7 @@ class RepartitionFunction extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
execute(x = 1) {
|
||||
var ret = 0;
|
||||
let ret = 0;
|
||||
Object.keys(this.probabilities).sort((a,b) => a-b).forEach(idx => {
|
||||
if(x >= idx) ret += parseFloat(this.probabilities[idx].replace(/,/g, '.'))
|
||||
})
|
||||
|
@ -77,6 +75,7 @@ class RepartitionFunction extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
canExecute(x = 1) {return true}
|
||||
|
||||
// Simplify returns the simplified string of the expression.
|
||||
simplify(x = 1) {
|
||||
return this.execute(x)
|
||||
|
@ -94,8 +93,8 @@ class RepartitionFunction extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
draw(canvas, ctx) {
|
||||
var currentY = 0;
|
||||
var keys = Object.keys(this.probabilities).map(idx => parseInt(idx)).sort((a,b) => a-b)
|
||||
let currentY = 0;
|
||||
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, '.'))) {
|
||||
canvas.drawLine(ctx,
|
||||
0,
|
||||
|
@ -109,8 +108,8 @@ class RepartitionFunction extends Common.ExecutableObject {
|
|||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < keys.length-1; i++) {
|
||||
var idx = keys[i];
|
||||
for(let i = 0; i < keys.length-1; i++) {
|
||||
let idx = keys[i];
|
||||
currentY += parseFloat(this.probabilities[idx].replace(/,/g, '.'));
|
||||
if(canvas.isVisible(idx,currentY) || canvas.isVisible(keys[i+1],currentY)) {
|
||||
canvas.drawLine(ctx,
|
|
@ -16,16 +16,15 @@
|
|||
* 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 "function.js" as F
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
.import "../math/latex.js" as Latex
|
||||
import { API as Common, ExecutableObject } from "common.mjs"
|
||||
import Function from "function.mjs"
|
||||
|
||||
|
||||
class Sequence extends Common.ExecutableObject {
|
||||
export default class Sequence extends ExecutableObject {
|
||||
static type(){return 'Sequence'}
|
||||
static displayType(){return qsTr('Sequence')}
|
||||
static displayTypeMultiple(){return qsTr('Sequences')}
|
||||
|
@ -59,18 +58,21 @@ class Sequence extends Common.ExecutableObject {
|
|||
|
||||
export() {
|
||||
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() {
|
||||
console.log('Updating sequence', this.sequence)
|
||||
console.trace()
|
||||
super.update()
|
||||
if(
|
||||
this.sequence == null || this.baseValues != this.sequence.baseValues ||
|
||||
this.sequence.name != this.name ||
|
||||
this.sequence.expr != Object.values(this.defaultExpression)[0] ||
|
||||
this.sequence.valuePlus != Object.keys(this.defaultExpression)[0]
|
||||
this.sequence == null || this.baseValues !== this.sequence.baseValues ||
|
||||
this.sequence.name !== this.name ||
|
||||
this.sequence.expr !== Object.values(this.defaultExpression)[0] ||
|
||||
this.sequence.valuePlus !== Object.keys(this.defaultExpression)[0]
|
||||
)
|
||||
this.sequence = new MathLib.Sequence(
|
||||
this.sequence = new MathSequence(
|
||||
this.name, this.baseValues,
|
||||
Object.keys(this.defaultExpression)[0],
|
||||
Object.values(this.defaultExpression)[0]
|
||||
|
@ -86,15 +88,15 @@ class Sequence extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
execute(x = 1) {
|
||||
if(x % 1 == 0)
|
||||
if(x % 1 === 0)
|
||||
return this.sequence.execute(x)
|
||||
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(x = 1) {
|
||||
if(x % 1 == 0)
|
||||
if(x % 1 === 0)
|
||||
return this.sequence.simplify(x)
|
||||
return null
|
||||
}
|
||||
|
@ -122,7 +124,7 @@ class Sequence extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
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
|
||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
|
@ -16,17 +16,16 @@
|
|||
* 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 "function.js" as F
|
||||
.import "../objects.js" as Objects
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
.import "../math/latex.js" as Latex
|
||||
import { ExecutableObject } from "common.mjs"
|
||||
import Function from "function.mjs"
|
||||
|
||||
|
||||
class SommeGainsBode extends Common.ExecutableObject {
|
||||
export default class SommeGainsBode extends ExecutableObject {
|
||||
static type(){return 'Somme gains Bode'}
|
||||
static displayType(){return qsTr('Bode Magnitudes Sum')}
|
||||
static displayTypeMultiple(){return qsTr('Bode Magnitudes Sum')}
|
||||
|
@ -58,7 +57,7 @@ class SommeGainsBode extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
execute(x = 0) {
|
||||
for(var [dbfn, inDrawDom] of this.cachedParts) {
|
||||
for(let [dbfn, inDrawDom] of this.cachedParts) {
|
||||
if(inDrawDom.includes(x)) {
|
||||
return dbfn.execute(x)
|
||||
}
|
||||
|
@ -71,7 +70,7 @@ class SommeGainsBode extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
simplify(x = 1) {
|
||||
for(var [dbfn, inDrawDom] of this.cachedParts) {
|
||||
for(let [dbfn, inDrawDom] of this.cachedParts) {
|
||||
if(inDrawDom.includes(x)) {
|
||||
return dbfn.simplify(x)
|
||||
}
|
||||
|
@ -82,33 +81,33 @@ class SommeGainsBode extends Common.ExecutableObject {
|
|||
recalculateCache() {
|
||||
this.cachedParts = []
|
||||
// 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')
|
||||
// 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
|
||||
var om0xGains = {1000000000: 0} // To draw the last part
|
||||
var om0xPass = {1000000000: 'high'} // To draw the last part
|
||||
let baseY = 0
|
||||
let om0xGains = {1000000000: 0} // 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.
|
||||
var om0x = gainObj.om_0.x.execute()
|
||||
if(om0xGains[om0x] == undefined) {
|
||||
let om0x = gainObj.om_0.x.execute()
|
||||
if(om0xGains[om0x] === undefined) {
|
||||
om0xGains[om0x] = gainObj.gain.execute()
|
||||
om0xPass[om0x] = gainObj.pass == 'high'
|
||||
om0xPass[om0x] = gainObj.pass === 'high'
|
||||
} else {
|
||||
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)
|
||||
})
|
||||
// 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)
|
||||
// Adding the total gains.
|
||||
var gainsBeforeP = []
|
||||
var gainsAfterP = []
|
||||
var gainTotal = 0
|
||||
for(var om0x of om0xList){
|
||||
let gainsBeforeP = []
|
||||
let gainsAfterP = []
|
||||
let gainTotal = 0
|
||||
for(let om0x of om0xList){
|
||||
if(om0xPass[om0x]) { // High-pass
|
||||
gainsBeforeP.push(om0xGains[om0x])
|
||||
gainsAfterP.push(0)
|
||||
|
@ -119,10 +118,10 @@ class SommeGainsBode extends Common.ExecutableObject {
|
|||
}
|
||||
}
|
||||
// Calculating parts
|
||||
var previousPallier = drawMin
|
||||
for(var pallier = 0; pallier < om0xList.length; pallier++) {
|
||||
var dbfn = new MathLib.Expression(`${gainTotal}*(ln(x)-ln(${previousPallier}))/ln(10)+${baseY}`)
|
||||
var inDrawDom = MathLib.parseDomain(`]${previousPallier};${om0xList[pallier]}]`)
|
||||
let previousPallier = drawMin
|
||||
for(let pallier = 0; pallier < om0xList.length; pallier++) {
|
||||
let dbfn = new Expression(`${gainTotal}*(ln(x)-ln(${previousPallier}))/ln(10)+${baseY}`)
|
||||
let inDrawDom = parseDomain(`]${previousPallier};${om0xList[pallier]}]`)
|
||||
this.cachedParts.push([dbfn, inDrawDom])
|
||||
previousPallier = om0xList[pallier]
|
||||
baseY = dbfn.execute(om0xList[pallier])
|
||||
|
@ -133,8 +132,8 @@ class SommeGainsBode extends Common.ExecutableObject {
|
|||
|
||||
draw(canvas, ctx) {
|
||||
if(this.cachedParts.length > 0) {
|
||||
for(var [dbfn, inDrawDom] of this.cachedParts) {
|
||||
F.Function.drawFunction(canvas, ctx, dbfn, inDrawDom, MathLib.Domain.R)
|
||||
for(let [dbfn, inDrawDom] of this.cachedParts) {
|
||||
Function.drawFunction(canvas, ctx, dbfn, inDrawDom, Domain.R)
|
||||
if(inDrawDom.includes(this.labelX)) {
|
||||
// Label
|
||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
|
@ -16,16 +16,14 @@
|
|||
* 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 "../objects.js" as Objects
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
.import "../math/latex.js" as Latex
|
||||
import { ExecutableObject } from "common.mjs"
|
||||
|
||||
|
||||
class SommePhasesBode extends Common.ExecutableObject {
|
||||
export default class SommePhasesBode extends ExecutableObject {
|
||||
static type(){return 'Somme phases Bode'}
|
||||
static displayType(){return qsTr('Bode Phases Sum')}
|
||||
static displayTypeMultiple(){return qsTr('Bode Phases Sum')}
|
||||
|
@ -57,18 +55,18 @@ class SommePhasesBode extends Common.ExecutableObject {
|
|||
}
|
||||
|
||||
execute(x=1) {
|
||||
if(typeof x == 'string') x = MathLib.executeExpression(x)
|
||||
for(var i = 0; i < this.om0xList.length-1; i++) {
|
||||
if(typeof x == 'string') x = executeExpression(x)
|
||||
for(let i = 0; i < this.om0xList.length-1; i++) {
|
||||
if(x >= this.om0xList[i] && x < this.om0xList[i+1]) return this.phasesList[i]
|
||||
}
|
||||
}
|
||||
|
||||
simplify(x = 1) {
|
||||
var xval = x
|
||||
if(typeof x == 'string') xval = MathLib.executeExpression(x)
|
||||
for(var i = 0; i < this.om0xList.length-1; i++) {
|
||||
let xval = x
|
||||
if(typeof x == 'string') xval = executeExpression(x)
|
||||
for(let i = 0; i < this.om0xList.length-1; i++) {
|
||||
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'
|
||||
|
@ -80,20 +78,20 @@ class SommePhasesBode extends Common.ExecutableObject {
|
|||
|
||||
recalculateCache() {
|
||||
// Minimum to draw (can be expended if needed, just not infinite or it'll cause issues.
|
||||
var drawMin = 0.001
|
||||
var drawMax = 100000
|
||||
let drawMin = 0.001
|
||||
let drawMax = 100000
|
||||
this.om0xList = [drawMin, drawMax]
|
||||
this.phasesList = [0]
|
||||
this.phasesExprList = ['0']
|
||||
var phasesDict = {}
|
||||
var phasesExprDict = {}
|
||||
let phasesDict = {}
|
||||
let phasesExprDict = {}
|
||||
phasesDict[drawMax] = 0
|
||||
|
||||
if(Objects.currentObjects['Phase Bode'] != undefined) {
|
||||
if(Objects.currentObjects['Phase Bode'] !== undefined) {
|
||||
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())
|
||||
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()
|
||||
phasesExprDict[obj.om_0.x.execute()] = obj.phase.toEditableString()
|
||||
} else {
|
||||
|
@ -104,8 +102,8 @@ class SommePhasesBode extends Common.ExecutableObject {
|
|||
this.phasesExprList[0] += '+' + obj.om_0.y.toEditableString()
|
||||
}
|
||||
this.om0xList.sort((a,b) => a - b)
|
||||
var totalAdded = this.phasesList[0]
|
||||
for(var i = 1; i < this.om0xList.length; i++) {
|
||||
let totalAdded = this.phasesList[0]
|
||||
for(let i = 1; i < this.om0xList.length; i++) {
|
||||
this.phasesList[i] = this.phasesList[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) {
|
||||
for(var i = 0; i < this.om0xList.length-1; i++) {
|
||||
var om0xBegin = canvas.x2px(this.om0xList[i])
|
||||
var om0xEnd = canvas.x2px(this.om0xList[i+1])
|
||||
var baseY = canvas.y2px(this.phasesList[i])
|
||||
var nextY = canvas.y2px(this.phasesList[i+1])
|
||||
for(let i = 0; i < this.om0xList.length-1; i++) {
|
||||
let om0xBegin = canvas.x2px(this.om0xList[i])
|
||||
let om0xEnd = canvas.x2px(this.om0xList[i+1])
|
||||
let baseY = canvas.y2px(this.phasesList[i])
|
||||
let nextY = canvas.y2px(this.phasesList[i+1])
|
||||
canvas.drawLine(ctx, om0xBegin, baseY, om0xEnd, baseY)
|
||||
canvas.drawLine(ctx, om0xEnd, baseY, om0xEnd, nextY)
|
||||
}
|
|
@ -16,16 +16,14 @@
|
|||
* 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 "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
.import "../math/latex.js" as Latex
|
||||
import { API as Common, DrawableObject } from "common.mjs"
|
||||
|
||||
|
||||
|
||||
class Text extends Common.DrawableObject {
|
||||
export default class Text extends DrawableObject {
|
||||
static type(){return 'Text'}
|
||||
static displayType(){return qsTr('Text')}
|
||||
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) {
|
||||
if(name == null) name = Common.getNewName('t')
|
||||
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
|
||||
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.labelPosition = labelPosition
|
||||
this.text = text
|
|
@ -16,17 +16,15 @@
|
|||
* 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 "../objects.js" as Objects
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
.import "../math/latex.js" as Latex
|
||||
import { API as Common, DrawableObject } from "common.mjs"
|
||||
|
||||
|
||||
|
||||
class XCursor extends Common.DrawableObject {
|
||||
export default class XCursor extends DrawableObject {
|
||||
static type(){return 'X Cursor'}
|
||||
static displayType(){return qsTr('X Cursor')}
|
||||
static displayTypeMultiple(){return qsTr('X Cursors')}
|
||||
|
@ -51,7 +49,7 @@ class XCursor extends Common.DrawableObject {
|
|||
super(name, visible, color, labelContent)
|
||||
this.approximate = approximate
|
||||
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.targetElement = targetElement
|
||||
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))
|
||||
|
||||
// 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()))
|
||||
this.drawLabel(canvas, ctx, this.labelPosition, xpos, ypos, false,
|
||||
this.getTargetValueLatexLabel.bind(this), this.getTargetValueLabel.bind(this),
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
class Expression {
|
||||
export class Expression {
|
||||
constructor(...variables) {
|
||||
this.type = 'Expression'
|
||||
this.variables = variables
|
||||
|
@ -27,7 +27,7 @@ class Expression {
|
|||
}
|
||||
}
|
||||
|
||||
class Enum {
|
||||
export class Enum {
|
||||
constructor(...values) {
|
||||
this.type = 'Enum'
|
||||
this.values = values
|
||||
|
@ -39,7 +39,7 @@ class Enum {
|
|||
}
|
||||
}
|
||||
|
||||
class ObjectType {
|
||||
export class ObjectType {
|
||||
constructor(objType) {
|
||||
this.type = 'ObjectType'
|
||||
this.objType = objType
|
||||
|
@ -50,7 +50,7 @@ class ObjectType {
|
|||
}
|
||||
}
|
||||
|
||||
class List {
|
||||
export class List {
|
||||
constructor(type, format = /^.+$/, label = '', forbidAdding = false) {
|
||||
// type can be string, int and double.
|
||||
this.type = 'List'
|
||||
|
@ -65,7 +65,7 @@ class List {
|
|||
}
|
||||
}
|
||||
|
||||
class Dictionary {
|
||||
export class Dictionary {
|
||||
constructor(type, keyType = 'string', format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = '', postKeyLabel = ': ', forbidAdding = false) {
|
||||
// type & keyType can be string, int and double.
|
||||
this.type = 'Dict'
|
|
@ -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.
|
||||
|
||||
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.
|
||||
|
|
|
@ -16,9 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
class InputExpression {
|
||||
export default class InputExpression {
|
||||
constructor(expression) {
|
||||
this.position = 0;
|
||||
this.input = expression;
|
||||
|
@ -33,7 +32,7 @@ class InputExpression {
|
|||
}
|
||||
|
||||
skip(char) {
|
||||
if(!this.atEnd() && this.peek() == char) {
|
||||
if(!this.atEnd() && this.peek() === char) {
|
||||
this.position++;
|
||||
} else {
|
||||
this.raise("Unexpected character " + this.peek() + ". Expected character " + char);
|
|
@ -16,19 +16,17 @@
|
|||
* 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
|
||||
.import "tokenizer.js" as TK
|
||||
.import "common.js" as Common
|
||||
export const Input = InputExpression
|
||||
export const TokenType = T.TokenType
|
||||
export const Token = T.Token
|
||||
export const Tokenizer = T.ExpressionTokenizer
|
||||
|
||||
var Input = Common.InputExpression
|
||||
var TokenType = TK.TokenType
|
||||
var Token = TK.Token
|
||||
var Tokenizer = TK.ExpressionTokenizer
|
||||
|
||||
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
|
||||
export const FUNCTIONS_LIST = Reference.FUNCTIONS_LIST
|
||||
export const FUNCTIONS = Reference.FUNCTIONS
|
||||
export const FUNCTIONS_USAGE = Reference.FUNCTIONS_USAGE
|
||||
export const CONSTANTS_LIST = Reference.CONSTANTS_LIST
|
||||
export const CONSTANTS = Reference.CONSTANTS
|
|
@ -17,10 +17,7 @@
|
|||
*/
|
||||
|
||||
// Contains polyfill math functions used for reference.
|
||||
|
||||
.pragma library
|
||||
|
||||
function factorial(x) {
|
||||
export function factorial(x) {
|
||||
if (x < 0) // Integrating by less than 0
|
||||
if(isFinite(n))
|
||||
return Infinity
|
||||
|
@ -43,7 +40,7 @@ let GAMMA_P = [
|
|||
0.36899182659531622704e-5
|
||||
]
|
||||
|
||||
function gamma(n) {
|
||||
export function gamma(n) {
|
||||
if(n <= 0) // Integrating by less than 0
|
||||
if(isFinite(n))
|
||||
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
|
||||
}
|
||||
|
||||
function arrayMap(f, arr) {
|
||||
export function arrayMap(f, arr) {
|
||||
if (typeof f != 'function')
|
||||
throw new EvalError(qsTranslate('error', 'First argument to map is not a function.'))
|
||||
if (!Array.isArray(arr))
|
||||
|
@ -99,7 +96,7 @@ function arrayMap(f, arr) {
|
|||
return arr.map(f)
|
||||
}
|
||||
|
||||
function arrayFold(f, init, arr) {
|
||||
export function arrayFold(f, init, arr) {
|
||||
if (typeof f != 'function')
|
||||
throw new EvalError(qsTranslate('error', 'First argument to fold is not a function.'))
|
||||
if (!Array.isArray(arr))
|
||||
|
@ -107,7 +104,7 @@ function arrayFold(f, init, arr) {
|
|||
return arr.reduce(f, init)
|
||||
}
|
||||
|
||||
function arrayFilter(f, arr) {
|
||||
export function arrayFilter(f, arr) {
|
||||
if (typeof f != 'function')
|
||||
throw new EvalError(qsTranslate('error', 'First argument to filter is not a function.'))
|
||||
if (!Array.isArray(arr))
|
||||
|
@ -115,21 +112,13 @@ function arrayFilter(f, arr) {
|
|||
return arr.filter(f)
|
||||
}
|
||||
|
||||
function arrayFilter(f, 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) {
|
||||
export function arrayJoin(sep, arr) {
|
||||
if (!Array.isArray(arr))
|
||||
throw new Error(qsTranslate('error', 'Second argument to join is not an array.'))
|
||||
return arr.join(sep)
|
||||
}
|
||||
|
||||
function indexOf(target, s) {
|
||||
export function indexOf(target, s) {
|
||||
if (!(Array.isArray(s) || typeof s === 'string'))
|
||||
throw new Error(qsTranslate('error', 'Second argument to indexOf is not a string or array.'))
|
||||
return s.indexOf(target)
|
|
@ -16,12 +16,9 @@
|
|||
* 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
|
||||
|
||||
|
||||
const CONSTANTS = {
|
||||
export const CONSTANTS = {
|
||||
"π": Math.PI,
|
||||
"pi": Math.PI,
|
||||
"inf": Infinity,
|
||||
|
@ -29,9 +26,9 @@ const CONSTANTS = {
|
|||
"∞": Infinity,
|
||||
"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
|
||||
// in the parser, or not to be used for autocompletion.
|
||||
// Unary operators
|
||||
|
@ -94,9 +91,9 @@ const FUNCTIONS = {
|
|||
'integral': () => 0, // TODO: Implement
|
||||
'derivative': () => 0,
|
||||
}
|
||||
const FUNCTIONS_LIST = Object.keys(FUNCTIONS);
|
||||
export const FUNCTIONS_LIST = Object.keys(FUNCTIONS);
|
||||
|
||||
class P {
|
||||
export class P {
|
||||
// Parameter class.
|
||||
constructor(type, name = '', optional = false, multipleAllowed = false) {
|
||||
this.name = name
|
||||
|
@ -107,7 +104,7 @@ class P {
|
|||
|
||||
toString() {
|
||||
let base_string = this.type
|
||||
if(this.name != '')
|
||||
if(this.name !== '')
|
||||
base_string = `${this.name}: ${base_string}`
|
||||
if(this.multipleAllowed)
|
||||
base_string += '...'
|
||||
|
@ -119,12 +116,12 @@ class P {
|
|||
}
|
||||
}
|
||||
|
||||
let string = new P('string')
|
||||
let bool = new P('bool')
|
||||
let number = new P('number')
|
||||
let array = new P('array')
|
||||
export let string = new P('string')
|
||||
export let bool = new P('bool')
|
||||
export let number = new P('number')
|
||||
export let array = new P('array')
|
||||
|
||||
const FUNCTIONS_USAGE = {
|
||||
export const FUNCTIONS_USAGE = {
|
||||
'length': [string],
|
||||
'not': [bool],
|
||||
// Math functions
|
|
@ -16,18 +16,17 @@
|
|||
* 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 STRING_LIMITORS = '"\'`';
|
||||
const STRING_LIMITERS = '"\'`';
|
||||
const OPERATORS = "+-*/^%?:=!><";
|
||||
const PUNCTUTATION = "()[]{},.";
|
||||
const PUNCTUATION = "()[]{},.";
|
||||
const NUMBER_CHARS = "0123456789"
|
||||
const IDENTIFIER_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_₀₁₂₃₄₅₆₇₈₉αβγδεζηθκλμξρςστφχψωₐₑₒₓₔₕₖₗₘₙₚₛₜ"
|
||||
|
||||
var TokenType = {
|
||||
export const TokenType = {
|
||||
// Expression type
|
||||
"WHITESPACE": "WHITESPACE",
|
||||
"VARIABLE": "VARIABLE",
|
||||
|
@ -40,7 +39,7 @@ var TokenType = {
|
|||
"UNKNOWN": "UNKNOWN"
|
||||
}
|
||||
|
||||
class Token {
|
||||
export class Token {
|
||||
constructor(type, value, startPosition) {
|
||||
this.type = type;
|
||||
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) {
|
||||
this.input = input;
|
||||
this.currentToken = null;
|
||||
|
@ -71,12 +76,12 @@ class ExpressionTokenizer {
|
|||
|
||||
readString() {
|
||||
let delimitation = this.input.peek();
|
||||
if(STRING_LIMITORS.includes(delimitation)) {
|
||||
if(STRING_LIMITERS.includes(delimitation)) {
|
||||
this.input.skip(delimitation)
|
||||
let included = "";
|
||||
let justEscaped = false;
|
||||
while(!this.input.atEnd() && (!STRING_LIMITORS.includes(this.input.peek()) || justEscaped)) {
|
||||
justEscaped = this.input.peek() == "\\"
|
||||
while(!this.input.atEnd() && (!STRING_LIMITERS.includes(this.input.peek()) || justEscaped)) {
|
||||
justEscaped = this.input.peek() === "\\"
|
||||
if(!justEscaped)
|
||||
included += this.input.next();
|
||||
}
|
||||
|
@ -92,8 +97,8 @@ class ExpressionTokenizer {
|
|||
readNumber() {
|
||||
let included = "";
|
||||
let hasDot = false;
|
||||
while(!this.input.atEnd() && (NUMBER_CHARS.includes(this.input.peek()) || this.input.peek() == '.')) {
|
||||
if(this.input.peek() == ".") {
|
||||
while(!this.input.atEnd() && (NUMBER_CHARS.includes(this.input.peek()) || this.input.peek() === '.')) {
|
||||
if(this.input.peek() === ".") {
|
||||
if(hasDot) this.input.raise("Unexpected '.'. Expected digit")
|
||||
hasDot = true;
|
||||
}
|
||||
|
@ -130,14 +135,14 @@ class ExpressionTokenizer {
|
|||
if(this.input.atEnd()) return null;
|
||||
let c = this.input.peek();
|
||||
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(IDENTIFIER_CHARS.includes(c.toLowerCase())) return this.readIdentifier();
|
||||
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(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)
|
||||
this.input.throw("Unknown token character " + c)
|
||||
this.input.raise("Unknown token character " + c)
|
||||
else
|
||||
return new Token(TokenType.UNKNOWN, this.input.next(), this.input.position-1);
|
||||
}
|
||||
|
@ -163,7 +168,7 @@ class ExpressionTokenizer {
|
|||
|
||||
skip(type) {
|
||||
let next = this.next();
|
||||
if(next.type != type)
|
||||
input.raise("Unexpected token " + next.type.toLowerCase() + ' "' + next.value + '". Expected ' + type.toLowerCase());
|
||||
if(next.type !== type)
|
||||
this.input.raise("Unexpected token " + next.type.toLowerCase() + ' "' + next.value + '". Expected ' + type.toLowerCase());
|
||||
}
|
||||
}
|
|
@ -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.`)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +1,22 @@
|
|||
/**
|
||||
* 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
|
||||
|
||||
var powerpos = {
|
||||
const powerpos = {
|
||||
"-": "⁻",
|
||||
"+": "⁺",
|
||||
"=": "⁼",
|
||||
|
@ -62,12 +60,13 @@ var powerpos = {
|
|||
"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": "ₓ",
|
||||
}
|
||||
// Put a text in sup position
|
||||
function textsup(text) {
|
||||
var ret = ""
|
||||
export function textsup(text) {
|
||||
let ret = ""
|
||||
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) {
|
||||
ret += powerpos[text[i]]
|
||||
} else {
|
||||
|
@ -117,10 +116,10 @@ function textsup(text) {
|
|||
}
|
||||
|
||||
// Put a text in sub position
|
||||
function textsub(text) {
|
||||
var ret = ""
|
||||
export function textsub(text) {
|
||||
let ret = ""
|
||||
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) {
|
||||
ret += indicepos[text[i]]
|
||||
} else {
|
||||
|
@ -130,8 +129,13 @@ function textsub(text) {
|
|||
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.
|
||||
// [// Decomposition way 2
|
||||
// /(^|[+-] |\()([-.\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 +).
|
||||
// Putting all n in form of number
|
||||
//n2 = n2 == undefined ? 1 : parseFloat(n)
|
||||
n1 = m1 == undefined ? 1 : eval(m1 + '1')
|
||||
n2 = m2 == undefined ? 1 : eval('1' + m2)
|
||||
n3 = m3 == undefined ? 1 : eval(m3 + '1')
|
||||
n4 = m4 == undefined ? 1 : eval('1' + m4)
|
||||
//var [n1, n2, n3, n4] = [n1, n2, n3, n4].map(n => n == undefined ? 1 : parseFloat(n))
|
||||
n1 = m1 === undefined ? 1 : eval(m1 + '1')
|
||||
n2 = m2 === undefined ? 1 : eval('1' + m2)
|
||||
n3 = m3 === undefined ? 1 : eval(m3 + '1')
|
||||
n4 = m4 === undefined ? 1 : eval('1' + m4)
|
||||
//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)
|
||||
var [ope2, ope4] = [ope2, ope4].map(ope => ope == '/' ? '/' : '*')
|
||||
var coeff1 = n1*n2
|
||||
var coeff2 = n3*n4
|
||||
var coefficient = coeff1+coeff2-(opeM == '-' ? 2*coeff2 : 0)
|
||||
[ope2, ope4] = [ope2, ope4].map(ope => ope === '/' ? '/' : '*')
|
||||
let coeff1 = n1*n2
|
||||
let coeff2 = n3*n4
|
||||
let coefficient = coeff1+coeff2-(opeM === '-' ? 2*coeff2 : 0)
|
||||
|
||||
return `${coefficient} * π`
|
||||
}
|
||||
|
@ -172,14 +176,14 @@ function simplifyExpression(str) {
|
|||
function(match, b4, middle, after) {return `${b4}${middle}${after}`}
|
||||
],
|
||||
[ // Removing parenthesis when content is only multiplied.
|
||||
/(^|[*\/-+] |\()\(([^)(+-]+)\)($| [*\/]|\))/g,
|
||||
/(^|[*\/+-] |\()\(([^)(+-]+)\)($| [*\/]|\))/g,
|
||||
function(match, b4, middle, after) {return `${b4}${middle}${after}`}
|
||||
],
|
||||
[// Simplification additions/substractions.
|
||||
/(^|[^*\/] |\()([-.\d]+) (\+|\-) (\([^)(]+\)|[^)(]+) (\+|\-) ([-.\d]+)($| [^*\/]|\))/g,
|
||||
[// Simplification additions/subtractions.
|
||||
/(^|[^*\/] |\()([-.\d]+) [+-] (\([^)(]+\)|[^)(]+) [+-] ([-.\d]+)($| [^*\/]|\))/g,
|
||||
function(match, b4, n1, op1, middle, op2, n2, after) {
|
||||
var total
|
||||
if(op2 == '+') {
|
||||
let total
|
||||
if(op2 === '+') {
|
||||
total = parseFloat(n1) + parseFloat(n2)
|
||||
} else {
|
||||
total = parseFloat(n1) - parseFloat(n2)
|
||||
|
@ -188,14 +192,14 @@ function simplifyExpression(str) {
|
|||
}
|
||||
],
|
||||
[// Simplification multiplications/divisions.
|
||||
/([-.\d]+) (\*|\/) (\([^)(]+\)|[^)(+-]+) (\*|\/) ([-.\d]+)/g,
|
||||
/([-.\d]+) [*\/] (\([^)(]+\)|[^)(+-]+) [*\/] ([-.\d]+)/g,
|
||||
function(match, n1, op1, middle, op2, n2) {
|
||||
if(parseInt(n1) == n1 && parseInt(n2) == n2 && op2 == '/' &&
|
||||
(parseInt(n1) / parseInt(n2)) % 1 != 0) {
|
||||
if(parseInt(n1) === n1 && parseInt(n2) === n2 && op2 === '/' &&
|
||||
(parseInt(n1) / parseInt(n2)) % 1 !== 0) {
|
||||
// Non int result for int division.
|
||||
return `(${n1} / ${n2}) ${op1} ${middle}`
|
||||
} else {
|
||||
if(op2 == '*') {
|
||||
if(op2 === '*') {
|
||||
return `${parseFloat(n1) * parseFloat(n2)} ${op1} ${middle}`
|
||||
} else {
|
||||
return `${parseFloat(n1) / parseFloat(n2)} ${op1} ${middle}`
|
||||
|
@ -206,7 +210,7 @@ function simplifyExpression(str) {
|
|||
[// Starting & ending parenthesis if not needed.
|
||||
/^\s*\((.*)\)\s*$/g,
|
||||
function(match, middle) {
|
||||
var str = middle
|
||||
let str = middle
|
||||
// Replace all groups
|
||||
while(/\([^)(]+\)/g.test(str))
|
||||
str = str.replace(/\([^)(]+\)/g, '')
|
||||
|
@ -225,19 +229,19 @@ function simplifyExpression(str) {
|
|||
// [/(\s|^|\()0(\.0+)? \* ([^)(+-]+)/g, '$10'],
|
||||
// [/(\([^)(]\)) \* 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'],
|
||||
// [/ (\*|\/) 1(\.0+)?(\s|$|\))/g, '$3'],
|
||||
// [/ [\*\/] 1(\.0+)?(\s|$|\))/g, '$3'],
|
||||
// [/ (\+|\-) 0(\.0+)?(\s|$|\))/g, '$3'],
|
||||
// [/(^| |\() /g, '$1'],
|
||||
// [/ ($|\))/g, '$1'],
|
||||
]
|
||||
|
||||
// Replacements
|
||||
var found
|
||||
let found
|
||||
do {
|
||||
found = false
|
||||
for(var replacement of replacements)
|
||||
for(let replacement of replacements)
|
||||
while(replacement[0].test(str)) {
|
||||
found = true
|
||||
str = str.replace(replacement[0], replacement[1])
|
||||
|
@ -247,9 +251,15 @@ function simplifyExpression(str) {
|
|||
}
|
||||
|
||||
|
||||
function makeExpressionReadable(str) {
|
||||
var replacements = [
|
||||
// variables
|
||||
/**
|
||||
* Transforms a mathematical expression to make it readable by humans.
|
||||
* NOTE: Will break parsing of expression.
|
||||
* @param {string} str - Expression to parse.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function makeExpressionReadable(str) {
|
||||
let replacements = [
|
||||
// letiables
|
||||
[/pi/g, 'π'],
|
||||
[/Infinity/g, '∞'],
|
||||
[/inf/g, '∞'],
|
||||
|
@ -264,28 +274,35 @@ function makeExpressionReadable(str) {
|
|||
[/(\d|\))×/g, '$1'],
|
||||
//[/×(\d|\()/g, '$1'],
|
||||
[/([^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) {
|
||||
return `∫${textsub(a)}${textsup(b)} ${body} d${by}`
|
||||
} else {
|
||||
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`
|
||||
}]
|
||||
]
|
||||
|
||||
// str = simplifyExpression(str)
|
||||
// Replacements
|
||||
for(var replacement of replacements)
|
||||
for(let replacement of replacements)
|
||||
while(replacement[0].test(str))
|
||||
str = str.replace(replacement[0], replacement[1])
|
||||
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
|
||||
[/([^a-z]|^)al(pha)?([^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()
|
||||
// Replacements
|
||||
for(var replacement of replacements)
|
||||
for(let replacement of replacements)
|
||||
str = str.replace(replacement[0], replacement[1])
|
||||
return str
|
||||
}
|
||||
|
@ -339,21 +356,36 @@ String.prototype.toLatinUppercase = function() {
|
|||
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")
|
||||
}
|
||||
|
||||
function getRandomColor() {
|
||||
var clrs = '0123456789ABCDEF';
|
||||
var color = '#';
|
||||
for(var i = 0; i < 6; i++) {
|
||||
color += clrs[Math.floor(Math.random() * (16-5*(i%2==0)))];
|
||||
/**
|
||||
* Creates a randomized color string.
|
||||
* @returns {string}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
function escapeHTML(str) {
|
||||
/**
|
||||
* Escapes text to html entities.
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
export function escapeHTML(str) {
|
||||
return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>') ;
|
||||
}
|
||||
|
||||
|
@ -364,6 +396,6 @@ function escapeHTML(str) {
|
|||
* @param {string} expression - The expression to replace in.
|
||||
* @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(''))
|
||||
}
|
Loading…
Reference in a new issue