Separating objects.js in their new respective files, adding basic time measurement system.
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Adsooi 2021-08-14 18:57:29 +02:00
parent 87c855e8a1
commit ec800e622d
Signed by: Ad5001
GPG key ID: 7251B1AF90B960F9
20 changed files with 1713 additions and 1378 deletions

View file

@ -16,6 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from time import time
start_time = time()
from PySide2.QtWidgets import QApplication, QFileDialog from PySide2.QtWidgets import QApplication, QFileDialog
from PySide2.QtQml import QQmlApplicationEngine, qmlRegisterType from PySide2.QtQml import QQmlApplicationEngine, qmlRegisterType
from PySide2.QtCore import Qt, QObject, Signal, Slot, Property from PySide2.QtCore import Qt, QObject, Signal, Slot, Property
@ -121,6 +125,9 @@ def run():
"darwin": "default" "darwin": "default"
}[platform] }[platform]
dep_time = time()
print("Loaded dependencies in " + str((dep_time - start_time)*1000) + "ms.")
icon_fallbacks = QIcon.fallbackSearchPaths(); icon_fallbacks = QIcon.fallbackSearchPaths();
icon_fallbacks.append(os.path.realpath(os.path.join(os.getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "icons"))) icon_fallbacks.append(os.path.realpath(os.path.join(os.getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "icons")))
icon_fallbacks.append(os.path.realpath(os.path.join(os.getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "icons", "settings"))) icon_fallbacks.append(os.path.realpath(os.path.join(os.getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "icons", "settings")))
@ -135,6 +142,7 @@ def run():
helper = Helper() helper = Helper()
engine.rootContext().setContextProperty("Helper", helper) engine.rootContext().setContextProperty("Helper", helper)
engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv) engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv)
engine.rootContext().setContextProperty("StartTime", dep_time)
engine.addImportPath(os.path.realpath(os.path.join(os.getcwd(), "qml"))) engine.addImportPath(os.path.realpath(os.path.join(os.getcwd(), "qml")))
engine.load(os.path.realpath(os.path.join(os.getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "LogarithmPlotter.qml"))) engine.load(os.path.realpath(os.path.join(os.getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "LogarithmPlotter.qml")))

View file

@ -133,7 +133,6 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
Objects.history = historyObj HistoryLib.history = historyObj
Objects.HistoryLib = HistoryLib
} }
} }

View file

@ -20,8 +20,11 @@ import QtQml 2.12
import QtQuick.Controls 2.12 import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12
import QtQuick 2.12 import QtQuick 2.12
import eu.ad5001.LogarithmPlotter.ObjectLists 1.0 // Auto loading all objects.
import "js/objs/autoload.js" as ALObjects
import "js/objects.js" as Objects import "js/objects.js" as Objects
import eu.ad5001.LogarithmPlotter.ObjectLists 1.0
ApplicationWindow { ApplicationWindow {
@ -136,9 +139,15 @@ ApplicationWindow {
showxgrad: settings.showxgrad showxgrad: settings.showxgrad
showygrad: settings.showygrad showygrad: settings.showygrad
onPainted: if(TestBuild == true) { property bool firstDrawDone: false
console.log("Plot drawn in canvas, terminating test of build in 100ms.")
testBuildTimer.start() onPainted: if(!firstDrawDone) {
firstDrawDone = true;
console.info("First paint done in " + (new Date().getTime()-(StartTime*1000)) + "ms")
if(TestBuild == true) {
console.log("Plot drawn in canvas, terminating test of build in 100ms.")
testBuildTimer.start()
}
} }
} }
@ -207,7 +216,7 @@ ApplicationWindow {
root.height = data["height"] root.height = data["height"]
root.width = data["width"] root.width = data["width"]
// Importing objectw // Importing objects
Objects.currentObjects = {} Objects.currentObjects = {}
for(var objType in data['objects']) { for(var objType in data['objects']) {
if(Object.keys(Objects.types).indexOf(objType) > -1) { if(Object.keys(Objects.types).indexOf(objType) > -1) {

View file

@ -21,6 +21,7 @@ import QtQuick.Controls 2.12
import QtQuick.Dialogs 1.3 as D import QtQuick.Dialogs 1.3 as D
import eu.ad5001.LogarithmPlotter 1.0 import eu.ad5001.LogarithmPlotter 1.0
import "../js/objects.js" as Objects import "../js/objects.js" as Objects
import "../js/objs/common.js" as ObjectsCommons
import "../js/historylib.js" as HistoryLib import "../js/historylib.js" as HistoryLib
import "../js/utils.js" as Utils import "../js/utils.js" as Utils
import "../js/mathlib.js" as MathLib import "../js/mathlib.js" as MathLib
@ -65,7 +66,7 @@ D.Dialog {
if(newName != '' && objEditor.obj.name != newName) { if(newName != '' && objEditor.obj.name != newName) {
if(Objects.getObjectByName(newName) != null) { if(Objects.getObjectByName(newName) != null) {
console.log(Objects.getObjectByName(newName).name, newName) console.log(Objects.getObjectByName(newName).name, newName)
newName = Objects.getNewName(newName) newName = ObjectsCommons.getNewName(newName)
} }
history.addToHistory(new HistoryLib.NameChanged( history.addToHistory(new HistoryLib.NameChanged(
objEditor.obj.name, objEditor.objType, newName objEditor.obj.name, objEditor.objType, newName

View file

@ -41,12 +41,12 @@ Column {
Button { Button {
id: createBtn id: createBtn
text: modelData text: Objects.types[modelData].displayType()
width: parent.width/3 width: parent.width/3
visible: Objects.types[modelData].createable() visible: Objects.types[modelData].createable()
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
display: AbstractButton.TextUnderIcon display: AbstractButton.TextUnderIcon
icon.name: Objects.types[modelData].displayType() icon.name: modelData
icon.width: 24 icon.width: 24
icon.height: 24 icon.height: 24
icon.color: sysPalette.windowText icon.color: sysPalette.windowText

View file

@ -24,6 +24,8 @@
.import "utils.js" as Utils .import "utils.js" as Utils
.import "mathlib.js" as MathLib .import "mathlib.js" as MathLib
var history = null;
class Action { class Action {
// Type of the action done. // Type of the action done.
type(){return 'Unknown'} type(){return 'Unknown'}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
/**
* LogarithmPlotter - Create graphs with logarithm scales.
* Copyright (C) 2021 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 "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)

View file

@ -0,0 +1,139 @@
/**
* LogarithmPlotter - Create graphs with logarithm scales.
* Copyright (C) 2021 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 "../objects.js" as Objects
// This file contains the default data to be imported from all other objects
function getNewName(allowedLetters) {
// Allows to get a new name, based on the allowed letters,
// as well as adding a sub number when needs be.
var newid = 0
var ret
do {
var letter = allowedLetters[newid % allowedLetters.length]
var num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length)
ret = letter + (num > 0 ? Utils.textsub(num-1) : '')
newid += 1
} while(Objects.getObjectByName(ret) != null)
return ret
}
class DrawableObject {
// Class to extend for every type of object that
// can be drawn on the canvas.
// Base name of the object. Needs to be constant over time.
static type(){return 'Unknown'}
// (Potentially translated) name of the object to be shown to the user.
static displayType(){return 'Unknown'}
// Label used for the list on the ObjectsList sidebar.
static displayTypeMultiple(){return 'Unknowns'}
// Whether this object can be created by the user
// or are instantiated by other objects.
static createable() {return true}
// Properties are set with key as property name and
// value as it's type name (e.g 'Expression', 'string'...),
// an Enum for enumerations, an ObjectType for DrawableObjects
// with a specific type, a List instance for lists, a
// Dictionary instance for dictionaries...
// Used for property modifier in the sidebar.
static properties() {return {}}
// Whether the object can be executed (instance of ExecutableObject)
static executable() {return false}
constructor(name, visible = true, color = null, labelContent = 'name + value') {
if(color == null) color = Utils.getRandomColor()
this.type = 'Unknown'
this.name = name
this.visible = visible
this.color = color
this.labelContent = labelContent // "null", "name", "name + value"
this.requiredBy = []
}
export() {
// Should return what will be inputed as arguments when a file is loaded (serializable form)
return [this.name, this.visible, this.color.toString(), this.labelContent]
}
getReadableString() {
return `${this.name} = Unknown`
}
getLabel() {
switch(this.labelContent) {
case 'name':
return this.name
case 'name + value':
return this.getReadableString()
case 'null':
return ''
}
}
update() {
for(var req of this.requiredBy) {
req.update()
}
}
delete() {
for(var toRemove of this.requiredBy) {
toRemove.delete()
currentObjects[toRemove.type] = currentObjects[toRemove.type].filter(obj => obj.name != toRemove.name)
}
}
draw(canvas, ctx) {}
}
class ExecutableObject extends DrawableObject {
// Class to be extended for every class upon which we
// calculate an y for a x with the execute function.
// If a value cannot be found during execute, it should
// return null. However, theses values should
// return false when passed to canExecute.
execute(x = 1) {return 0}
canExecute(x = 1) {return true}
// Simplify returns the simplified string of the expression.
simplify(x = 1) {return '0'}
// Whether the object can be executed (instance of ExecutableObject)
static executable() {return true}
}
function registerObject(obj) {
// Registers an object to be used in LogarithmPlotter.
// This function is called from autoload.js
if(obj.prototype instanceof DrawableObject) {
Objects.types[obj.type()] = obj
} else {
console.error("Could not register object " + (obj.type()) + ", as it isn't a DrawableObject.")
}
}

View file

@ -0,0 +1,178 @@
/**
* LogarithmPlotter - Create graphs with logarithm scales.
* Copyright (C) 2021 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 "common.js" as Common
.import "../utils.js" as Utils
.import "../mathlib.js" as MathLib
.import "../parameters.js" as P
class Function extends Common.ExecutableObject {
static type(){return 'Function'}
static displayType(){return 'Function'}
static displayTypeMultiple(){return 'Functions'}
static properties() {return {
'expression': 'Expression',
'definitionDomain': 'Domain',
'destinationDomain': 'Domain',
'comment1': 'Ex: R+* (ℝ⁺*), N (), Z-* (ℤ⁻*), ]0;1[, {3;4;5}',
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
'displayMode': new P.Enum('application', 'function'),
'labelX': 'number',
'comment2': 'The following parameters are used when the definition domain is a non-continuous set. (Ex: , , sets like {0;3}...)',
'drawPoints': 'boolean',
'drawDashedLines': 'boolean'
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
expression = 'x', definitionDomain = 'RPE', destinationDomain = 'R',
displayMode = 'application', labelPosition = 'above', labelX = 1,
drawPoints = true, drawDashedLines = true) {
if(name == null) name = Common.getNewName('fghjqlmnopqrstuvwabcde')
super(name, visible, color, labelContent)
this.type = 'Function'
if(typeof expression == 'number' || typeof expression == 'string') expression = new MathLib.Expression(expression.toString())
this.expression = expression
if(typeof definitionDomain == 'string') definitionDomain = MathLib.parseDomain(definitionDomain)
this.definitionDomain = definitionDomain
if(typeof destinationDomain == 'string') destinationDomain = MathLib.parseDomain(destinationDomain)
this.destinationDomain = destinationDomain
this.displayMode = displayMode
this.labelPosition = labelPosition
this.labelX = labelX
this.drawPoints = drawPoints
this.drawDashedLines = drawDashedLines
}
getReadableString() {
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}`
}
}
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent,
this.expression.toEditableString(), this.definitionDomain.toString(), this.destinationDomain.toString(),
this.displayMode, this.labelPosition, this.labelX, this.drawPoints, this.drawDashedLines]
}
execute(x = 1) {
if(this.definitionDomain.includes(x))
return this.expression.execute(x)
return null
}
canExecute(x = 1) {
return this.definitionDomain.includes(x)
}
simplify(x = 1) {
if(this.definitionDomain.includes(x))
return this.expression.simplify(x)
return ''
}
draw(canvas, ctx) {
Function.drawFunction(canvas, ctx, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines)
// Label
var text = this.getLabel()
ctx.font = `${canvas.textsize}px sans-serif`
var textSize = canvas.measureText(ctx, text)
var posX = canvas.x2px(this.labelX)
var posY = canvas.y2px(this.execute(this.labelX))
switch(this.labelPosition) {
case 'above':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
break;
case 'below':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
break;
case 'left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
break;
case 'right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
break;
case 'above-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
break;
case 'above-right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
break;
case 'below-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
break;
case 'below-right':
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
break;
}
}
static drawFunction(canvas, ctx, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) {
// Reusable in other objects.
// Drawing small traits every 2px
var pxprecision = 2
var previousX = canvas.px2x(0)
var previousY;
if(definitionDomain instanceof MathLib.SpecialDomain && definitionDomain.moveSupported) {
// Point based functions.
previousX = definitionDomain.previous(previousX)
if(previousX === null) previousX = definitionDomain.next(canvas.px2x(0))
previousY = expr.execute(previousX)
if(!drawPoints && !drawDash) return
while(previousX !== null && canvas.x2px(previousX) < canvas.canvasSize.width) {
var currentX = definitionDomain.next(previousX)
var currentY = expr.execute(currentX)
if(currentX === null) break;
if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) &&
(destinationDomain.includes(currentY) || destinationDomain.includes(previousY))) {
if(drawDash)
canvas.drawDashedLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
if(drawPoints) {
ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
}
}
previousX = currentX
previousY = currentY
}
if(drawPoints) {
// Drawing the last cross
ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
}
} else {
previousY = expr.execute(previousX)
for(var px = pxprecision; px < canvas.canvasSize.width; px += pxprecision) {
var currentX = canvas.px2x(px)
var currentY = expr.execute(currentX)
if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) &&
(destinationDomain.includes(currentY) || destinationDomain.includes(previousY)) &&
Math.abs(previousY-currentY)<100) {
canvas.drawLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
}
previousX = currentX
previousY = currentY
}
}
}
}

View file

@ -0,0 +1,167 @@
/**
* LogarithmPlotter - Create graphs with logarithm scales.
* Copyright (C) 2021 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 "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
class GainBode extends Common.ExecutableObject {
static type(){return 'Gain Bode'}
static displayType(){return 'Bode Magnitude'}
static displayTypeMultiple(){return 'Bode Magnitudes'}
static properties() {return {
'om_0': new P.ObjectType('Point'),
'pass': new P.Enum('high', 'low'),
'gain': 'Expression',
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
'labelX': 'number',
'omGraduation': 'boolean'
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
om_0 = '', pass = 'high', gain = '20', labelPosition = 'above', labelX = 1, omGraduation = false) {
if(name == null) name = Common.getNewName('G')
if(name == 'G') name = 'G₀' // G is reserved for sum of BODE magnitudes (Somme gains Bode).
super(name, visible, color, labelContent)
this.type = 'Gain Bode'
if(typeof om_0 == "string") {
// Point name or create one
om_0 = Objects.getObjectByName(om_0, 'Point')
if(om_0 == null) {
// Create new point
om_0 = Objects.createNewRegisteredObject('Point')
om_0.name = Common.getNewName('ω')
om_0.labelContent = 'name'
om_0.color = this.color
HistoryLib.history.addToHistory(new HistoryLib.CreateNewObject(om_0.name, 'Point', om_0.export()))
labelPosition = 'below'
}
om_0.requiredBy.push(this)
}
this.om_0 = om_0
this.pass = pass
if(typeof gain == 'number' || typeof gain == 'string') gain = new MathLib.Expression(gain.toString())
this.gain = gain
this.labelPosition = labelPosition
this.labelX = labelX
this.omGraduation = omGraduation
}
getReadableString() {
return `${this.name}: ${this.pass}-pass; ${this.om_0.name} = ${this.om_0.x}\n ${' '.repeat(this.name.length)}${this.gain.toString(true)} dB/dec`
}
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent,
this.om_0.name, this.pass.toString(), this.gain.toEditableString(), this.labelPosition, this.labelX, this.omGraduation]
}
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}`)
return dbfn.execute(x)
} else {
return this.om_0.y.execute()
}
}
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}`)
return dbfn.simplify(x)
} else {
return this.om_0.y.toString()
}
}
canExecute(x = 1) {
return true
}
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()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
var inDrawDom = new MathLib.EmptySet()
if(this.pass == 'high') {
// High pass, linear line from begining, 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}[`)
} 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[`)
}
F.Function.drawFunction(canvas, ctx, dbfn, inDrawDom, MathLib.Domain.R)
// Dashed line representing break in function
var xpos = canvas.x2px(this.om_0.x.execute())
var dashPxSize = 10
for(var i = 0; i < canvas.canvasSize.height && this.omGraduation; i += dashPxSize*2)
canvas.drawLine(ctx, xpos, i, xpos, i+dashPxSize)
// Label
var text = this.getLabel()
ctx.font = `${canvas.textsize}px sans-serif`
var textSize = canvas.measureText(ctx, text)
var posX = canvas.x2px(this.labelX)
var posY = canvas.y2px(this.execute(this.labelX))
switch(this.labelPosition) {
case 'above':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
break;
case 'below':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
break;
case 'left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
break;
case 'right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
break;
case 'above-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
break;
case 'above-right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
break;
case 'below-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
break;
case 'below-right':
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
break;
}
}
update() {
super.update()
if(Objects.currentObjects['Somme gains Bode'] != undefined) {
Objects.currentObjects['Somme gains Bode'][0].recalculateCache()
} else {
Objects.createNewRegisteredObject('Somme gains Bode')
}
}
}

View file

@ -0,0 +1,157 @@
/**
* LogarithmPlotter - Create graphs with logarithm scales.
* Copyright (C) 2021 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 "common.js" as Common
.import "../objects.js" as Objects
.import "../mathlib.js" as MathLib
.import "../historylib.js" as HistoryLib
.import "../parameters.js" as P
class PhaseBode extends Common.ExecutableObject {
static type(){return 'Phase Bode'}
static displayType(){return 'Bode Phase'}
static displayTypeMultiple(){return 'Bode Phases'}
static properties() {return {
'om_0': new P.ObjectType('Point'),
'phase': 'Expression',
'unit': new P.Enum('°', 'deg', 'rad'),
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
'labelX': 'number'
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
om_0 = '', phase = 90, unit = '°', labelPosition = 'above', labelX = 1) {
if(name == null) name = Common.getNewName('φ')
if(name == 'φ') name = 'φ₀' // φ is reserved for sum of BODE phases (Somme phases Bode).
super(name, visible, color, labelContent)
this.type = 'Phase Bode'
if(typeof phase == 'number' || typeof phase == 'string') phase = new MathLib.Expression(phase.toString())
this.phase = phase
if(typeof om_0 == "string") {
// Point name or create one
om_0 = Objects.getObjectByName(om_0, 'Point')
if(om_0 == null) {
// Create new point
om_0 = Objects.createNewRegisteredObject('Point')
om_0.name = Common.getNewName('ω')
om_0.color = this.color
om_0.labelContent = 'name'
om_0.labelPosition = this.phase.execute() >= 0 ? 'bottom' : 'top'
HistoryLib.history.addToHistory(new HistoryLib.CreateNewObject(om_0.name, 'Point', om_0.export()))
labelPosition = 'below'
}
om_0.requiredBy.push(this)
}
this.om_0 = om_0
this.unit = unit
this.labelPosition = labelPosition
this.labelX = labelX
}
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent,
this.om_0.name, this.phase.toEditableString(), this.unit, this.labelPosition, this.labelX]
}
getReadableString() {
return `${this.name}: ${this.phase.toString(true)}${this.unit} at ${this.om_0.name} = ${this.om_0.x}`
}
execute(x=1) {
if(typeof x == 'string') x = MathLib.executeExpression(x)
if(x < this.om_0.x) {
return this.om_0.y.execute()
} else {
return this.om_0.y.execute() + this.phase.execute()
}
}
simplify(x = 1) {
var xval = x
if(typeof x == 'string') xval = MathLib.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()
}
}
canExecute(x = 1) {
return true
}
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)
// Before change line.
canvas.drawLine(ctx, 0, baseY, Math.min(baseX, canvas.canvasSize.height), baseY)
// Transition line.
canvas.drawLine(ctx, baseX, baseY, baseX, augmtY)
// After change line
canvas.drawLine(ctx, Math.max(0, baseX), augmtY, canvas.canvasSize.width, augmtY)
// Label
var text = this.getLabel()
ctx.font = `${canvas.textsize}px sans-serif`
var textSize = canvas.measureText(ctx, text)
var posX = canvas.x2px(this.labelX)
var posY = canvas.y2px(this.execute(this.labelX))
switch(this.labelPosition) {
case 'above':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
break;
case 'below':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
break;
case 'left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
break;
case 'right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
break;
case 'above-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
break;
case 'above-right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
break;
case 'below-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
break;
case 'below-right':
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
break;
}
}
update() {
if(Objects.currentObjects['Somme phases Bode'] != undefined) {
Objects.currentObjects['Somme phases Bode'][0].recalculateCache()
} else {
Objects.createNewRegisteredObject('Somme phases Bode')
}
}
}

View file

@ -0,0 +1,108 @@
/**
* LogarithmPlotter - Create graphs with logarithm scales.
* Copyright (C) 2021 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 "common.js" as Common
.import "../mathlib.js" as MathLib
.import "../parameters.js" as P
class Point extends Common.DrawableObject {
static type(){return 'Point'}
static displayType(){return 'Point'}
static displayTypeMultiple(){return 'Points'}
static properties() {return {
'x': 'Expression',
'y': 'Expression',
'labelPosition': new P.Enum('top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'),
'pointStyle': new P.Enum('●', '✕', ''),
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
x = 1, y = 0, labelPosition = 'top', pointStyle = '●') {
if(name == null) name = Common.getNewName('ABCDEFJKLMNOPQRSTUVW')
super(name, visible, color, labelContent)
this.type = 'Point'
if(typeof x == 'number' || typeof x == 'string') x = new MathLib.Expression(x.toString())
this.x = x
if(typeof y == 'number' || typeof y == 'string') y = new MathLib.Expression(y.toString())
this.y = y
this.labelPosition = labelPosition
this.pointStyle = pointStyle
}
getReadableString() {
return `${this.name} = (${this.x}, ${this.y})`
}
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent, this.x.toEditableString(), this.y.toEditableString(), this.labelPosition, this.pointStyle]
}
draw(canvas, ctx) {
var [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())]
var pointSize = 8+(ctx.lineWidth*2)
switch(this.pointStyle) {
case '●':
ctx.beginPath();
ctx.ellipse(canvasX-pointSize/2, canvasY-pointSize/2, pointSize, pointSize)
ctx.fill();
break;
case '✕':
canvas.drawLine(ctx, canvasX-pointSize/2, canvasY-pointSize/2, canvasX+pointSize/2, canvasY+pointSize/2)
canvas.drawLine(ctx, canvasX-pointSize/2, canvasY+pointSize/2, canvasX+pointSize/2, canvasY-pointSize/2)
break;
case '':
ctx.fillRect(canvasX-pointSize/2, canvasY-1, pointSize, 2)
ctx.fillRect(canvasX-1, canvasY-pointSize/2, 2, pointSize)
break;
}
var text = this.getLabel()
ctx.font = `${canvas.textsize}px sans-serif`
var textSize = ctx.measureText(text).width
switch(this.labelPosition) {
case 'top':
canvas.drawVisibleText(ctx, text, canvasX-textSize/2, canvasY-16)
break;
case 'bottom':
canvas.drawVisibleText(ctx, text, canvasX-textSize/2, canvasY+16)
break;
case 'left':
canvas.drawVisibleText(ctx, text, canvasX-textSize-10, canvasY+4)
break;
case 'right':
canvas.drawVisibleText(ctx, text, canvasX+10, canvasY+4)
break;
case 'top-left':
canvas.drawVisibleText(ctx, text, canvasX-textSize-10, canvasY-16)
break;
case 'top-right':
canvas.drawVisibleText(ctx, text, canvasX+10, canvasY-16)
break;
case 'bottom-left':
canvas.drawVisibleText(ctx, text, canvasX-textSize-10, canvasY+16)
break;
case 'bottom-right':
canvas.drawVisibleText(ctx, text, canvasX+10, canvasY+16)
break;
}
}
}

View file

@ -0,0 +1,172 @@
/**
* LogarithmPlotter - Create graphs with logarithm scales.
* Copyright (C) 2021 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 "common.js" as Common
.import "../parameters.js" as P
class RepartitionFunction extends Common.ExecutableObject {
static type(){return 'Repartition'}
static displayType(){return 'Repartition'}
static displayTypeMultiple(){return 'Repartition functions'}
static properties() {return {
'beginIncluded': 'boolean',
'drawLineEnds': 'boolean',
'comment1': 'Note: Specify the properties for each potential result.',
'probabilities': new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d\.,]+$/, 'P({name} = ', ') = '),
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
'labelX': 'number'
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
beginIncluded = true, drawLineEnds = true, probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) {
if(name == null) name = Common.getNewName('XYZUVW')
super(name, visible, color, labelContent)
this.beginIncluded = beginIncluded
this.drawLineEnds = drawLineEnds
this.probabilities = probabilities
this.labelPosition = labelPosition
this.labelX = labelX
this.update()
}
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent,
this.beginIncluded, this.drawLineEnds, this.probabilities, this.labelPosition, this.labelX]
}
getReadableString() {
var keys = Object.keys(this.probabilities).sort((a,b) => a-b);
return `F_${this.name}(x) = P(${this.name} ≤ x)\n` + keys.map(idx => `P(${this.name}=${idx})=${this.probabilities[idx]}`).join("; ")
}
execute(x = 1) {
var ret = 0;
Object.keys(this.probabilities).sort((a,b) => a-b).forEach(idx => {
if(x >= idx) ret += parseFloat(this.probabilities[idx].replace(/,/g, '.'))
})
return ret
}
canExecute(x = 1) {return true}
// Simplify returns the simplified string of the expression.
simplify(x = 1) {
return this.execute(x)
}
getLabel() {
switch(this.labelContent) {
case 'name':
return `P(${this.name} ≤ x)`
case 'name + value':
return this.getReadableString()
case 'null':
return ''
}
}
draw(canvas, ctx) {
var currentY = 0;
var keys = Object.keys(this.probabilities).map(idx => parseInt(idx)).sort((a,b) => a-b)
if(canvas.visible(keys[0],this.probabilities[keys[0]].replace(/,/g, '.'))) {
canvas.drawLine(ctx,
0,
canvas.y2px(0),
canvas.x2px(keys[0]),
canvas.y2px(0)
)
if(canvas.visible(keys[0],0)) {
ctx.beginPath();
ctx.arc(canvas.x2px(keys[0])+4,canvas.y2px(0), 4, Math.PI / 2, 3 * Math.PI / 2);
ctx.stroke();
}
}
for(var i = 0; i < keys.length-1; i++) {
var idx = keys[i];
currentY += parseFloat(this.probabilities[idx].replace(/,/g, '.'));
if(canvas.visible(idx,currentY) || canvas.visible(keys[i+1],currentY)) {
canvas.drawLine(ctx,
Math.max(0,canvas.x2px(idx)),
canvas.y2px(currentY),
Math.min(canvas.canvasSize.width,canvas.x2px(keys[i+1])),
canvas.y2px(currentY)
)
if(canvas.visible(idx,currentY)) {
ctx.beginPath();
ctx.arc(canvas.x2px(idx),canvas.y2px(currentY), 4, 0, 2 * Math.PI);
ctx.fill();
}
if(canvas.visible(keys[i+1],currentY)) {
ctx.beginPath();
ctx.arc(canvas.x2px(keys[i+1])+4,canvas.y2px(currentY), 4, Math.PI / 2, 3 * Math.PI / 2);
ctx.stroke();
}
}
}
if(canvas.visible(keys[keys.length-1],currentY+parseFloat(this.probabilities[keys[keys.length-1]]))) {
canvas.drawLine(ctx,
Math.max(0,canvas.x2px(keys[keys.length-1])),
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))),
canvas.canvasSize.width,
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.')))
)
ctx.beginPath();
ctx.arc(
canvas.x2px(keys[keys.length-1]),
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))),
4, 0, 2 * Math.PI);
ctx.fill();
}
// Label
var text = this.getLabel()
ctx.font = `${canvas.textsize}px sans-serif`
var textSize = canvas.measureText(ctx, text)
var posX = canvas.x2px(this.labelX)
var posY = canvas.y2px(this.execute(this.labelX))
switch(this.labelPosition) {
case 'above':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
break;
case 'below':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
break;
case 'left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
break;
case 'right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
break;
case 'above-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
break;
case 'above-right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
break;
case 'below-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
break;
case 'below-right':
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
break;
}
}
}

View file

@ -0,0 +1,143 @@
/**
* LogarithmPlotter - Create graphs with logarithm scales.
* Copyright (C) 2021 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 "common.js" as Common
.import "function.js" as F
.import "../mathlib.js" as MathLib
.import "../parameters.js" as P
class Sequence extends Common.ExecutableObject {
static type(){return 'Sequence'}
static displayType(){return 'Sequence'}
static displayTypeMultiple(){return 'Sequences'}
static properties() {return {
'drawPoints': 'boolean',
'drawDashedLines': 'boolean',
'defaultExpression': new P.Dictionary('string', 'int', /^.+$/, /^\d+$/, '{name}[n+', '] = ', true),
'comment1': 'Note: Use {name}[n] to refer to {name}ₙ, {name}[n+1] for {name}ₙ₊₁...',
'baseValues': new P.Dictionary('string', 'int', /^.+$/, /^\d+$/, '{name}[', '] = '),
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
'labelX': 'number'
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
drawPoints = true, drawDashedLines = true, defaultExp = {1: "n"},
baseValues = {0: 0}, labelPosition = 'above', labelX = 1) {
if(name == null) name = Common.getNewName('uvwPSUVWabcde')
super(name, visible, color, labelContent)
this.drawPoints = drawPoints
this.drawDashedLines = drawDashedLines
this.defaultExpression = defaultExp
this.baseValues = baseValues
this.labelPosition = labelPosition
this.labelX = labelX
this.update()
}
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent,
this.drawPoints, this.drawDashedLines, this.defaultExpression, this.baseValues, this.labelPosition, this.labelX]
}
update() {
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 = new MathLib.Sequence(
this.name, this.baseValues,
Object.keys(this.defaultExpression)[0],
Object.values(this.defaultExpression)[0]
)
}
getReadableString() {
return this.sequence.toString()
}
execute(x = 1) {
if(x % 1 == 0)
return this.sequence.execute(x)
return null
}
canExecute(x = 1) {return x%1 == 0}
// Simplify returns the simplified string of the expression.
simplify(x = 1) {
if(x % 1 == 0)
return this.sequence.simplify(x)
return null
}
getLabel() {
switch(this.labelContent) {
case 'name':
return `(${this.name}ₙ)`
case 'name + value':
return this.getReadableString()
case 'null':
return ''
}
}
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)
// Label
var text = this.getLabel()
ctx.font = `${canvas.textsize}px sans-serif`
var textSize = canvas.measureText(ctx, text)
var posX = canvas.x2px(this.labelX)
var posY = canvas.y2px(this.execute(this.labelX))
switch(this.labelPosition) {
case 'above':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
break;
case 'below':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
break;
case 'left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
break;
case 'right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
break;
case 'above-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
break;
case 'above-right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
break;
case 'below-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
break;
case 'below-right':
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
break;
}
}
}

View file

@ -0,0 +1,170 @@
/**
* LogarithmPlotter - Create graphs with logarithm scales.
* Copyright (C) 2021 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 "common.js" as Common
.import "function.js" as F
.import "../objects.js" as Objects
.import "../mathlib.js" as MathLib
.import "../parameters.js" as P
class SommeGainsBode extends Common.DrawableObject {
static type(){return 'Somme gains Bode'}
static displayType(){return 'Bode Magnitudes Sum'}
static displayTypeMultiple(){return 'Bode Magnitudes Sum'}
static createable() {return false}
static properties() {return {
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
'labelX': 'number'
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
labelPosition = 'above', labelX = 1) {
if(name == null) name = 'G'
super(name, visible, color, labelContent)
this.labelPosition = labelPosition
this.labelX = labelX
this.recalculateCache()
}
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent, this.labelPosition, this.labelX]
}
getReadableString() {
return `${this.name} = ${Objects.getObjectsName('Gain Bode').join(' + ')}`
}
execute(x = 0) {
for(var [dbfn, inDrawDom] of this.cachedParts) {
if(inDrawDom.includes(x)) {
return dbfn.execute(x)
}
}
return null
}
canExecute(x = 1) {
return true // Should always be true
}
simplify(x = 1) {
for(var [dbfn, inDrawDom] of this.cachedParts) {
if(inDrawDom.includes(x)) {
return dbfn.simplify(x)
}
}
return ''
}
recalculateCache() {
this.cachedParts = []
// Calculating this is fairly resource expansive so it's cached.
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
var baseY = 0
var om0xGains = {100000: 0} // To draw the last part
var om0xPass = {100000: '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) {
om0xGains[om0x] = gainObj.gain.execute()
om0xPass[om0x] = gainObj.pass == 'high'
} else {
om0xGains[om0x+0.001] = gainObj.gain.execute()
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...
om0xList.sort((a,b) => a - b)
// Adding the total gains.
var gainsBeforeP = []
var gainsAfterP = []
var gainTotal = 0
for(var om0x of om0xList){
if(om0xPass[om0x]) { // High-pass
gainsBeforeP.push(om0xGains[om0x])
gainsAfterP.push(0)
gainTotal += om0xGains[om0x] // Gain at first
} else {
gainsBeforeP.push(0)
gainsAfterP.push(om0xGains[om0x])
}
}
// 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]}]`)
this.cachedParts.push([dbfn, inDrawDom])
previousPallier = om0xList[pallier]
baseY = dbfn.execute(om0xList[pallier])
gainTotal += gainsAfterP[pallier] - gainsBeforeP[pallier]
}
}
}
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)
if(inDrawDom.includes(this.labelX)) {
// Label
var text = this.getLabel()
ctx.font = `${canvas.textsize}px sans-serif`
var textSize = canvas.measureText(ctx, text)
var posX = canvas.x2px(this.labelX)
var posY = canvas.y2px(dbfn.execute(this.labelX))
switch(this.labelPosition) {
case 'above':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
break;
case 'below':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
break;
case 'left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
break;
case 'right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
break;
case 'above-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
break;
case 'above-right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
break;
case 'below-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
break;
case 'below-right':
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
break;
}
}
}
}
}
}

View file

@ -0,0 +1,154 @@
/**
* LogarithmPlotter - Create graphs with logarithm scales.
* Copyright (C) 2021 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 "common.js" as Common
.import "../objects.js" as Objects
.import "../mathlib.js" as MathLib
.import "../parameters.js" as P
class SommePhasesBode extends Common.ExecutableObject {
static type(){return 'Somme phases Bode'}
static displayType(){return 'Bode Phases Sum'}
static displayTypeMultiple(){return 'Bode Phases Sum'}
static createable() {return false}
static properties() {return {
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
'labelX': 'number'
}}
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
labelPosition = 'above', labelX = 1) {
if(name == null) name = 'φ'
super(name, visible, color, labelContent)
this.labelPosition = labelPosition
this.labelX = labelX
this.recalculateCache()
}
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent, this.labelPosition, this.labelX]
}
getReadableString() {
return `${this.name} = ${Objects.getObjectsName('Phase Bode').join(' + ')}`
}
execute(x=1) {
if(typeof x == 'string') x = MathLib.executeExpression(x)
for(var 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++) {
if(xval >= this.om0xList[i] && xval < this.om0xList[i+1]) {
return (new MathLib.Expression(this.phasesExprList[i])).simplify()
}
}
return '0'
}
canExecute(x = 1) {
return true
}
recalculateCache() {
// Minimum to draw (can be expended if needed, just not infinite or it'll cause issues.
var drawMin = 0.001
var drawMax = 100000
this.om0xList = [drawMin, drawMax]
this.phasesList = [0]
this.phasesExprList = ['0']
var phasesDict = {}
var phasesExprDict = {}
phasesDict[drawMax] = 0
if(Objects.currentObjects['Phase Bode'] != undefined) {
console.log('Recalculating cache phase')
for(var obj of Objects.currentObjects['Phase Bode']) {
this.om0xList.push(obj.om_0.x.execute())
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 {
phasesDict[obj.om_0.x.execute()] += obj.phase.execute()
phasesExprDict[obj.om_0.x.execute()] += '+' + obj.phase.toEditableString()
}
this.phasesList[0] += obj.om_0.y.execute()
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++) {
this.phasesList[i] = this.phasesList[i-1] + phasesDict[this.om0xList[i]]
this.phasesExprList[i] = this.phasesExprList[i-1] + '+' + phasesDict[this.om0xList[i]]
}
}
}
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])
canvas.drawLine(ctx, om0xBegin, baseY, om0xEnd, baseY)
canvas.drawLine(ctx, om0xEnd, baseY, om0xEnd, nextY)
}
// Label
var text = this.getLabel()
ctx.font = `${canvas.textsize}px sans-serif`
var textSize = canvas.measureText(ctx, text)
var posX = canvas.x2px(this.labelX)
var posY = canvas.y2px(this.execute(this.labelX))
switch(this.labelPosition) {
case 'above':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
break;
case 'below':
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
break;
case 'left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
break;
case 'right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
break;
case 'above-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
break;
case 'above-right':
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
break;
case 'below-left':
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
break;
case 'below-right':
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
break;
}
}
}

View file

@ -0,0 +1,94 @@
/**
* LogarithmPlotter - Create graphs with logarithm scales.
* Copyright (C) 2021 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 "common.js" as Common
.import "../mathlib.js" as MathLib
.import "../parameters.js" as P
class Text extends Common.DrawableObject {
static type(){return 'Text'}
static displayType(){return 'Text'}
static displayTypeMultiple(){return 'Texts'}
static properties() {return {
'x': 'Expression',
'y': 'Expression',
'labelPosition': new P.Enum('center', 'top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'),
'text': 'string',
}}
constructor(name = null, visible = true, color = null, labelContent = 'null',
x = 1, y = 0, labelPosition = 'center', text = 'New text') {
if(name == null) name = Common.getNewName('t')
super(name, visible, color, labelContent)
this.type = 'Point'
if(typeof x == 'number' || typeof x == 'string') x = new MathLib.Expression(x.toString())
this.x = x
if(typeof y == 'number' || typeof y == 'string') y = new MathLib.Expression(y.toString())
this.y = y
this.labelPosition = labelPosition
this.text = text
}
getReadableString() {
return `${this.name} = "${this.text}"`
}
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent, this.x.toEditableString(), this.y.toEditableString(), this.labelPosition, this.text]
}
draw(canvas, ctx) {
var [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())]
ctx.font = `${canvas.textsize}px sans-serif`
var textSize = ctx.measureText(this.text).width
switch(this.labelPosition) {
case 'center':
canvas.drawVisibleText(ctx, this.text, canvasX-textSize/2, canvasY+4)
break;
case 'top':
canvas.drawVisibleText(ctx, this.text, canvasX-textSize/2, canvasY-16)
break;
case 'bottom':
canvas.drawVisibleText(ctx, this.text, canvasX-textSize/2, canvasY+16)
break;
case 'left':
canvas.drawVisibleText(ctx, this.text, canvasX-textSize-5, canvasY+4)
break;
case 'right':
canvas.drawVisibleText(ctx, this.text, canvasX+5, canvasY+4)
break;
case 'top-left':
canvas.drawVisibleText(ctx, this.text, canvasX-textSize-5, canvasY-16)
break;
case 'top-right':
canvas.drawVisibleText(ctx, this.text, canvasX+5, canvasY-16)
break;
case 'bottom-left':
canvas.drawVisibleText(ctx, this.text, canvasX-textSize-5, canvasY+16)
break;
case 'bottom-right':
canvas.drawVisibleText(ctx, this.text, canvasX+5, canvasY+16)
break;
}
}
}

View file

@ -0,0 +1,157 @@
/**
* LogarithmPlotter - Create graphs with logarithm scales.
* Copyright (C) 2021 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 "common.js" as Common
.import "../objects.js" as Objects
.import "../mathlib.js" as MathLib
.import "../parameters.js" as P
class XCursor extends Common.DrawableObject {
static type(){return 'X Cursor'}
static displayType(){return 'X Cursor'}
static displayTypeMultiple(){return 'X Cursors'}
static properties() {
return {
'x': 'Expression',
'targetElement': new P.ObjectType('ExecutableObject'),
'labelPosition': new P.Enum('left', 'right'),
'approximate': 'boolean',
'rounding': 'number',
'displayStyle': new P.Enum(
'— — — — — — —',
'⸺⸺⸺⸺⸺⸺',
'• • • • • • • • • •'
),
'targetValuePosition' : new P.Enum('Next to target', 'With label', 'Hidden')
}
}
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
x = 1, targetElement = null, labelPosition = 'left', approximate = true,
rounding = 3, displayStyle = '— — — — — — —', targetValuePosition = 'Next to target') {
if(name == null) name = Common.getNewName('X')
super(name, visible, color, labelContent)
this.type = 'X Cursor'
this.approximate = approximate
this.rounding = rounding
if(typeof x == 'number' || typeof x == 'string') x = new MathLib.Expression(x.toString())
this.x = x
this.targetElement = targetElement
if(typeof targetElement == "string") {
this.targetElement = Objects.getObjectByName(targetElement, elementTypes)
}
this.labelPosition = labelPosition
this.displayStyle = displayStyle
this.targetValuePosition = targetValuePosition
}
export() {
return [this.name, this.visible, this.color.toString(), this.labelContent,
this.x.toEditableString(), this.targetElement == null ? null : this.targetElement.name, this.labelPosition,
this.approximate, this.rounding, this.displayStyle, this.targetValuePosition]
}
getReadableString() {
if(this.targetElement == null) return `${this.name} = ${this.x.toString()}`
return `${this.name} = ${this.x.toString()}\n${this.getTargetValueLabel()}`
}
getTargetValueLabel() {
var t = this.targetElement
var approx = ''
if(this.approximate) {
approx = t.execute(this.x.execute())
approx = approx.toPrecision(this.rounding + Math.round(approx).toString().length)
}
return `${t.name}(${this.name}) = ${t.simplify(this.x.toEditableString())}` +
(this.approximate ? ' ≈ ' + approx : '')
}
getLabel() {
switch(this.labelContent) {
case 'name':
return this.name
break;
case 'name + value':
switch(this.targetValuePosition) {
case 'Next to target':
case 'Hidden':
return `${this.name} = ${this.x.toString()}`
break;
case 'With label':
return this.getReadableString()
break;
}
case 'null':
return ''
}
}
draw(canvas, ctx) {
var xpos = canvas.x2px(this.x.execute())
switch(this.displayStyle) {
case '— — — — — — —':
var dashPxSize = 10
for(var i = 0; i < canvas.canvasSize.height; i += dashPxSize*2)
canvas.drawLine(ctx, xpos, i, xpos, i+dashPxSize)
break;
case '⸺⸺⸺⸺⸺⸺':
canvas.drawXLine(ctx, this.x.execute())
break;
case '• • • • • • • • • •':
var pointDistancePx = 10
var pointSize = 2
ctx.beginPath();
for(var i = 0; i < canvas.canvasSize.height; i += pointDistancePx)
ctx.ellipse(xpos-pointSize/2, i-pointSize/2, pointSize, pointSize)
ctx.fill();
break;
}
// Label
var text = this.getLabel()
ctx.font = `${canvas.textsize}px sans-serif`
var textSize = canvas.measureText(ctx, text)
switch(this.labelPosition) {
case 'left':
canvas.drawVisibleText(ctx, text, xpos-textSize.width-5, textSize.height+5)
break;
case 'right':
canvas.drawVisibleText(ctx, text, xpos+5, textSize.height+5)
break;
}
if(this.targetValuePosition == 'Next to target' && this.targetElement != null) {
var text = this.getTargetValueLabel()
var textSize = canvas.measureText(ctx, text)
var ypox = canvas.y2px(this.targetElement.execute(this.x.execute()))
switch(this.labelPosition) {
case 'left':
canvas.drawVisibleText(ctx, text, xpos-textSize.width-5, ypox+textSize.height)
break;
case 'right':
canvas.drawVisibleText(ctx, text, xpos+5, ypox+textSize.height)
break;
}
}
}
}

View file

@ -1 +1 @@
{"xzoom":300,"yzoom":22,"xmin":0.5,"ymax":22,"xaxisstep":"4","yaxisstep":"4","xaxislabel":"","yaxislabel":"","logscalex":true,"linewidth":2,"showxgrad":true,"showygrad":true,"textsize":14,"history":[[["CreateNewObject",["g","Function",["g",true,"#697F4E","name + value","x","ℝ⁺*","","application","above",1,true,true]]],["EditedProperty",["g","Function","expression","x","(x ^ 3)",true]],["CreateNewObject",["h","Function",["h",true,"#0CA920","name + value","x","ℝ⁺*","","application","above",1,true,true]]],["EditedProperty",["h","Function","expression","x","Integrate(\"x^3\", x)",true]],["EditedProperty",["h","Function","expression","Integrate(\"x^3\", x)","integral(0, x, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(0, x, \"t^3\", \"t\")","integral(0, x, \"t^3\", \"t\")",true]],["EditedProperty",["g","Function","labelX",1,2,false]],["EditedProperty",["g","Function","displayMode","application","function",false]],["EditedProperty",["g","Function","labelPosition","above","above-left",false]],["EditedProperty",["g","Function","labelX",2,1.5,false]],["EditedProperty",["g","Function","labelPosition","above-left","above",false]],["EditedProperty",["h","Function","expression","integral(0, x, \"t^3\", \"t\")","integral((x + 1), x, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral((x + 1), x, \"t^3\", \"t\")","integral(x, (x + 1), \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(x, (x + 1), \"t^3\", \"t\")","integral(0, (x + 1), \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(0, (x + 1), \"t^3\", \"t\")","integral(0, x, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(0, x, \"t^3\", \"t\")","integral(0, (x + 0), \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(0, (x + 0), \"t^3\", \"t\")","integral(1, x, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(1, x, \"t^3\", \"t\")","integral(x, 0, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(x, 0, \"t^3\", \"t\")","integral(x, (x + 1), \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(x, (x + 1), \"t^3\", \"t\")","integral(x, 1, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(x, 1, \"t^3\", \"t\")","integral(0, x, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","displayMode","application","function",false]],["EditedProperty",["h","Function","displayMode","function","application",false]],["EditedProperty",["h","Function","labelX",1,2,false]],["EditedProperty",["h","Function","labelPosition","above","below",false]],["EditedProperty",["h","Function","labelPosition","below","below-right",false]]],[]],"width":1027,"height":594,"objects":{"Function":[["f",true,"#8F6027","name + value","derivative(\"t^3\", \"t\", x)","ℝ⁺*","","application","above-left",2,true,true],["g",true,"#697F4E","name + value","(x ^ 3)","ℝ⁺*","","function","above",1.5,true,true],["h",true,"#0CA920","name + value","integral(0, x, \"t^3\", \"t\")","ℝ⁺*","","application","below-right",2,true,true]]},"type":"logplotv1"} LPFv1{"xzoom":300,"yzoom":22,"xmin":0.5,"ymax":22,"xaxisstep":"4","yaxisstep":"4","xaxislabel":"","yaxislabel":"","logscalex":true,"linewidth":2,"showxgrad":true,"showygrad":true,"textsize":14,"history":[[["CreateNewObject",["g","Function",["g",true,"#697F4E","name + value","x","ℝ⁺*","","application","above",1,true,true]]],["EditedProperty",["g","Function","expression","x","(x ^ 3)",true]],["CreateNewObject",["h","Function",["h",true,"#0CA920","name + value","x","ℝ⁺*","","application","above",1,true,true]]],["EditedProperty",["h","Function","expression","x","Integrate(\"x^3\", x)",true]],["EditedProperty",["h","Function","expression","Integrate(\"x^3\", x)","integral(0, x, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(0, x, \"t^3\", \"t\")","integral(0, x, \"t^3\", \"t\")",true]],["EditedProperty",["g","Function","labelX",1,2,false]],["EditedProperty",["g","Function","displayMode","application","function",false]],["EditedProperty",["g","Function","labelPosition","above","above-left",false]],["EditedProperty",["g","Function","labelX",2,1.5,false]],["EditedProperty",["g","Function","labelPosition","above-left","above",false]],["EditedProperty",["h","Function","expression","integral(0, x, \"t^3\", \"t\")","integral((x + 1), x, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral((x + 1), x, \"t^3\", \"t\")","integral(x, (x + 1), \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(x, (x + 1), \"t^3\", \"t\")","integral(0, (x + 1), \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(0, (x + 1), \"t^3\", \"t\")","integral(0, x, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(0, x, \"t^3\", \"t\")","integral(0, (x + 0), \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(0, (x + 0), \"t^3\", \"t\")","integral(1, x, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(1, x, \"t^3\", \"t\")","integral(x, 0, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(x, 0, \"t^3\", \"t\")","integral(x, (x + 1), \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(x, (x + 1), \"t^3\", \"t\")","integral(x, 1, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(x, 1, \"t^3\", \"t\")","integral(0, x, \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","displayMode","application","function",false]],["EditedProperty",["h","Function","displayMode","function","application",false]],["EditedProperty",["h","Function","labelX",1,2,false]],["EditedProperty",["h","Function","labelPosition","above","below",false]],["EditedProperty",["h","Function","labelPosition","below","below-right",false]],["EditedProperty",["h","Function","expression","integral(0, x, \"t^3\", \"t\")","integral(0, (x - 1), \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(0, (x - 1), \"t^3\", \"t\")","integral(0, (x - 0), \"t^3\", \"t\")",true]],["EditedProperty",["h","Function","expression","integral(0, (x - 0), \"t^3\", \"t\")","integral(0, (x + 0.01), \"t^3\", \"t\")",true]]],[]],"width":1027,"height":594,"objects":{"Function":[["f",true,"#8F6027","name + value","derivative(\"t^3\", \"t\", x)","ℝ⁺*","","application","above-left",2,true,true],["g",true,"#697F4E","name + value","(x ^ 3)","ℝ⁺*","","function","above",1.5,true,true],["h",true,"#0CA920","name + value","integral(0, (x + 0.01), \"t^3\", \"t\")","ℝ⁺*","","application","below-right",2,true,true]]},"type":"logplotv1"}