Compare commits
63 commits
Author | SHA1 | Date | |
---|---|---|---|
136ac4c7bc | |||
87d8882db1 | |||
Ad5001 | 915c6b5246 | ||
Ad5001 | 54f9802975 | ||
Ad5001 | 997a1645a0 | ||
Ad5001 | fefb0f92b0 | ||
Ad5001 | 665906ecb3 | ||
Ad5001 | 781a0f4aae | ||
Ad5001 | 5f8c756dc7 | ||
Ad5001 | 8e75f13e9a | ||
Ad5001 | 82e8413e56 | ||
Ad5001 | 73cba85592 | ||
Ad5001 | 861bb001c9 | ||
Ad5001 | 08fea34366 | ||
Ad5001 | a6fcf6da19 | ||
Ad5001 | f730121047 | ||
Ad5001 | 80f1077b35 | ||
Ad5001 | 33c790b113 | ||
Ad5001 | f9b0bb99ce | ||
Ad5001 | 557592708f | ||
Ad5001 | 1f72c76fb4 | ||
Ad5001 | 66b4c8df90 | ||
Ad5001 | 70b1ac09f7 | ||
Ad5001 | 2d6389bfd5 | ||
Ad5001 | d0851b819f | ||
Ad5001 | cff994d9d7 | ||
Ad5001 | fde2526c54 | ||
Ad5001 | e5fe8afd06 | ||
Ad5001 | f9a7443631 | ||
Ad5001 | 874046960f | ||
Ad5001 | d53573d468 | ||
Ad5001 | 1da1221c51 | ||
8bb147cbee | |||
Ad5001 | bd2e692285 | ||
Ad5001 | 298acaace2 | ||
Ad5001 | 5284c71ee7 | ||
Ad5001 | b8d679e118 | ||
Ad5001 | 22106f97b5 | ||
Ad5001 | bf211df67d | ||
Ad5001 | 803416d08d | ||
Ad5001 | d991deee7b | ||
Ad5001 | ed4d30573c | ||
Ad5001 | 3f1d089a78 | ||
Ad5001 | 4b692894f2 | ||
Ad5001 | 062fde6b16 | ||
Ad5001 | 2b5d442d3a | ||
Ad5001 | 038dd9f4a8 | ||
Ad5001 | cf754a7a34 | ||
Ad5001 | bc35b18da0 | ||
Ad5001 | 7542d63121 | ||
Ad5001 | 4fe1086d68 | ||
Ad5001 | a66ccd1319 | ||
Ad5001 | 9879e7fbc9 | ||
Ad5001 | 999999832a | ||
Ad5001 | 7408520819 | ||
Ad5001 | 9f2b08b938 | ||
Ad5001 | 3039aade29 | ||
Ad5001 | d6a83b0f4b | ||
Ad5001 | 0b5aa36c23 | ||
Ad5001 | 25347a8eff | ||
d6d7f3a511 | |||
Ad5001 | e67619771b | ||
Ad5001 | df3e7d4796 |
1
.gitignore
vendored
|
@ -16,6 +16,7 @@ linux/flatpak/.flatpak-builder
|
||||||
*.jsc
|
*.jsc
|
||||||
*.qmlc
|
*.qmlc
|
||||||
*.log
|
*.log
|
||||||
|
**/*.dxvk-cache
|
||||||
.DS_Store
|
.DS_Store
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
**/__pycache__/
|
**/__pycache__/
|
||||||
|
|
30
CHANGELOG.md
|
@ -1,5 +1,35 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v0.5.0 (11 Jan 2024)
|
||||||
|
|
||||||
|
**New**
|
||||||
|
|
||||||
|
* New, reworked application icon.
|
||||||
|
* Graph is now mouse interactive:
|
||||||
|
* You can now drag to move and scroll to zoom!
|
||||||
|
* Builtin functions now provide usage when used in the autocomplete of the expression editor.
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
|
||||||
|
* When creating an object that can be positioned, new default behavior is to pick first instead of opening object settings.
|
||||||
|
* Icons with text now use the SVG's text element, allowing them to integrate better with the system's default font.
|
||||||
|
* Special characters popup is now context aware (e.g. no sub/supscript symbols in expressions).
|
||||||
|
* New symbols in special characters popup.
|
||||||
|
* Integrals and derivatives can now be provided with an executable object (e.g. Functions) instead of strings as function.
|
||||||
|
* New description on Linux.
|
||||||
|
|
||||||
|
**Fixed bugs**
|
||||||
|
|
||||||
|
* Fixing ∞ 'variable' in domains and expressions.
|
||||||
|
* Several other bugs related to constants in expresions were fixed as well.
|
||||||
|
* Builtin functions now send an error message when not provided with the proper arguments.
|
||||||
|
|
||||||
|
**Internal changes**
|
||||||
|
|
||||||
|
* Updated to PySide6 v6.6.1.
|
||||||
|
* Reworked continuous functions' rendering to make it faster.
|
||||||
|
* Removed old bits from an unfinished new parser that weren't used.
|
||||||
|
|
||||||
## v0.4.0 (27 May 2023)
|
## v0.4.0 (27 May 2023)
|
||||||
|
|
||||||
**Changes**
|
**Changes**
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""
|
"""
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,8 +17,8 @@
|
||||||
"""
|
"""
|
||||||
from shutil import which
|
from shutil import which
|
||||||
|
|
||||||
__VERSION__ = "0.4.0"
|
__VERSION__ = "0.6.0"
|
||||||
is_release = True
|
is_release = False
|
||||||
|
|
||||||
|
|
||||||
# Check if development version, if so get the date of the latest git patch
|
# Check if development version, if so get the date of the latest git patch
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""
|
"""
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
lupdate -extensions js,qs,qml,py -recursive .. -ts lp_*.ts
|
lupdate -extensions mjs,js,qs,qml,py -recursive .. -ts lp_*.ts
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""
|
"""
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -41,15 +41,16 @@ chdir(path.dirname(path.realpath(__file__)))
|
||||||
if path.realpath(path.join(getcwd(), "..")) not in sys_path:
|
if path.realpath(path.join(getcwd(), "..")) not in sys_path:
|
||||||
sys_path.append(path.realpath(path.join(getcwd(), "..")))
|
sys_path.append(path.realpath(path.join(getcwd(), "..")))
|
||||||
|
|
||||||
|
|
||||||
from LogarithmPlotter import __VERSION__
|
from LogarithmPlotter import __VERSION__
|
||||||
from LogarithmPlotter.util import config, native
|
from LogarithmPlotter.util import config, native
|
||||||
from LogarithmPlotter.util.update import check_for_updates
|
from LogarithmPlotter.util.update import check_for_updates
|
||||||
from LogarithmPlotter.util.helper import Helper
|
from LogarithmPlotter.util.helper import Helper
|
||||||
from LogarithmPlotter.util.latex import Latex
|
from LogarithmPlotter.util.latex import Latex
|
||||||
|
from LogarithmPlotter.util.js import PyJSValue
|
||||||
|
|
||||||
config.init()
|
config.init()
|
||||||
|
|
||||||
|
|
||||||
def get_linux_theme():
|
def get_linux_theme():
|
||||||
des = {
|
des = {
|
||||||
"KDE": "Fusion",
|
"KDE": "Fusion",
|
||||||
|
@ -62,9 +63,9 @@ def get_linux_theme():
|
||||||
else:
|
else:
|
||||||
# Android
|
# Android
|
||||||
return "Material"
|
return "Material"
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
|
|
||||||
if not 'QT_QUICK_CONTROLS_STYLE' in environ:
|
if not 'QT_QUICK_CONTROLS_STYLE' in environ:
|
||||||
environ["QT_QUICK_CONTROLS_STYLE"] = {
|
environ["QT_QUICK_CONTROLS_STYLE"] = {
|
||||||
"linux": get_linux_theme(),
|
"linux": get_linux_theme(),
|
||||||
|
@ -73,9 +74,9 @@ def run():
|
||||||
"cygwin": "Fusion",
|
"cygwin": "Fusion",
|
||||||
"darwin": "macOS"
|
"darwin": "macOS"
|
||||||
}[platform]
|
}[platform]
|
||||||
|
|
||||||
dep_time = time()
|
dep_time = time()
|
||||||
print("Loaded dependencies in " + str((dep_time - start_time)*1000) + "ms.")
|
print("Loaded dependencies in " + str((dep_time - start_time) * 1000) + "ms.")
|
||||||
|
|
||||||
icon_fallbacks = QIcon.fallbackSearchPaths();
|
icon_fallbacks = QIcon.fallbackSearchPaths();
|
||||||
base_icon_path = path.join(getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "icons")
|
base_icon_path = path.join(getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "icons")
|
||||||
|
@ -85,42 +86,45 @@ def run():
|
||||||
icon_fallbacks.append(path.realpath(path.join(base_icon_path, "settings")))
|
icon_fallbacks.append(path.realpath(path.join(base_icon_path, "settings")))
|
||||||
icon_fallbacks.append(path.realpath(path.join(base_icon_path, "settings", "custom")))
|
icon_fallbacks.append(path.realpath(path.join(base_icon_path, "settings", "custom")))
|
||||||
QIcon.setFallbackSearchPaths(icon_fallbacks);
|
QIcon.setFallbackSearchPaths(icon_fallbacks);
|
||||||
|
|
||||||
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
|
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
|
||||||
app = QApplication(argv)
|
app = QApplication(argv)
|
||||||
app.setApplicationName("LogarithmPlotter")
|
app.setApplicationName("LogarithmPlotter")
|
||||||
|
app.setDesktopFileName("eu.ad5001.LogarithmPlotter.desktop")
|
||||||
app.setOrganizationName("Ad5001")
|
app.setOrganizationName("Ad5001")
|
||||||
app.styleHints().setShowShortcutsInContextMenus(True)
|
app.styleHints().setShowShortcutsInContextMenus(True)
|
||||||
app.setWindowIcon(QIcon(path.realpath(path.join(getcwd(), "logarithmplotter.svg"))))
|
app.setWindowIcon(QIcon(path.realpath(path.join(getcwd(), "logarithmplotter.svg"))))
|
||||||
|
|
||||||
# Installing translators
|
# Installing translators
|
||||||
translator = QTranslator()
|
translator = QTranslator()
|
||||||
# Check if lang is forced.
|
# Check if lang is forced.
|
||||||
forcedlang = [p for p in argv if p[:7]=="--lang="]
|
forcedlang = [p for p in argv if p[:7] == "--lang="]
|
||||||
locale = QLocale(forcedlang[0][7:]) if len(forcedlang) > 0 else QLocale()
|
locale = QLocale(forcedlang[0][7:]) if len(forcedlang) > 0 else QLocale()
|
||||||
if (translator.load(locale, "lp", "_", path.realpath(path.join(getcwd(), "i18n")))):
|
if translator.load(locale, "lp", "_", path.realpath(path.join(getcwd(), "i18n"))):
|
||||||
app.installTranslator(translator);
|
app.installTranslator(translator);
|
||||||
|
|
||||||
# Installing macOS file handler.
|
# Installing macOS file handler.
|
||||||
macOSFileOpenHandler = None
|
macOSFileOpenHandler = None
|
||||||
if platform == "darwin":
|
if platform == "darwin":
|
||||||
macOSFileOpenHandler = native.MacOSFileOpenHandler()
|
macOSFileOpenHandler = native.MacOSFileOpenHandler()
|
||||||
app.installEventFilter(macOSFileOpenHandler)
|
app.installEventFilter(macOSFileOpenHandler)
|
||||||
|
|
||||||
engine = QQmlApplicationEngine()
|
engine = QQmlApplicationEngine()
|
||||||
global tmpfile
|
global tmpfile
|
||||||
helper = Helper(pwd, tmpfile)
|
helper = Helper(pwd, tmpfile)
|
||||||
latex = Latex(tempdir)
|
latex = Latex(tempdir)
|
||||||
engine.rootContext().setContextProperty("Helper", helper)
|
js_globals = PyJSValue(engine.globalObject())
|
||||||
engine.rootContext().setContextProperty("Latex", latex)
|
js_globals.Modules = engine.newObject()
|
||||||
|
js_globals.Helper = engine.newQObject(helper)
|
||||||
|
js_globals.Latex = engine.newQObject(latex)
|
||||||
engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv)
|
engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv)
|
||||||
engine.rootContext().setContextProperty("StartTime", dep_time)
|
engine.rootContext().setContextProperty("StartTime", dep_time)
|
||||||
|
|
||||||
app.translate("About","About LogarithmPlotter") # FOR SOME REASON, if this isn't included, Qt refuses to load the QML file.
|
app.translate("About", "About LogarithmPlotter")
|
||||||
|
# FOR SOME REASON, if this isn't included, Qt refuses to load the QML file.
|
||||||
|
|
||||||
engine.addImportPath(path.realpath(path.join(getcwd(), "qml")))
|
engine.addImportPath(path.realpath(path.join(getcwd(), "qml")))
|
||||||
engine.load(path.realpath(path.join(getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "LogarithmPlotter.qml")))
|
engine.load(path.realpath(path.join(getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "LogarithmPlotter.qml")))
|
||||||
|
|
||||||
|
|
||||||
if not engine.rootObjects():
|
if not engine.rootObjects():
|
||||||
print("No root object", path.realpath(path.join(getcwd(), "qml")))
|
print("No root object", path.realpath(path.join(getcwd(), "qml")))
|
||||||
|
@ -130,26 +134,26 @@ def run():
|
||||||
# Open the current diagram
|
# Open the current diagram
|
||||||
chdir(pwd)
|
chdir(pwd)
|
||||||
if len(argv) > 0 and path.exists(argv[-1]) and argv[-1].split('.')[-1] in ['lpf']:
|
if len(argv) > 0 and path.exists(argv[-1]) and argv[-1].split('.')[-1] in ['lpf']:
|
||||||
engine.rootObjects()[0].loadDiagram(argv[-1])
|
js_globals.Modules.IO.loadDiagram(argv[-1])
|
||||||
chdir(path.dirname(path.realpath(__file__)))
|
chdir(path.dirname(path.realpath(__file__)))
|
||||||
|
|
||||||
if platform == "darwin":
|
if platform == "darwin":
|
||||||
macOSFileOpenHandler.init_graphics(engine.rootObjects()[0])
|
macOSFileOpenHandler.init_io(js_globals.Modules.IO)
|
||||||
|
|
||||||
# Check for LaTeX installation if LaTeX support is enabled
|
# Check for LaTeX installation if LaTeX support is enabled
|
||||||
if config.getSetting("enable_latex"):
|
if config.getSetting("enable_latex"):
|
||||||
latex.check_latex_install()
|
latex.check_latex_install()
|
||||||
|
|
||||||
# Check for updates
|
# Check for updates
|
||||||
if config.getSetting("check_for_updates"):
|
if config.getSetting("check_for_updates"):
|
||||||
check_for_updates(__VERSION__, engine.rootObjects()[0])
|
check_for_updates(__VERSION__, engine.rootObjects()[0])
|
||||||
|
|
||||||
exit_code = app.exec_()
|
exit_code = app.exec()
|
||||||
|
|
||||||
tempdir.cleanup()
|
tempdir.cleanup()
|
||||||
config.save()
|
config.save()
|
||||||
exit(exit_code)
|
exit(exit_code)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
run()
|
run()
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,64 @@
|
||||||
<svg id="SVGRoot" width="48" height="48" version="1.1" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<title>LogarithmPlotter Icon v1.0</title>
|
<svg
|
||||||
<g fill-rule="evenodd" stroke-width="2">
|
width="24.0px"
|
||||||
<rect width="48" height="48" ry="6" fill="#fff"/>
|
height="24.0px"
|
||||||
<rect x="2" y="38" width="44" height="4" stroke-opacity="0"/>
|
viewBox="0 0 24.0 24.0"
|
||||||
<rect x="18" y="2" width="4" height="44" stroke-opacity="0"/>
|
version="1.1"
|
||||||
</g>
|
id="SVGRoot"
|
||||||
<path d="M 42.05,2 A 40.05,36.05 0 0 1 2,38.05" fill="none" stroke="#f00" stroke-width="3.9012"/>
|
xml:space="preserve"
|
||||||
</svg>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"><title
|
||||||
|
id="title836">LogarithmPlotter Icon v1.0</title><defs
|
||||||
|
id="defs833" /><metadata
|
||||||
|
id="metadata836"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title>LogarithmPlotter Icon v1.0</dc:title><cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-nc-sa/4.0/" /><dc:date>2021</dc:date><dc:creator><cc:Agent><dc:title>Ad5001</dc:title></cc:Agent></dc:creator><dc:rights><cc:Agent><dc:title>(c) Ad5001 2021 - All rights reserved</dc:title></cc:Agent></dc:rights></cc:Work><cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-nc-sa/4.0/"><cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" /><cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" /><cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" /><cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" /><cc:prohibits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#CommercialUse" /><cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /><cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" /></cc:License></rdf:RDF></metadata><g
|
||||||
|
id="layer2"
|
||||||
|
transform="matrix(1,0,0,0.94444444,0,1.1666667)"
|
||||||
|
style="fill:#666666"><rect
|
||||||
|
style="fill:#666666;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect1546"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
x="3"
|
||||||
|
y="3"
|
||||||
|
ry="2.25" /></g><g
|
||||||
|
id="layer2-6"
|
||||||
|
transform="matrix(1,0,0,0.94444444,0,0.16666668)"
|
||||||
|
style="fill:#f9f9f9"><rect
|
||||||
|
style="fill:#f9f9f9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect1546-7"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
x="3"
|
||||||
|
y="3"
|
||||||
|
ry="2.25" /></g><g
|
||||||
|
id="layer1"
|
||||||
|
style="stroke-width:2;stroke-dasharray:none"><rect
|
||||||
|
style="fill:#000000;fill-rule:evenodd;stroke-width:1.86898;stroke-dasharray:none;stroke-opacity:0"
|
||||||
|
id="rect1410"
|
||||||
|
width="14"
|
||||||
|
height="2"
|
||||||
|
x="5"
|
||||||
|
y="15.5" /><rect
|
||||||
|
style="fill:#000000;fill-rule:evenodd;stroke-width:2;stroke-dasharray:none;stroke-opacity:0"
|
||||||
|
id="rect1412"
|
||||||
|
width="2"
|
||||||
|
height="15"
|
||||||
|
x="9"
|
||||||
|
y="3.9768662" /><path
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path1529"
|
||||||
|
d="M 18,4 C 18,10.017307 13.40948,15.5 5,15.5" /></g></svg>
|
||||||
|
|
Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 3.1 KiB |
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,9 +20,7 @@ import QtQuick
|
||||||
import Qt.labs.platform as Native
|
import Qt.labs.platform as Native
|
||||||
//import QtQuick.Controls 2.15
|
//import QtQuick.Controls 2.15
|
||||||
import eu.ad5001.MixedMenu 1.1
|
import eu.ad5001.MixedMenu 1.1
|
||||||
import "js/objects.js" as Objects
|
import "js/historylib.mjs" as HistoryLib
|
||||||
import "js/historylib.js" as HistoryLib
|
|
||||||
import "js/math/latex.js" as LatexJS
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -35,7 +33,6 @@ import "js/math/latex.js" as LatexJS
|
||||||
\sa LogarithmPlotter
|
\sa LogarithmPlotter
|
||||||
*/
|
*/
|
||||||
MenuBar {
|
MenuBar {
|
||||||
property var settingsMenu: settingsSubMenu
|
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
title: qsTr("&File")
|
title: qsTr("&File")
|
||||||
|
@ -92,30 +89,36 @@ MenuBar {
|
||||||
icon.color: enabled ? sysPalette.windowText : sysPaletteIn.windowText
|
icon.color: enabled ? sysPalette.windowText : sysPaletteIn.windowText
|
||||||
enabled: history.redoCount > 0
|
enabled: history.redoCount > 0
|
||||||
}
|
}
|
||||||
MenuSeparator { }
|
|
||||||
Action {
|
Action {
|
||||||
text: qsTr("&Copy plot")
|
text: qsTr("&Copy plot")
|
||||||
shortcut: StandardKey.Copy
|
shortcut: StandardKey.Copy
|
||||||
onTriggered: root.copyDiagramToClipboard()
|
onTriggered: root.copyDiagramToClipboard()
|
||||||
icon.name: 'edit-copy'
|
icon.name: 'edit-copy'
|
||||||
}
|
}
|
||||||
|
MenuSeparator { }
|
||||||
|
Action {
|
||||||
|
text: qsTr("&Preferences")
|
||||||
|
shortcut: StandardKey.Copy
|
||||||
|
onTriggered: preferences.open()
|
||||||
|
icon.name: 'settings'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
title: qsTr("&Create")
|
title: qsTr("&Create")
|
||||||
// Services repeater
|
// Services repeater
|
||||||
Repeater {
|
Repeater {
|
||||||
model: Object.keys(Objects.types)
|
model: Object.keys(Modules.Objects.types)
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: Objects.types[modelData].displayType()
|
text: Modules.Objects.types[modelData].displayType()
|
||||||
visible: Objects.types[modelData].createable()
|
visible: Modules.Objects.types[modelData].createable()
|
||||||
height: visible ? implicitHeight : 0
|
height: visible ? implicitHeight : 0
|
||||||
icon.name: modelData
|
icon.name: modelData
|
||||||
icon.source: './icons/objects/' + modelData + '.svg'
|
icon.source: './icons/objects/' + modelData + '.svg'
|
||||||
icon.color: sysPalette.buttonText
|
icon.color: sysPalette.buttonText
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
var newObj = Objects.createNewRegisteredObject(modelData)
|
var newObj = Modules.Objects.createNewRegisteredObject(modelData)
|
||||||
history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export()))
|
history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export()))
|
||||||
objectLists.update()
|
objectLists.update()
|
||||||
}
|
}
|
||||||
|
@ -123,105 +126,6 @@ MenuBar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu {
|
|
||||||
id: settingsSubMenu
|
|
||||||
title: qsTr("&Settings")
|
|
||||||
Action {
|
|
||||||
id: checkForUpdatesMenuSetting
|
|
||||||
text: qsTr("Check for updates on startup")
|
|
||||||
checkable: true
|
|
||||||
checked: Helper.getSettingBool("check_for_updates")
|
|
||||||
onTriggered: Helper.setSettingBool("check_for_updates", checked)
|
|
||||||
icon.name: 'update'
|
|
||||||
icon.color: sysPalette.buttonText
|
|
||||||
}
|
|
||||||
|
|
||||||
Action {
|
|
||||||
id: resetRedoStackMenuSetting
|
|
||||||
text: qsTr("Reset redo stack automaticly")
|
|
||||||
checkable: true
|
|
||||||
checked: Helper.getSettingBool("reset_redo_stack")
|
|
||||||
onTriggered: Helper.setSettingBool("reset_redo_stack", checked)
|
|
||||||
icon.name: 'timeline'
|
|
||||||
icon.color: sysPalette.buttonText
|
|
||||||
}
|
|
||||||
|
|
||||||
Action {
|
|
||||||
id: enableLatexJSSetting
|
|
||||||
text: qsTr("Enable LaTeX rendering")
|
|
||||||
checkable: true
|
|
||||||
checked: Helper.getSettingBool("enable_latex")
|
|
||||||
onTriggered: {
|
|
||||||
Helper.setSettingBool("enable_latex", checked)
|
|
||||||
LatexJS.enabled = checked
|
|
||||||
drawCanvas.requestPaint()
|
|
||||||
}
|
|
||||||
icon.name: 'Expression'
|
|
||||||
icon.color: sysPalette.buttonText
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu {
|
|
||||||
title: qsTr("Expression editor")
|
|
||||||
|
|
||||||
Action {
|
|
||||||
id: autocloseFormulaSetting
|
|
||||||
text: qsTr("Automatically close parenthesises and brackets")
|
|
||||||
checkable: true
|
|
||||||
checked: Helper.getSettingBool("expression_editor.autoclose")
|
|
||||||
onTriggered: {
|
|
||||||
Helper.setSettingBool("expression_editor.autoclose", checked)
|
|
||||||
}
|
|
||||||
icon.name: 'Text'
|
|
||||||
icon.color: sysPalette.buttonText
|
|
||||||
}
|
|
||||||
|
|
||||||
Action {
|
|
||||||
id: colorizeFormulaSetting
|
|
||||||
text: qsTr("Enable syntax highlighting")
|
|
||||||
checkable: true
|
|
||||||
checked: Helper.getSettingBool("expression_editor.colorize")
|
|
||||||
onTriggered: {
|
|
||||||
Helper.setSettingBool("expression_editor.colorize", checked)
|
|
||||||
}
|
|
||||||
icon.name: 'appearance'
|
|
||||||
icon.color: sysPalette.buttonText
|
|
||||||
}
|
|
||||||
|
|
||||||
Action {
|
|
||||||
id: autocompleteFormulaSetting
|
|
||||||
text: qsTr("Enable autocompletion")
|
|
||||||
checkable: true
|
|
||||||
checked: Helper.getSettingBool("autocompletion.enabled")
|
|
||||||
onTriggered: {
|
|
||||||
Helper.setSettingBool("autocompletion.enabled", checked)
|
|
||||||
}
|
|
||||||
icon.name: 'label'
|
|
||||||
icon.color: sysPalette.buttonText
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu {
|
|
||||||
id: colorSchemeSetting
|
|
||||||
title: qsTr("Color Scheme")
|
|
||||||
property var schemes: ["Breeze Light", "Breeze Dark", "Solarized", "Github Light", "Github Dark", "Nord", "Monokai"]
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: colorSchemeSetting.schemes
|
|
||||||
|
|
||||||
MenuItem {
|
|
||||||
text: modelData
|
|
||||||
checkable: true
|
|
||||||
checked: Helper.getSettingInt("expression_editor.color_scheme") == index
|
|
||||||
onTriggered: {
|
|
||||||
parent.children[Helper.getSettingInt("expression_editor.color_scheme")].checked = false
|
|
||||||
checked = true
|
|
||||||
Helper.setSettingInt("expression_editor.color_scheme", index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
title: qsTr("&Help")
|
title: qsTr("&Help")
|
||||||
Action {
|
Action {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,9 +19,7 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick.Window
|
import QtQuick.Window
|
||||||
import "../js/objects.js" as Objects
|
import "../js/historylib.mjs" as HistoryLib
|
||||||
import "../js/historylib.js" as HistoryLib
|
|
||||||
import "../js/history/common.js" as HistoryCommon
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmltype History
|
\qmltype History
|
||||||
|
@ -214,8 +212,8 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
HistoryLib.history = historyObj
|
Modules.History.history = historyObj
|
||||||
HistoryCommon.themeTextColor = sysPalette.windowText
|
Modules.History.themeTextColor = sysPalette.windowText
|
||||||
HistoryCommon.imageDepth = Screen.devicePixelRatio
|
Modules.History.imageDepth = Screen.devicePixelRatio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||||
import "../js/utils.js" as Utils
|
import "../js/utils.mjs" as Utils
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -54,6 +54,7 @@ Item {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
placeholderText: qsTr("Filter...")
|
placeholderText: qsTr("Filter...")
|
||||||
|
category: "all"
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
import "../js/utils.js" as Utils
|
import "../js/utils.mjs" as Utils
|
||||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -18,9 +18,8 @@
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Qt.labs.platform as Native
|
import Qt.labs.platform as Native
|
||||||
import "js/objects.js" as Objects
|
import "js/utils.mjs" as Utils
|
||||||
import "js/utils.js" as Utils
|
import "js/mathlib.mjs" as MathLib
|
||||||
import "js/mathlib.js" as MathLib
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmltype LogGraphCanvas
|
\qmltype LogGraphCanvas
|
||||||
|
@ -127,37 +126,6 @@ Canvas {
|
||||||
*/
|
*/
|
||||||
property int maxgradx: 20
|
property int maxgradx: 20
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlproperty var LogGraphCanvas::yaxisstepExpr
|
|
||||||
Expression for the y axis step (used to create labels).
|
|
||||||
*/
|
|
||||||
property var yaxisstepExpr: (new MathLib.Expression(`x*(${yaxisstep})`))
|
|
||||||
/*!
|
|
||||||
\qmlproperty double LogGraphCanvas::yaxisstep1
|
|
||||||
Value of the for the y axis step.
|
|
||||||
*/
|
|
||||||
property double yaxisstep1: yaxisstepExpr.execute(1)
|
|
||||||
/*!
|
|
||||||
\qmlproperty int LogGraphCanvas::drawMaxY
|
|
||||||
Minimum value of y that should be drawn onto the canvas.
|
|
||||||
*/
|
|
||||||
property int drawMaxY: Math.ceil(Math.max(Math.abs(ymax), Math.abs(px2y(canvasSize.height)))/yaxisstep1)
|
|
||||||
/*!
|
|
||||||
\qmlproperty var LogGraphCanvas::xaxisstepExpr
|
|
||||||
Expression for the x axis step (used to create labels).
|
|
||||||
*/
|
|
||||||
property var xaxisstepExpr: (new MathLib.Expression(`x*(${xaxisstep})`))
|
|
||||||
/*!
|
|
||||||
\qmlproperty double LogGraphCanvas::xaxisstep1
|
|
||||||
Value of the for the x axis step.
|
|
||||||
*/
|
|
||||||
property double xaxisstep1: xaxisstepExpr.execute(1)
|
|
||||||
/*!
|
|
||||||
\qmlproperty int LogGraphCanvas::drawMaxX
|
|
||||||
Maximum value of x that should be drawn onto the canvas.
|
|
||||||
*/
|
|
||||||
property int drawMaxX: Math.ceil(Math.max(Math.abs(xmin), Math.abs(px2x(canvasSize.width)))/xaxisstep1)
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty var LogGraphCanvas::imageLoaders
|
\qmlproperty var LogGraphCanvas::imageLoaders
|
||||||
Dictionary of format {image: [callback.image data]} containing data for defered image loading.
|
Dictionary of format {image: [callback.image data]} containing data for defered image loading.
|
||||||
|
@ -169,7 +137,10 @@ Canvas {
|
||||||
*/
|
*/
|
||||||
property var ctx
|
property var ctx
|
||||||
|
|
||||||
Component.onCompleted: imageLoaders = {}
|
Component.onCompleted: {
|
||||||
|
imageLoaders = {}
|
||||||
|
Modules.Canvas.initialize(canvas, drawingErrorDialog)
|
||||||
|
}
|
||||||
|
|
||||||
Native.MessageDialog {
|
Native.MessageDialog {
|
||||||
id: drawingErrorDialog
|
id: drawingErrorDialog
|
||||||
|
@ -184,27 +155,7 @@ Canvas {
|
||||||
onPaint: function(rect) {
|
onPaint: function(rect) {
|
||||||
//console.log('Redrawing')
|
//console.log('Redrawing')
|
||||||
if(rect.width == canvas.width) { // Redraw full canvas
|
if(rect.width == canvas.width) { // Redraw full canvas
|
||||||
ctx = getContext("2d");
|
Modules.Canvas.redraw()
|
||||||
reset(ctx)
|
|
||||||
drawGrille(ctx)
|
|
||||||
drawAxises(ctx)
|
|
||||||
drawLabels(ctx)
|
|
||||||
ctx.lineWidth = linewidth
|
|
||||||
for(var objType in Objects.currentObjects) {
|
|
||||||
for(var obj of Objects.currentObjects[objType]){
|
|
||||||
ctx.strokeStyle = obj.color
|
|
||||||
ctx.fillStyle = obj.color
|
|
||||||
if(obj.visible)
|
|
||||||
try {
|
|
||||||
obj.draw(canvas, ctx)
|
|
||||||
} catch(e) {
|
|
||||||
// Drawing throws an error. Generally, it's due to a new modification (or the opening of a file)
|
|
||||||
drawingErrorDialog.showDialog(objType, obj.name, e.message)
|
|
||||||
history.undo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.lineWidth = 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,172 +169,7 @@ Canvas {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmlmethod void LogGraphCanvas::reset(var ctx)
|
|
||||||
Resets the canvas to a blank one with default setting using 2D \c ctx.
|
|
||||||
*/
|
|
||||||
function reset(ctx){
|
|
||||||
// Reset
|
|
||||||
ctx.fillStyle = "#FFFFFF"
|
|
||||||
ctx.strokeStyle = "#000000"
|
|
||||||
ctx.font = `${canvas.textsize}px sans-serif`
|
|
||||||
ctx.fillRect(0,0,width,height)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drawing the log based graph
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod void LogGraphCanvas::drawGrille(var ctx)
|
|
||||||
Draws the grid using 2D \c ctx.
|
|
||||||
*/
|
|
||||||
function drawGrille(ctx) {
|
|
||||||
ctx.strokeStyle = "#C0C0C0"
|
|
||||||
if(logscalex) {
|
|
||||||
for(var xpow = -maxgradx; xpow <= maxgradx; xpow++) {
|
|
||||||
for(var xmulti = 1; xmulti < 10; xmulti++) {
|
|
||||||
drawXLine(ctx, Math.pow(10, xpow)*xmulti)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for(var x = 0; x < drawMaxX; x+=1) {
|
|
||||||
drawXLine(ctx, x*xaxisstep1)
|
|
||||||
drawXLine(ctx, -x*xaxisstep1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(var y = 0; y < drawMaxY; y+=1) {
|
|
||||||
drawYLine(ctx, y*yaxisstep1)
|
|
||||||
drawYLine(ctx, -y*yaxisstep1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod void LogGraphCanvas::drawAxises(var ctx)
|
|
||||||
Draws the graph axises using 2D \c ctx.
|
|
||||||
*/
|
|
||||||
function drawAxises(ctx) {
|
|
||||||
ctx.strokeStyle = "#000000"
|
|
||||||
var axisypos = logscalex ? 1 : 0
|
|
||||||
drawXLine(ctx, axisypos)
|
|
||||||
drawYLine(ctx, 0)
|
|
||||||
var axisypx = x2px(axisypos) // X coordinate of Y axis
|
|
||||||
var axisxpx = y2px(0) // Y coordinate of X axis
|
|
||||||
// Drawing arrows
|
|
||||||
drawLine(ctx, axisypx, 0, axisypx-10, 10)
|
|
||||||
drawLine(ctx, axisypx, 0, axisypx+10, 10)
|
|
||||||
drawLine(ctx, canvasSize.width, axisxpx, canvasSize.width-10, axisxpx-10)
|
|
||||||
drawLine(ctx, canvasSize.width, axisxpx, canvasSize.width-10, axisxpx+10)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod void LogGraphCanvas::drawLabels(var ctx)
|
|
||||||
Draws all labels (graduation & axises labels) using 2D \c ctx.
|
|
||||||
*/
|
|
||||||
function drawLabels(ctx) {
|
|
||||||
var axisypx = x2px(logscalex ? 1 : 0) // X coordinate of Y axis
|
|
||||||
var axisxpx = y2px(0) // Y coordinate of X axis
|
|
||||||
// Labels
|
|
||||||
ctx.fillStyle = "#000000"
|
|
||||||
ctx.font = `${canvas.textsize}px sans-serif`
|
|
||||||
ctx.fillText(ylabel, axisypx+10, 24)
|
|
||||||
var textSize = ctx.measureText(xlabel).width
|
|
||||||
ctx.fillText(xlabel, canvasSize.width-14-textSize, axisxpx-5)
|
|
||||||
// Axis graduation labels
|
|
||||||
ctx.font = `${canvas.textsize-4}px sans-serif`
|
|
||||||
|
|
||||||
var txtMinus = ctx.measureText('-').width
|
|
||||||
if(showxgrad) {
|
|
||||||
if(logscalex) {
|
|
||||||
for(var xpow = -maxgradx; xpow <= maxgradx; xpow+=1) {
|
|
||||||
var textSize = ctx.measureText("10"+Utils.textsup(xpow)).width
|
|
||||||
if(xpow != 0)
|
|
||||||
drawVisibleText(ctx, "10"+Utils.textsup(xpow), x2px(Math.pow(10,xpow))-textSize/2, axisxpx+16+(6*(xpow==1)))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for(var x = 1; x < drawMaxX; x += 1) {
|
|
||||||
var drawX = x*xaxisstep1
|
|
||||||
var txtX = xaxisstepExpr.simplify(x)
|
|
||||||
var textSize = measureText(ctx, txtX, 6).height
|
|
||||||
drawVisibleText(ctx, txtX, x2px(drawX)-4, axisxpx+textsize/2+textSize)
|
|
||||||
drawVisibleText(ctx, '-'+txtX, x2px(-drawX)-4, axisxpx+textsize/2+textSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(showygrad) {
|
|
||||||
for(var y = 0; y < drawMaxY; y += 1) {
|
|
||||||
var drawY = y*yaxisstep1
|
|
||||||
var txtY = yaxisstepExpr.simplify(y)
|
|
||||||
var textSize = ctx.measureText(txtY).width
|
|
||||||
drawVisibleText(ctx, txtY, axisypx-6-textSize, y2px(drawY)+4+(10*(y==0)))
|
|
||||||
if(y != 0)
|
|
||||||
drawVisibleText(ctx, '-'+txtY, axisypx-6-textSize-txtMinus, y2px(-drawY)+4)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.fillStyle = "#FFFFFF"
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod void LogGraphCanvas::drawXLine(var ctx, double x)
|
|
||||||
Draws an horizontal line at \c x plot coordinate using 2D \c ctx.
|
|
||||||
*/
|
|
||||||
function drawXLine(ctx, x) {
|
|
||||||
if(isVisible(x, ymax)) {
|
|
||||||
drawLine(ctx, x2px(x), 0, x2px(x), canvasSize.height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod void LogGraphCanvas::drawXLine(var ctx, double x)
|
|
||||||
Draws an vertical line at \c y plot coordinate using 2D \c ctx.
|
|
||||||
*/
|
|
||||||
function drawYLine(ctx, y) {
|
|
||||||
if(isVisible(xmin, y)) {
|
|
||||||
drawLine(ctx, 0, y2px(y), canvasSize.width, y2px(y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod void LogGraphCanvas::drawVisibleText(var ctx, string text, double x, double y)
|
|
||||||
Writes multline \c text onto the canvas using 2D \c ctx.
|
|
||||||
\note The \c x and \c y properties here are relative to the canvas, not the plot.
|
|
||||||
*/
|
|
||||||
function drawVisibleText(ctx, text, x, y) {
|
|
||||||
if(x > 0 && x < canvasSize.width && y > 0 && y < canvasSize.height) {
|
|
||||||
text.toString().split("\n").forEach(function(txt, i){
|
|
||||||
ctx.fillText(txt, x, y+(canvas.textsize*i))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod void LogGraphCanvas::drawVisibleImage(var ctx, var image, double x, double y)
|
|
||||||
Draws an \c image onto the canvas using 2D \c ctx.
|
|
||||||
\note The \c x, \c y \c width and \c height properties here are relative to the canvas, not the plot.
|
|
||||||
*/
|
|
||||||
function drawVisibleImage(ctx, image, x, y, width, height) {
|
|
||||||
//console.log("Drawing image", isImageLoaded(image), isImageError(image))
|
|
||||||
markDirty(Qt.rect(x, y, width, height));
|
|
||||||
ctx.drawImage(image, x, y, width, height)
|
|
||||||
/*if(true || (x > 0 && x < canvasSize.width && y > 0 && y < canvasSize.height)) {
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod var LogGraphCanvas::measureText(var ctx, string text)
|
|
||||||
Measures the wicth and height of a multiline \c text that would be drawn onto the canvas using 2D \c ctx.
|
|
||||||
Return format: dictionary {"width": width, "height": height}
|
|
||||||
*/
|
|
||||||
function measureText(ctx, text) {
|
|
||||||
let theight = 0
|
|
||||||
let twidth = 0
|
|
||||||
let defaultHeight = ctx.measureText("M").width // Approximate but good enough!
|
|
||||||
text.split("\n").forEach(function(txt, i){
|
|
||||||
theight += defaultHeight
|
|
||||||
if(ctx.measureText(txt).width > twidth) twidth = ctx.measureText(txt).width
|
|
||||||
})
|
|
||||||
return {'width': twidth, 'height': theight}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod double LogGraphCanvas::x2px(double x)
|
\qmlmethod double LogGraphCanvas::x2px(double x)
|
||||||
Converts an \c x coordinate to it's relative position on the canvas.
|
Converts an \c x coordinate to it's relative position on the canvas.
|
||||||
It supports both logarithmic and non logarithmic scale depending on the currently selected mode.
|
It supports both logarithmic and non logarithmic scale depending on the currently selected mode.
|
||||||
|
@ -423,71 +209,4 @@ Canvas {
|
||||||
function px2y(px) {
|
function px2y(px) {
|
||||||
return -(px/yzoom-ymax)
|
return -(px/yzoom-ymax)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod bool LogGraphCanvas::isVisible(double x, double y)
|
|
||||||
Checks whether a plot point (\c x, \c y) is visible or not on the canvas.
|
|
||||||
*/
|
|
||||||
function isVisible(x, y) {
|
|
||||||
return (x2px(x) >= 0 && x2px(x) <= canvasSize.width) && (y2px(y) >= 0 && y2px(y) <= canvasSize.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod bool LogGraphCanvas::drawLine(var ctx, double x1, double y1, double x2, double y2)
|
|
||||||
Draws a line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx.
|
|
||||||
*/
|
|
||||||
function drawLine(ctx, x1, y1, x2, y2) {
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(x1, y1);
|
|
||||||
ctx.lineTo(x2, y2);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod bool LogGraphCanvas::drawDashedLine2(var ctx, double x1, double y1, double x2, double y2)
|
|
||||||
Draws a dashed line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx.
|
|
||||||
*/
|
|
||||||
function drawDashedLine2(ctx, x1, y1, x2, y2, dashPxSize = 5) {
|
|
||||||
ctx.setLineDash([dashPxSize, dashPxSize]);
|
|
||||||
drawLine(ctx, x1, y1, x2, y2)
|
|
||||||
ctx.setLineDash([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod bool LogGraphCanvas::drawDashedLine(var ctx, double x1, double y1, double x2, double y2)
|
|
||||||
Draws a dashed line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx.
|
|
||||||
(Legacy slower method)
|
|
||||||
*/
|
|
||||||
function drawDashedLine(ctx, x1, y1, x2, y2, dashPxSize = 10) {
|
|
||||||
var distance = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))
|
|
||||||
var progPerc = dashPxSize/distance
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(x1, y1);
|
|
||||||
for(var i = 0; i < 1; i += progPerc) {
|
|
||||||
ctx.lineTo(x1-(x1-x2)*i, y1-(y1-y2)*i)
|
|
||||||
ctx.moveTo(x1-(x1-x2)*(i+progPerc/2), y1-(y1-y2)*(i+progPerc/2))
|
|
||||||
}
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod var LogGraphCanvas::renderLatexImage(string ltxText, color)
|
|
||||||
Renders latex markup \c ltxText to an image and loads it. Returns a dictionary with three values: source, width and height.
|
|
||||||
*/
|
|
||||||
function renderLatexImage(ltxText, color, callback) {
|
|
||||||
let [ltxSrc, ltxWidth, ltxHeight] = Latex.render(ltxText, textsize, color).split(",")
|
|
||||||
let imgData = {
|
|
||||||
"source": ltxSrc,
|
|
||||||
"width": parseFloat(ltxWidth),
|
|
||||||
"height": parseFloat(ltxHeight)
|
|
||||||
};
|
|
||||||
if(!isImageLoaded(ltxSrc) && !isImageLoading(ltxSrc)){
|
|
||||||
// Wait until the image is loaded to callback.
|
|
||||||
loadImage(ltxSrc)
|
|
||||||
imageLoaders[ltxSrc] = [callback, imgData]
|
|
||||||
} else {
|
|
||||||
// Callback directly
|
|
||||||
callback(canvas, ctx, imgData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -21,11 +21,10 @@ import QtQuick.Controls
|
||||||
import eu.ad5001.MixedMenu 1.1
|
import eu.ad5001.MixedMenu 1.1
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import QtQuick
|
import QtQuick
|
||||||
// Auto loading all objects.
|
|
||||||
import "js/objs/autoload.js" as ALObjects
|
|
||||||
|
|
||||||
import "js/objects.js" as Objects
|
// Auto loading all modules.
|
||||||
import "js/math/latex.js" as LatexJS
|
import "js/autoload.js" as ModulesAutoload
|
||||||
|
|
||||||
import eu.ad5001.LogarithmPlotter.History 1.0
|
import eu.ad5001.LogarithmPlotter.History 1.0
|
||||||
import eu.ad5001.LogarithmPlotter.ObjectLists 1.0
|
import eu.ad5001.LogarithmPlotter.ObjectLists 1.0
|
||||||
import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup
|
import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup
|
||||||
|
@ -45,16 +44,7 @@ ApplicationWindow {
|
||||||
color: sysPalette.window
|
color: sysPalette.window
|
||||||
title: "LogarithmPlotter " + (settings.saveFilename != "" ? " - " + settings.saveFilename.split('/').pop() : "") + (history.saved ? "" : "*")
|
title: "LogarithmPlotter " + (settings.saveFilename != "" ? " - " + settings.saveFilename.split('/').pop() : "") + (history.saved ? "" : "*")
|
||||||
|
|
||||||
SystemPalette {
|
SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active }
|
||||||
id: sysPalette; colorGroup: SystemPalette.Active
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
// LatexJS initialization.
|
|
||||||
LatexJS.enabled = Helper.getSettingBool("enable_latex")
|
|
||||||
LatexJS.Renderer = Latex
|
|
||||||
LatexJS.defaultColor = sysPalette.windowText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SystemPalette { id: sysPaletteIn; colorGroup: SystemPalette.Disabled }
|
SystemPalette { id: sysPaletteIn; colorGroup: SystemPalette.Disabled }
|
||||||
|
|
||||||
menuBar: appMenu.trueItem
|
menuBar: appMenu.trueItem
|
||||||
|
@ -65,6 +55,8 @@ ApplicationWindow {
|
||||||
|
|
||||||
Popup.GreetScreen {}
|
Popup.GreetScreen {}
|
||||||
|
|
||||||
|
Popup.Preferences {id: preferences}
|
||||||
|
|
||||||
Popup.Changelog {id: changelog}
|
Popup.Changelog {id: changelog}
|
||||||
|
|
||||||
Popup.About {id: about}
|
Popup.About {id: about}
|
||||||
|
@ -169,7 +161,7 @@ ApplicationWindow {
|
||||||
|
|
||||||
property bool firstDrawDone: false
|
property bool firstDrawDone: false
|
||||||
|
|
||||||
onPainted: if(!firstDrawDone) {
|
onPainted: if(!firstDrawDone) {
|
||||||
firstDrawDone = true;
|
firstDrawDone = true;
|
||||||
console.info("First paint done in " + (new Date().getTime()-(StartTime*1000)) + "ms")
|
console.info("First paint done in " + (new Date().getTime()-(StartTime*1000)) + "ms")
|
||||||
if(TestBuild == true) {
|
if(TestBuild == true) {
|
||||||
|
@ -178,6 +170,13 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ViewPositionChangeOverlay {
|
||||||
|
id: viewPositionChanger
|
||||||
|
anchors.fill: parent
|
||||||
|
canvas: parent
|
||||||
|
settingsInstance: settings
|
||||||
|
}
|
||||||
|
|
||||||
PickLocationOverlay {
|
PickLocationOverlay {
|
||||||
id: positionPicker
|
id: positionPicker
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -185,130 +184,6 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod void LogarithmPlotter::saveDiagram(string filename)
|
|
||||||
Saves the diagram to a certain \c filename.
|
|
||||||
*/
|
|
||||||
function saveDiagram(filename) {
|
|
||||||
if(['lpf'].indexOf(filename.split('.')[filename.split('.').length-1]) == -1)
|
|
||||||
filename += '.lpf'
|
|
||||||
settings.saveFilename = filename
|
|
||||||
var objs = {}
|
|
||||||
for(var objType in Objects.currentObjects){
|
|
||||||
objs[objType] = []
|
|
||||||
for(var obj of Objects.currentObjects[objType]) {
|
|
||||||
objs[objType].push(obj.export())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Helper.write(filename, JSON.stringify({
|
|
||||||
"xzoom": settings.xzoom,
|
|
||||||
"yzoom": settings.yzoom,
|
|
||||||
"xmin": settings.xmin,
|
|
||||||
"ymax": settings.ymax,
|
|
||||||
"xaxisstep": settings.xaxisstep,
|
|
||||||
"yaxisstep": settings.yaxisstep,
|
|
||||||
"xaxislabel": settings.xlabel,
|
|
||||||
"yaxislabel": settings.ylabel,
|
|
||||||
"logscalex": settings.logscalex,
|
|
||||||
"linewidth": settings.linewidth,
|
|
||||||
"showxgrad": settings.showxgrad,
|
|
||||||
"showygrad": settings.showygrad,
|
|
||||||
"textsize": settings.textsize,
|
|
||||||
"history": history.serialize(),
|
|
||||||
"width": root.width,
|
|
||||||
"height": root.height,
|
|
||||||
"objects": objs,
|
|
||||||
"type": "logplotv1"
|
|
||||||
}))
|
|
||||||
alert.show(qsTr("Saved plot to '%1'.").arg(filename.split("/").pop()))
|
|
||||||
history.saved = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod void LogarithmPlotter::saveDiagram(string filename)
|
|
||||||
Loads the diagram from a certain \c filename.
|
|
||||||
*/
|
|
||||||
function loadDiagram(filename) {
|
|
||||||
let basename = filename.split("/").pop()
|
|
||||||
alert.show(qsTr("Loading file '%1'.").arg(basename))
|
|
||||||
let data = JSON.parse(Helper.load(filename))
|
|
||||||
let error = "";
|
|
||||||
if(Object.keys(data).includes("type") && data["type"] == "logplotv1") {
|
|
||||||
history.clear()
|
|
||||||
// Importing settings
|
|
||||||
settings.saveFilename = filename
|
|
||||||
settings.xzoom = data["xzoom"]
|
|
||||||
settings.yzoom = data["yzoom"]
|
|
||||||
settings.xmin = data["xmin"]
|
|
||||||
settings.ymax = data["ymax"]
|
|
||||||
settings.xaxisstep = data["xaxisstep"]
|
|
||||||
settings.yaxisstep = data["yaxisstep"]
|
|
||||||
settings.xlabel = data["xaxislabel"]
|
|
||||||
settings.ylabel = data["yaxislabel"]
|
|
||||||
settings.logscalex = data["logscalex"]
|
|
||||||
if("showxgrad" in data)
|
|
||||||
settings.showxgrad = data["showxgrad"]
|
|
||||||
if("showygrad" in data)
|
|
||||||
settings.textsize = data["showygrad"]
|
|
||||||
if("linewidth" in data)
|
|
||||||
settings.linewidth = data["linewidth"]
|
|
||||||
if("textsize" in data)
|
|
||||||
settings.textsize = data["textsize"]
|
|
||||||
root.height = data["height"]
|
|
||||||
root.width = data["width"]
|
|
||||||
|
|
||||||
// Importing objects
|
|
||||||
Objects.currentObjects = {}
|
|
||||||
Object.keys(Objects.currentObjectsByName).forEach(key => {
|
|
||||||
delete 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] = []
|
|
||||||
for(let objData of data['objects'][objType]) {
|
|
||||||
let obj = new Objects.types[objType](...objData)
|
|
||||||
Objects.currentObjects[objType].push(obj)
|
|
||||||
Objects.currentObjectsByName[obj.name] = obj
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error += qsTr("Unknown object type: %1.").arg(objType) + "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updating object dependencies.
|
|
||||||
for(let objName in Objects.currentObjectsByName)
|
|
||||||
Objects.currentObjectsByName[objName].update()
|
|
||||||
|
|
||||||
// Importing history
|
|
||||||
if("history" in data)
|
|
||||||
history.unserialize(...data["history"])
|
|
||||||
|
|
||||||
// Refreshing sidebar
|
|
||||||
if(sidebarSelector.currentIndex == 0) {
|
|
||||||
// For some reason, if we load a file while the tab is on object,
|
|
||||||
// we get stuck in a Qt-side loop? Qt bug or side-effect here, I don't know.
|
|
||||||
sidebarSelector.currentIndex = 1
|
|
||||||
objectLists.update()
|
|
||||||
delayRefreshTimer.start()
|
|
||||||
} else {
|
|
||||||
objectLists.update()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error = qsTr("Invalid file provided.")
|
|
||||||
}
|
|
||||||
if(error != "") {
|
|
||||||
console.log(error)
|
|
||||||
alert.show(qsTr("Could not save file: ") + error)
|
|
||||||
// TODO: Error handling
|
|
||||||
return
|
|
||||||
}
|
|
||||||
drawCanvas.requestPaint()
|
|
||||||
alert.show(qsTr("Loaded file '%1'.").arg(basename))
|
|
||||||
history.saved = true
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: delayRefreshTimer
|
id: delayRefreshTimer
|
||||||
repeat: false
|
repeat: false
|
||||||
|
@ -330,6 +205,22 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlmethod void LogarithmPlotter::updateObjectsLists()
|
||||||
|
Updates the objects lists when loading a file.
|
||||||
|
*/
|
||||||
|
function updateObjectsLists() {
|
||||||
|
if(sidebarSelector.currentIndex === 0) {
|
||||||
|
// For some reason, if we load a file while the tab is on object,
|
||||||
|
// we get stuck in a Qt-side loop? Qt bug or side-effect here, I don't know.
|
||||||
|
sidebarSelector.currentIndex = 1
|
||||||
|
objectLists.update()
|
||||||
|
delayRefreshTimer.start()
|
||||||
|
} else {
|
||||||
|
objectLists.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmlmethod void LogarithmPlotter::copyDiagramToClipboard()
|
\qmlmethod void LogarithmPlotter::copyDiagramToClipboard()
|
||||||
Copies the current diagram image to the clipboard.
|
Copies the current diagram image to the clipboard.
|
||||||
|
@ -357,7 +248,7 @@ ApplicationWindow {
|
||||||
Action {
|
Action {
|
||||||
text: qsTr("&Update LogarithmPlotter")
|
text: qsTr("&Update LogarithmPlotter")
|
||||||
icon.name: 'update'
|
icon.name: 'update'
|
||||||
onTriggered: Qt.openUrlExternally("https://dev.apps.ad5001.eu/logarithmplotter")
|
onTriggered: Qt.openUrlExternally("https://apps.ad5001.eu/logarithmplotter/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,10 +20,9 @@ import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Qt.labs.platform as Native
|
import Qt.labs.platform as Native
|
||||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||||
import "../../js/objects.js" as Objects
|
import "../../js/historylib.mjs" as HistoryLib
|
||||||
import "../../js/historylib.js" as HistoryLib
|
import "../../js/utils.mjs" as Utils
|
||||||
import "../../js/utils.js" as Utils
|
import "../../js/mathlib.mjs" as MathLib
|
||||||
import "../../js/mathlib.js" as MathLib
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmltype CustomPropertyList
|
\qmltype CustomPropertyList
|
||||||
|
@ -91,7 +90,7 @@ Repeater {
|
||||||
root.changed()
|
root.changed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,14 +102,21 @@ Repeater {
|
||||||
height: 30
|
height: 30
|
||||||
label: propertyLabel
|
label: propertyLabel
|
||||||
icon: `settings/custom/${propertyIcon}.svg`
|
icon: `settings/custom/${propertyIcon}.svg`
|
||||||
isDouble: propertyType == 'number'
|
isDouble: propertyType == "number"
|
||||||
defValue: obj[propertyName] == null ? '' : obj[propertyName].toString()
|
defValue: obj[propertyName] == null ? '' : obj[propertyName].toString()
|
||||||
|
category: {
|
||||||
|
return {
|
||||||
|
"Domain": "domain",
|
||||||
|
"string": "all",
|
||||||
|
"number": "all"
|
||||||
|
}[propertyType]
|
||||||
|
}
|
||||||
onChanged: function(newValue) {
|
onChanged: function(newValue) {
|
||||||
try {
|
try {
|
||||||
var newValueParsed = {
|
var newValueParsed = {
|
||||||
'Domain': () => MathLib.parseDomain(newValue),
|
"Domain": () => MathLib.parseDomain(newValue),
|
||||||
'string': () => newValue,
|
"string": () => newValue,
|
||||||
'number': () => parseFloat(newValue)
|
"number": () => parseFloat(newValue)
|
||||||
}[propertyType]()
|
}[propertyType]()
|
||||||
|
|
||||||
// Ensuring old and new values are different to prevent useless adding to history.
|
// Ensuring old and new values are different to prevent useless adding to history.
|
||||||
|
@ -124,6 +130,7 @@ Repeater {
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
// Error in expression or domain
|
// Error in expression or domain
|
||||||
|
console.trace()
|
||||||
parsingErrorDialog.showDialog(propertyName, newValue, e.message)
|
parsingErrorDialog.showDialog(propertyName, newValue, e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,8 +188,8 @@ Repeater {
|
||||||
|
|
||||||
// Base, untranslated version of the model.
|
// Base, untranslated version of the model.
|
||||||
property var baseModel: selectObjMode ?
|
property var baseModel: selectObjMode ?
|
||||||
Objects.getObjectsName(propertyType.objType).concat(
|
Modules.Objects.getObjectsName(propertyType.objType).concat(
|
||||||
isRealObject ? [qsTr("+ Create new %1").arg(Objects.types[propertyType.objType].displayType())] : [])
|
isRealObject ? [qsTr("+ Create new %1").arg(Modules.Objects.types[propertyType.objType].displayType())] : [])
|
||||||
: propertyType.values
|
: propertyType.values
|
||||||
// Translated version of the model.
|
// Translated version of the model.
|
||||||
model: selectObjMode ? baseModel : propertyType.translatedValues
|
model: selectObjMode ? baseModel : propertyType.translatedValues
|
||||||
|
@ -192,20 +199,20 @@ Repeater {
|
||||||
if(selectObjMode) {
|
if(selectObjMode) {
|
||||||
// This is only done when what we're selecting are Objects.
|
// This is only done when what we're selecting are Objects.
|
||||||
// Setting object property.
|
// Setting object property.
|
||||||
var selectedObj = Objects.currentObjectsByName[baseModel[newIndex]]
|
var selectedObj = Modules.Objects.currentObjectsByName[baseModel[newIndex]]
|
||||||
if(newIndex != 0) {
|
if(newIndex != 0) {
|
||||||
// Make sure we don't set the object to null.
|
// Make sure we don't set the object to null.
|
||||||
if(selectedObj == null) {
|
if(selectedObj == null) {
|
||||||
// Creating new object.
|
// Creating new object.
|
||||||
selectedObj = Objects.createNewRegisteredObject(propertyType.objType)
|
selectedObj = Modules.Objects.createNewRegisteredObject(propertyType.objType)
|
||||||
history.addToHistory(new HistoryLib.CreateNewObject(selectedObj.name, propertyType.objType, selectedObj.export()))
|
history.addToHistory(new HistoryLib.CreateNewObject(selectedObj.name, propertyType.objType, selectedObj.export()))
|
||||||
baseModel = Objects.getObjectsName(propertyType.objType).concat(
|
baseModel = Modules.Objects.getObjectsName(propertyType.objType).concat(
|
||||||
isRealObject ? [qsTr("+ Create new %1").arg(Objects.types[propertyType.objType].displayType())] :
|
isRealObject ? [qsTr("+ Create new %1").arg(Modules.Objects.types[propertyType.objType].displayType())] :
|
||||||
[])
|
[])
|
||||||
currentIndex = baseModel.indexOf(selectedObj.name)
|
currentIndex = baseModel.indexOf(selectedObj.name)
|
||||||
}
|
}
|
||||||
selectedObj.requiredBy.push(Objects.currentObjects[objType][objIndex])
|
selectedObj.requiredBy.push(Modules.Objects.currentObjects[objType][objIndex])
|
||||||
//Objects.currentObjects[objType][objIndex].requiredBy = obj[propertyName].filter((obj) => obj.name != obj.name)
|
//Modules.Objects.currentObjects[objType][objIndex].requiredBy = obj[propertyName].filter((obj) => obj.name != obj.name)
|
||||||
}
|
}
|
||||||
obj.requiredBy = obj.requiredBy.filter((obj) => obj.name != obj.name)
|
obj.requiredBy = obj.requiredBy.filter((obj) => obj.name != obj.name)
|
||||||
history.addToHistory(new HistoryLib.EditedProperty(
|
history.addToHistory(new HistoryLib.EditedProperty(
|
||||||
|
@ -249,7 +256,7 @@ Repeater {
|
||||||
obj.name, objType, propertyName,
|
obj.name, objType, propertyName,
|
||||||
obj[propertyName], exported
|
obj[propertyName], exported
|
||||||
))
|
))
|
||||||
//Objects.currentObjects[objType][objIndex][propertyName] = exported
|
//Modules.Objects.currentObjects[objType][objIndex][propertyName] = exported
|
||||||
obj[propertyName] = exported
|
obj[propertyName] = exported
|
||||||
root.changed()
|
root.changed()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -22,11 +22,9 @@ import QtQuick.Dialogs as D
|
||||||
import Qt.labs.platform as Native
|
import Qt.labs.platform as Native
|
||||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||||
import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup
|
import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup
|
||||||
import "../../js/objects.js" as Objects
|
import "../../js/historylib.mjs" as HistoryLib
|
||||||
import "../../js/objs/common.js" as ObjectsCommons
|
import "../../js/utils.mjs" as Utils
|
||||||
import "../../js/historylib.js" as HistoryLib
|
import "../../js/mathlib.mjs" as MathLib
|
||||||
import "../../js/utils.js" as Utils
|
|
||||||
import "../../js/mathlib.js" as MathLib
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmltype Dialog
|
\qmltype Dialog
|
||||||
|
@ -54,7 +52,7 @@ Popup.BaseDialog {
|
||||||
\qmlproperty var EditorDialog::obj
|
\qmlproperty var EditorDialog::obj
|
||||||
Instance of the object being edited.
|
Instance of the object being edited.
|
||||||
*/
|
*/
|
||||||
property var obj: Objects.currentObjects[objType][objIndex]
|
property var obj: Modules.Objects.currentObjects[objType][objIndex]
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty var EditorDialog::posPicker
|
\qmlproperty var EditorDialog::posPicker
|
||||||
Reference to the global PositionPicker QML object.
|
Reference to the global PositionPicker QML object.
|
||||||
|
@ -87,7 +85,7 @@ Popup.BaseDialog {
|
||||||
Label {
|
Label {
|
||||||
id: dlgTitle
|
id: dlgTitle
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
text: qsTr("Edit properties of %1 %2").arg(Objects.types[objEditor.objType].displayType()).arg(objEditor.obj.name)
|
text: qsTr("Edit properties of %1 %2").arg(Modules.Objects.types[objEditor.objType].displayType()).arg(objEditor.obj.name)
|
||||||
font.pixelSize: 20
|
font.pixelSize: 20
|
||||||
color: sysPalette.windowText
|
color: sysPalette.windowText
|
||||||
}
|
}
|
||||||
|
@ -107,19 +105,20 @@ Popup.BaseDialog {
|
||||||
height: 30
|
height: 30
|
||||||
label: qsTr("Name")
|
label: qsTr("Name")
|
||||||
icon: "common/label.svg"
|
icon: "common/label.svg"
|
||||||
|
category: "name"
|
||||||
width: dlgProperties.width
|
width: dlgProperties.width
|
||||||
value: objEditor.obj.name
|
value: objEditor.obj.name
|
||||||
onChanged: function(newValue) {
|
onChanged: function(newValue) {
|
||||||
let newName = Utils.parseName(newValue)
|
let newName = Utils.parseName(newValue)
|
||||||
if(newName != '' && objEditor.obj.name != newName) {
|
if(newName != '' && objEditor.obj.name != newName) {
|
||||||
if(newName in Objects.currentObjectsByName) {
|
if(newName in Modules.Objects.currentObjectsByName) {
|
||||||
invalidNameDialog.showDialog(newName)
|
invalidNameDialog.showDialog(newName)
|
||||||
} else {
|
} else {
|
||||||
history.addToHistory(new HistoryLib.NameChanged(
|
history.addToHistory(new HistoryLib.NameChanged(
|
||||||
objEditor.obj.name, objEditor.objType, newName
|
objEditor.obj.name, objEditor.objType, newName
|
||||||
))
|
))
|
||||||
Objects.renameObject(obj.name, newName)
|
Modules.Objects.renameObject(obj.name, newName)
|
||||||
objEditor.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex]
|
objEditor.obj = Modules.Objects.currentObjects[objEditor.objType][objEditor.objIndex]
|
||||||
objectListList.update()
|
objectListList.update()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +163,7 @@ Popup.BaseDialog {
|
||||||
*/
|
*/
|
||||||
function open() {
|
function open() {
|
||||||
dlgCustomProperties.model = [] // Reset
|
dlgCustomProperties.model = [] // Reset
|
||||||
let objProps = Objects.types[objEditor.objType].properties()
|
let objProps = Modules.Objects.types[objEditor.objType].properties()
|
||||||
dlgCustomProperties.model = Object.keys(objProps).map(prop => [prop, objProps[prop]]) // Converted to 2-dimentional array.
|
dlgCustomProperties.model = Object.keys(objProps).map(prop => [prop, objProps[prop]]) // Converted to 2-dimentional array.
|
||||||
objEditor.show()
|
objEditor.show()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -18,8 +18,7 @@
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import "../js/objects.js" as Objects
|
import "../js/historylib.mjs" as HistoryLib
|
||||||
import "../js/historylib.js" as HistoryLib
|
|
||||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,6 +33,21 @@ Column {
|
||||||
id: createRow
|
id: createRow
|
||||||
property var objectEditor
|
property var objectEditor
|
||||||
property var objectLists
|
property var objectLists
|
||||||
|
property var posPicker
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlmethod int ObjectCreationGrid::openEditorDialog(var obj)
|
||||||
|
Opens the editor dialog for an object \c obj.
|
||||||
|
*/
|
||||||
|
function openEditorDialog(obj) {
|
||||||
|
// Open editor
|
||||||
|
objectEditor.obj = obj
|
||||||
|
objectEditor.objType = obj.type
|
||||||
|
objectEditor.objIndex = Modules.Objects.currentObjects[obj.type].indexOf(obj)
|
||||||
|
objectEditor.open()
|
||||||
|
// Disconnect potential link
|
||||||
|
posPicker.picked.disconnect(openEditorDialog)
|
||||||
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: createTitle
|
id: createTitle
|
||||||
|
@ -46,12 +60,12 @@ Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
columns: 3
|
columns: 3
|
||||||
Repeater {
|
Repeater {
|
||||||
model: Object.keys(Objects.types)
|
model: Object.keys(Modules.Objects.types)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: createBtn
|
id: createBtn
|
||||||
width: 96
|
width: 96
|
||||||
visible: Objects.types[modelData].createable()
|
visible: Modules.Objects.types[modelData].createable()
|
||||||
height: visible ? width*0.8 : 0
|
height: visible ? width*0.8 : 0
|
||||||
// The KDE SDK is kinda buggy, so it respects neither specified color nor display propreties.
|
// The KDE SDK is kinda buggy, so it respects neither specified color nor display propreties.
|
||||||
//display: AbstractButton.TextUnderIcon
|
//display: AbstractButton.TextUnderIcon
|
||||||
|
@ -79,7 +93,7 @@ Column {
|
||||||
anchors.rightMargin: 4
|
anchors.rightMargin: 4
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
font.pixelSize: 14
|
font.pixelSize: 14
|
||||||
text: Objects.types[modelData].displayType()
|
text: Modules.Objects.types[modelData].displayType()
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
clip: true
|
clip: true
|
||||||
}
|
}
|
||||||
|
@ -89,13 +103,26 @@ Column {
|
||||||
ToolTip.text: label.text
|
ToolTip.text: label.text
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var newObj = Objects.createNewRegisteredObject(modelData)
|
let newObj = Modules.Objects.createNewRegisteredObject(modelData)
|
||||||
history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export()))
|
history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export()))
|
||||||
objectLists.update()
|
objectLists.update()
|
||||||
objectEditor.obj = Objects.currentObjects[modelData][Objects.currentObjects[modelData].length - 1]
|
|
||||||
objectEditor.objType = modelData
|
let hasXProp = newObj.constructor.properties().hasOwnProperty('x')
|
||||||
objectEditor.objIndex = Objects.currentObjects[modelData].length - 1
|
let hasYProp = newObj.constructor.properties().hasOwnProperty('y')
|
||||||
objectEditor.open()
|
if(hasXProp || hasYProp) {
|
||||||
|
// Open picker
|
||||||
|
posPicker.objType = newObj.type
|
||||||
|
posPicker.objName = newObj.name
|
||||||
|
posPicker.pickX = hasXProp
|
||||||
|
posPicker.pickY = hasYProp
|
||||||
|
posPicker.propertyX = 'x'
|
||||||
|
posPicker.propertyY = 'y'
|
||||||
|
posPicker.visible = true
|
||||||
|
posPicker.picked.connect(openEditorDialog)
|
||||||
|
} else {
|
||||||
|
// Open editor
|
||||||
|
openEditorDialog(newObj)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -21,7 +21,6 @@ import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||||
import eu.ad5001.LogarithmPlotter.ObjectLists.Editor 1.0 as Editor
|
import eu.ad5001.LogarithmPlotter.ObjectLists.Editor 1.0 as Editor
|
||||||
import "../js/objects.js" as Objects
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmltype ObjectLists
|
\qmltype ObjectLists
|
||||||
|
@ -47,7 +46,7 @@ ScrollView {
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: objectsListView
|
id: objectsListView
|
||||||
model: Object.keys(Objects.types)
|
model: Object.keys(Modules.Objects.types)
|
||||||
//width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0)
|
//width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0)
|
||||||
implicitHeight: contentItem.childrenRect.height + footerItem.height + 10
|
implicitHeight: contentItem.childrenRect.height + footerItem.height + 10
|
||||||
|
|
||||||
|
@ -55,7 +54,7 @@ ScrollView {
|
||||||
id: objTypeList
|
id: objTypeList
|
||||||
property string objType: objectsListView.model[index]
|
property string objType: objectsListView.model[index]
|
||||||
property var editingRows: []
|
property var editingRows: []
|
||||||
model: Objects.currentObjects[objType]
|
model: Modules.Objects.currentObjects[objType]
|
||||||
width: objectsListView.width
|
width: objectsListView.width
|
||||||
implicitHeight: contentItem.childrenRect.height
|
implicitHeight: contentItem.childrenRect.height
|
||||||
visible: model != undefined && model.length > 0
|
visible: model != undefined && model.length > 0
|
||||||
|
@ -70,21 +69,23 @@ ScrollView {
|
||||||
|
|
||||||
CheckBox {
|
CheckBox {
|
||||||
id: typeVisibilityCheckBox
|
id: typeVisibilityCheckBox
|
||||||
checked: Objects.currentObjects[objType] != undefined ? Objects.currentObjects[objType].every(obj => obj.visible) : true
|
checked: Modules.Objects.currentObjects[objType] != undefined ? Modules.Objects.currentObjects[objType].every(obj => obj.visible) : true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
for(var obj of Objects.currentObjects[objType]) obj.visible = this.checked
|
for(var obj of Modules.Objects.currentObjects[objType]) obj.visible = this.checked
|
||||||
for(var obj of objTypeList.editingRows) obj.objVisible = this.checked
|
for(var obj of objTypeList.editingRows) obj.objVisible = this.checked
|
||||||
objectListList.changed()
|
objectListList.changed()
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.text: checked ? qsTr("Hide all %1").arg(Objects.types[objType].displayTypeMultiple()) : qsTr("Show all %1").arg(Objects.types[objType].displayTypeMultiple())
|
ToolTip.text: checked ?
|
||||||
|
qsTr("Hide all %1").arg(Modules.Objects.types[objType].displayTypeMultiple()) :
|
||||||
|
qsTr("Show all %1").arg(Modules.Objects.types[objType].displayTypeMultiple())
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: typeHeaderText
|
id: typeHeaderText
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
text: qsTranslate("control", "%1: ").arg(Objects.types[objType].displayTypeMultiple())
|
text: qsTranslate("control", "%1: ").arg(Modules.Objects.types[objType].displayTypeMultiple())
|
||||||
font.pixelSize: 20
|
font.pixelSize: 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,11 +93,11 @@ ScrollView {
|
||||||
delegate: ObjectRow {
|
delegate: ObjectRow {
|
||||||
id: controlRow
|
id: controlRow
|
||||||
width: objTypeList.width
|
width: objTypeList.width
|
||||||
obj: Objects.currentObjects[objType][index]
|
obj: Modules.Objects.currentObjects[objType][index]
|
||||||
posPicker: positionPicker
|
posPicker: positionPicker
|
||||||
|
|
||||||
onChanged: {
|
onChanged: {
|
||||||
obj = Objects.currentObjects[objType][index]
|
obj = Modules.Objects.currentObjects[objType][index]
|
||||||
objectListList.update()
|
objectListList.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +111,7 @@ ScrollView {
|
||||||
width: objectsListView.width
|
width: objectsListView.width
|
||||||
objectEditor: objEditor
|
objectEditor: objEditor
|
||||||
objectLists: objectListList
|
objectLists: objectListList
|
||||||
|
posPicker: positionPicker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +129,7 @@ ScrollView {
|
||||||
function update() {
|
function update() {
|
||||||
objectListList.changed()
|
objectListList.changed()
|
||||||
for(var objType in objectListList.listViews) {
|
for(var objType in objectListList.listViews) {
|
||||||
objectListList.listViews[objType].model = Objects.currentObjects[objType]
|
objectListList.listViews[objType].model = Modules.Objects.currentObjects[objType]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -21,9 +21,7 @@ import QtQuick.Dialogs
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Window
|
import QtQuick.Window
|
||||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||||
import "../js/objects.js" as Objects
|
import "../js/historylib.mjs" as HistoryLib
|
||||||
import "../js/historylib.js" as HistoryLib
|
|
||||||
import "../js/math/latex.js" as LatexJS
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -91,16 +89,16 @@ Item {
|
||||||
id: objDescription
|
id: objDescription
|
||||||
anchors.left: objVisibilityCheckBox.right
|
anchors.left: objVisibilityCheckBox.right
|
||||||
anchors.right: deleteButton.left
|
anchors.right: deleteButton.left
|
||||||
height: LatexJS.enabled ? Math.max(parent.minHeight, latexDescription.height+4) : parent.minHeight
|
height: Modules.Latex.enabled ? Math.max(parent.minHeight, latexDescription.height+4) : parent.minHeight
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
text: LatexJS.enabled ? "" : obj.getReadableString()
|
text: Modules.Latex.enabled ? "" : obj.getReadableString()
|
||||||
font.pixelSize: 14
|
font.pixelSize: 14
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: latexDescription
|
id: latexDescription
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
visible: LatexJS.enabled
|
visible: Modules.Latex.enabled
|
||||||
property double depth: Screen.devicePixelRatio
|
property double depth: Screen.devicePixelRatio
|
||||||
property var ltxInfo: visible ? Latex.render(obj.getLatexString(), depth*(parent.font.pixelSize+2), parent.color).split(",") : ["","0","0"]
|
property var ltxInfo: visible ? Latex.render(obj.getLatexString(), depth*(parent.font.pixelSize+2), parent.color).split(",") : ["","0","0"]
|
||||||
source: visible ? ltxInfo[0] : ""
|
source: visible ? ltxInfo[0] : ""
|
||||||
|
@ -111,7 +109,7 @@ Item {
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
objEditor.obj = Objects.currentObjects[obj.type][index]
|
objEditor.obj = Modules.Objects.currentObjects[obj.type][index]
|
||||||
objEditor.objType = obj.type
|
objEditor.objType = obj.type
|
||||||
objEditor.objIndex = index
|
objEditor.objIndex = index
|
||||||
//objEditor.editingRow = objectRow
|
//objEditor.editingRow = objectRow
|
||||||
|
@ -213,10 +211,14 @@ Item {
|
||||||
function deleteRecursively(object) {
|
function deleteRecursively(object) {
|
||||||
for(let toRemove of object.requiredBy)
|
for(let toRemove of object.requiredBy)
|
||||||
deleteRecursively(toRemove)
|
deleteRecursively(toRemove)
|
||||||
object.requiredBy = []
|
if(Modules.Objects.currentObjectsByName[object.name] != undefined) {
|
||||||
history.addToHistory(new HistoryLib.DeleteObject(
|
// Object still exists
|
||||||
object.name, object.type, object.export()
|
// Temporary fix for objects require not being propertly updated.
|
||||||
))
|
object.requiredBy = []
|
||||||
Objects.deleteObject(object.name)
|
history.addToHistory(new HistoryLib.DeleteObject(
|
||||||
|
object.name, object.type, object.export()
|
||||||
|
))
|
||||||
|
Modules.Objects.deleteObject(object.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,9 +19,8 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||||
import "js/objects.js" as Objects
|
import "js/mathlib.mjs" as MathLib
|
||||||
import "js/mathlib.js" as MathLib
|
import "js/historylib.mjs" as HistoryLib
|
||||||
import "js/historylib.js" as HistoryLib
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmltype PickLocationOverlay
|
\qmltype PickLocationOverlay
|
||||||
|
@ -39,6 +38,14 @@ Item {
|
||||||
visible: false
|
visible: false
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlsignal PickLocationOverlay::picked(var obj)
|
||||||
|
|
||||||
|
Emitted when a location has been picked
|
||||||
|
The corresponding handler is \c onPicked.
|
||||||
|
*/
|
||||||
|
signal picked(var obj)
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty var PickLocationOverlay::canvas
|
\qmlproperty var PickLocationOverlay::canvas
|
||||||
logGraphCanvas instance.
|
logGraphCanvas instance.
|
||||||
|
@ -106,7 +113,7 @@ Item {
|
||||||
if(mouse.button == Qt.LeftButton) { // Validate
|
if(mouse.button == Qt.LeftButton) { // Validate
|
||||||
let newValueX = !parent.userPickX ? null : parseValue(picked.mouseX.toString(), objType, propertyX)
|
let newValueX = !parent.userPickX ? null : parseValue(picked.mouseX.toString(), objType, propertyX)
|
||||||
let newValueY = !parent.userPickY ? null : parseValue(picked.mouseY.toString(), objType, propertyY)
|
let newValueY = !parent.userPickY ? null : parseValue(picked.mouseY.toString(), objType, propertyY)
|
||||||
let obj = Objects.currentObjectsByName[objName]
|
let obj = Modules.Objects.currentObjectsByName[objName]
|
||||||
// Set values
|
// Set values
|
||||||
if(parent.userPickX && parent.userPickY) {
|
if(parent.userPickX && parent.userPickY) {
|
||||||
history.addToHistory(new HistoryLib.EditedPosition(
|
history.addToHistory(new HistoryLib.EditedPosition(
|
||||||
|
@ -116,6 +123,7 @@ Item {
|
||||||
obj[propertyY] = newValueY
|
obj[propertyY] = newValueY
|
||||||
obj.update()
|
obj.update()
|
||||||
objectLists.update()
|
objectLists.update()
|
||||||
|
pickerRoot.picked(obj)
|
||||||
} else if(parent.userPickX) {
|
} else if(parent.userPickX) {
|
||||||
history.addToHistory(new HistoryLib.EditedProperty(
|
history.addToHistory(new HistoryLib.EditedProperty(
|
||||||
objName, objType, propertyX, obj[propertyX], newValueX
|
objName, objType, propertyX, obj[propertyX], newValueX
|
||||||
|
@ -123,6 +131,7 @@ Item {
|
||||||
obj[propertyX] = newValueX
|
obj[propertyX] = newValueX
|
||||||
obj.update()
|
obj.update()
|
||||||
objectLists.update()
|
objectLists.update()
|
||||||
|
pickerRoot.picked(obj)
|
||||||
} else if(parent.userPickY) {
|
} else if(parent.userPickY) {
|
||||||
history.addToHistory(new HistoryLib.EditedProperty(
|
history.addToHistory(new HistoryLib.EditedProperty(
|
||||||
objName, objType, propertyY, obj[propertyY], newValueY
|
objName, objType, propertyY, obj[propertyY], newValueY
|
||||||
|
@ -130,6 +139,7 @@ Item {
|
||||||
obj[propertyY] = newValueY
|
obj[propertyY] = newValueY
|
||||||
obj.update()
|
obj.update()
|
||||||
objectLists.update()
|
objectLists.update()
|
||||||
|
pickerRoot.picked(obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pickerRoot.visible = false;
|
pickerRoot.visible = false;
|
||||||
|
@ -252,7 +262,7 @@ Item {
|
||||||
color: 'black'
|
color: 'black'
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: canvas.x2px(picked.mouseX)
|
anchors.leftMargin: Modules.Canvas.x2px(picked.mouseX)
|
||||||
visible: parent.userPickX
|
visible: parent.userPickX
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +273,7 @@ Item {
|
||||||
color: 'black'
|
color: 'black'
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.topMargin: canvas.y2px(picked.mouseY)
|
anchors.topMargin: Modules.Canvas.y2px(picked.mouseY)
|
||||||
visible: parent.userPickY
|
visible: parent.userPickY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,25 +282,26 @@ Item {
|
||||||
x: picker.mouseX - width - 5
|
x: picker.mouseX - width - 5
|
||||||
y: picker.mouseY - height - 5
|
y: picker.mouseY - height - 5
|
||||||
color: 'black'
|
color: 'black'
|
||||||
property double axisX: canvas.xaxisstep1
|
property double axisX: Modules.Canvas.axesStep.x.value
|
||||||
|
property double axisY: Modules.Canvas.axesStep.y.value
|
||||||
property double mouseX: {
|
property double mouseX: {
|
||||||
let xpos = canvas.px2x(picker.mouseX)
|
let xpos = Modules.Canvas.px2x(picker.mouseX)
|
||||||
if(snapToGridCheckbox.checked) {
|
if(snapToGridCheckbox.checked) {
|
||||||
if(canvas.logscalex) {
|
if(canvas.logscalex) {
|
||||||
// Calculate the logged power
|
// Calculate the logged power
|
||||||
let pow = Math.pow(10, Math.floor(Math.log10(xpos)))
|
let pow = Math.pow(10, Math.floor(Math.log10(xpos)))
|
||||||
return pow*Math.round(xpos/pow)
|
return pow*Math.round(xpos/pow)
|
||||||
} else {
|
} else {
|
||||||
return canvas.xaxisstep1*Math.round(xpos/canvas.xaxisstep1)
|
return axisX*Math.round(xpos/axisX)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return xpos.toFixed(parent.precision)
|
return xpos.toFixed(parent.precision)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
property double mouseY: {
|
property double mouseY: {
|
||||||
let ypos = canvas.px2y(picker.mouseY)
|
let ypos = Modules.Canvas.px2y(picker.mouseY)
|
||||||
if(snapToGridCheckbox.checked) {
|
if(snapToGridCheckbox.checked) {
|
||||||
return canvas.yaxisstep1*Math.round(ypos/canvas.yaxisstep1)
|
return axisY*Math.round(ypos/axisY)
|
||||||
} else {
|
} else {
|
||||||
return ypos.toFixed(parent.precision)
|
return ypos.toFixed(parent.precision)
|
||||||
}
|
}
|
||||||
|
@ -313,7 +324,7 @@ Item {
|
||||||
Parses a given \c value as an expression or a number depending on the type of \c propertyName of all \c objType.
|
Parses a given \c value as an expression or a number depending on the type of \c propertyName of all \c objType.
|
||||||
*/
|
*/
|
||||||
function parseValue(value, objType, propertyName) {
|
function parseValue(value, objType, propertyName) {
|
||||||
if(Objects.types[objType].properties()[propertyName] == 'number')
|
if(Modules.Objects.types[objType].properties()[propertyName] == 'number')
|
||||||
return parseFloat(value)
|
return parseFloat(value)
|
||||||
else
|
else
|
||||||
return new MathLib.Expression(value)
|
return new MathLib.Expression(value)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -102,7 +102,7 @@ BaseDialog {
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
font.pixelSize: 13
|
font.pixelSize: 13
|
||||||
text: "Copyright © 2023 Ad5001 <mail@ad5001.eu><br>
|
text: "Copyright © 2021-2024 Ad5001 <mail@ad5001.eu><br>
|
||||||
<br>
|
<br>
|
||||||
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.<br>
|
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.<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -32,7 +32,7 @@ Popup {
|
||||||
id: changelogPopup
|
id: changelogPopup
|
||||||
x: (parent.width-width)/2
|
x: (parent.width-width)/2
|
||||||
y: Math.max(20, (parent.height-height)/2)
|
y: Math.max(20, (parent.height-height)/2)
|
||||||
width: changelog.width+40
|
width: 800
|
||||||
height: Math.min(parent.height-40, 500)
|
height: Math.min(parent.height-40, 500)
|
||||||
modal: true
|
modal: true
|
||||||
focus: true
|
focus: true
|
||||||
|
@ -44,33 +44,42 @@ Popup {
|
||||||
*/
|
*/
|
||||||
property bool changelogNeedsFetching: true
|
property bool changelogNeedsFetching: true
|
||||||
|
|
||||||
onAboutToShow: if(changelogNeedsFetching) Helper.fetchChangelog()
|
onAboutToShow: if(changelogNeedsFetching) {
|
||||||
|
Helper.fetchChangelog()
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Helper
|
target: Helper
|
||||||
function onChangelogFetched(chl) {
|
function onChangelogFetched(chl) {
|
||||||
changelogNeedsFetching = false;
|
changelogNeedsFetching = false;
|
||||||
changelog.text = chl
|
changelog.text = chl
|
||||||
|
changelogView.contentItem.implicitHeight = changelog.height
|
||||||
|
// console.log(changelog.height, changelogView.contentItem.implicitHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
id: changelogView
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: 10
|
anchors.topMargin: 10
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 10
|
||||||
anchors.bottom: doneBtn.top
|
anchors.bottom: doneBtn.top
|
||||||
anchors.bottomMargin: 10
|
anchors.bottomMargin: 10
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: changelog
|
id: changelog
|
||||||
color: sysPalette.windowText
|
color: sysPalette.windowText
|
||||||
|
width: 760
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
textFormat: TextEdit.MarkdownText
|
textFormat: TextEdit.MarkdownText
|
||||||
|
|
||||||
text: qsTr("Fetching changelog...")
|
text: qsTr("Fetching changelog...")
|
||||||
onLinkActivated: Qt.openUrlExternally(link)
|
onLinkActivated: Qt.openUrlExternally(link)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +96,7 @@ Popup {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: doneBtn
|
id: doneBtn
|
||||||
text: qsTr("Done")
|
text: qsTr("Close")
|
||||||
font.pixelSize: 18
|
font.pixelSize: 18
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.bottomMargin: 7
|
anchors.bottomMargin: 7
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import "../js/math/latex.js" as Latex
|
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmltype GreetScreen
|
\qmltype GreetScreen
|
||||||
|
@ -33,202 +33,122 @@ Popup {
|
||||||
id: greetingPopup
|
id: greetingPopup
|
||||||
x: (parent.width-width)/2
|
x: (parent.width-width)/2
|
||||||
y: Math.max(20, (parent.height-height)/2)
|
y: Math.max(20, (parent.height-height)/2)
|
||||||
width: Math.max(welcome.width+70, checkForUpdatesSetting.width, resetRedoStackSetting.width)+20
|
width: greetingLayout.width+20
|
||||||
height: Math.min(parent.height-40, 700)
|
height: Math.min(parent.height-40, 700)
|
||||||
modal: true
|
modal: true
|
||||||
focus: true
|
focus: true
|
||||||
clip: true
|
clip: true
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
|
||||||
ScrollView {
|
Column {
|
||||||
anchors.left: parent.left
|
id: greetingLayout
|
||||||
anchors.right: parent.right
|
width: 600
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: bottomButtons.height + 20
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
Column {
|
|
||||||
width: greetingPopup.width - 25
|
|
||||||
spacing: 10
|
|
||||||
clip: true
|
|
||||||
topPadding: 35
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: welcome
|
|
||||||
height: logo.height
|
|
||||||
spacing: 10
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: logo
|
|
||||||
source: "../icons/logarithmplotter.svg"
|
|
||||||
sourceSize.width: 48
|
|
||||||
sourceSize.height: 48
|
|
||||||
width: 48
|
|
||||||
height: 48
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: welcomeText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
font.pixelSize: 32
|
|
||||||
text: qsTr("Welcome to LogarithmPlotter")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: versionText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
width: implicitWidth
|
|
||||||
font.pixelSize: 18
|
|
||||||
font.italic: true
|
|
||||||
text: qsTr("Version %1").arg(Helper.getVersion())
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: helpText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
font.pixelSize: 14
|
|
||||||
width: parent.width - 50
|
|
||||||
text: qsTr("Take a few seconds to configure LogarithmPlotter.\nThese settings can be changed at any time from the \"Settings\" menu.")
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
id: checkForUpdatesSetting
|
|
||||||
anchors.left: parent.left
|
|
||||||
checked: Helper.getSettingBool("check_for_updates")
|
|
||||||
text: qsTr('Check for updates on startup (requires online connectivity)')
|
|
||||||
onClicked: {
|
|
||||||
Helper.setSettingBool("check_for_updates", checked)
|
|
||||||
// Set in the menu bar
|
|
||||||
appMenu.settingsMenu.children[0].checked = checked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
id: resetRedoStackSetting
|
|
||||||
anchors.left: parent.left
|
|
||||||
checked: Helper.getSettingBool("reset_redo_stack")
|
|
||||||
text: qsTr('Reset redo stack when a new action is added to history')
|
|
||||||
onClicked: {
|
|
||||||
Helper.setSettingBool("reset_redo_stack", checked)
|
|
||||||
appMenu.settingsMenu.children[1].checked = checked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
id: enableLatexSetting
|
|
||||||
anchors.left: parent.left
|
|
||||||
checked: Helper.getSettingBool("enable_latex")
|
|
||||||
text: qsTr('Enable LaTeX rendering')
|
|
||||||
onClicked: {
|
|
||||||
Helper.setSettingBool("enable_latex", checked)
|
|
||||||
appMenu.settingsMenu.children[2].checked = checked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
id: autocloseFormulaSetting
|
|
||||||
anchors.left: parent.left
|
|
||||||
checked: Helper.getSettingBool("expression_editor.autoclose")
|
|
||||||
text: qsTr('Automatically close parenthesises and brackets in expressions')
|
|
||||||
onClicked: {
|
|
||||||
Helper.setSettingBool("expression_editor.autoclose", checked)
|
|
||||||
appMenu.settingsMenu.children[3].children[0].checked = checked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
id: colorizeFormulaSetting
|
|
||||||
anchors.left: parent.left
|
|
||||||
checked: Helper.getSettingBool("expression_editor.colorize")
|
|
||||||
text: qsTr('Enable syntax highlighting for expressions')
|
|
||||||
onClicked: {
|
|
||||||
Helper.setSettingBool("expression_editor.colorize", checked)
|
|
||||||
appMenu.settingsMenu.children[3].children[1].checked = checked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
id: autocompleteFormulaSetting
|
|
||||||
anchors.left: parent.left
|
|
||||||
checked: Helper.getSettingBool("autocompletion.enabled")
|
|
||||||
text: qsTr('Enable autocompletion interface in expression editor')
|
|
||||||
onClicked: {
|
|
||||||
Helper.setSettingBool("autocompletion.enabled", checked)
|
|
||||||
appMenu.settingsMenu.children[3].children[2].checked = checked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: 10
|
|
||||||
spacing: 10
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: colorSchemeLabel
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
text: qsTr("Color scheme:")
|
|
||||||
}
|
|
||||||
|
|
||||||
ComboBox {
|
|
||||||
model: ["Breeze Light", "Breeze Dark", "Solarized", "Github Light", "Github Dark", "Nord", "Monokai"]
|
|
||||||
currentIndex: Helper.getSettingInt("expression_editor.color_scheme")
|
|
||||||
|
|
||||||
onActivated: function(index) {
|
|
||||||
Helper.setSettingInt("expression_editor.color_scheme", index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: bottomSeparator
|
|
||||||
opacity: 0.3
|
|
||||||
color: sysPalette.windowText
|
|
||||||
width: parent.width * 2 / 3
|
|
||||||
height: 1
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.bottom: bottomButtons.top
|
|
||||||
anchors.bottomMargin: 9
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: bottomButtons
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: 7
|
|
||||||
spacing: 10
|
spacing: 10
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
clip: true
|
||||||
|
topPadding: 35
|
||||||
|
|
||||||
Button {
|
Row {
|
||||||
id: userManualBtn
|
id: welcome
|
||||||
text: qsTr("User manual")
|
height: logo.height
|
||||||
font.pixelSize: 18
|
spacing: 10
|
||||||
onClicked: Qt.openUrlExternally("https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/_Sidebar")
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: logo
|
||||||
|
source: "../icons/logarithmplotter.svg"
|
||||||
|
sourceSize.width: 48
|
||||||
|
sourceSize.height: 48
|
||||||
|
width: 48
|
||||||
|
height: 48
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: welcomeText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
font.pixelSize: 32
|
||||||
|
text: qsTr("Welcome to LogarithmPlotter")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Label {
|
||||||
id: changelogBtn
|
id: versionText
|
||||||
text: qsTr("Changelog")
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: implicitWidth
|
||||||
font.pixelSize: 18
|
font.pixelSize: 18
|
||||||
onClicked: changelog.open()
|
font.italic: true
|
||||||
}
|
text: qsTr("Version %1").arg(Helper.getVersion())
|
||||||
|
|
||||||
Button {
|
|
||||||
id: doneBtn
|
|
||||||
text: qsTr("Done")
|
|
||||||
font.pixelSize: 18
|
|
||||||
onClicked: greetingPopup.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: greetingLayout.bottom
|
||||||
|
anchors.topMargin: 50
|
||||||
|
columns: 2
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: [{
|
||||||
|
name: qsTr("Changelog"),
|
||||||
|
icon: 'common/new.svg',
|
||||||
|
onClicked: () => changelog.open()
|
||||||
|
},{
|
||||||
|
name: qsTr("Preferences"),
|
||||||
|
icon: 'common/settings.svg',
|
||||||
|
onClicked: () => preferences.open()
|
||||||
|
},{
|
||||||
|
name: qsTr("User manual"),
|
||||||
|
icon: 'common/manual.svg',
|
||||||
|
onClicked: () => Qt.openUrlExternally("https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/_Sidebar")
|
||||||
|
},{
|
||||||
|
name: qsTr("Close"),
|
||||||
|
icon: 'common/close.svg',
|
||||||
|
onClicked: () => greetingPopup.close()
|
||||||
|
}]
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: createBtn
|
||||||
|
width: 96
|
||||||
|
height: 96
|
||||||
|
onClicked: modelData.onClicked()
|
||||||
|
|
||||||
Component.onCompleted: if(Helper.getSetting("last_install_greet") != Helper.getVersion()) {
|
Setting.Icon {
|
||||||
|
id: icon
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: (parent.width-width)/2
|
||||||
|
top: parent.top
|
||||||
|
topMargin: (label.y-height)/2
|
||||||
|
}
|
||||||
|
color: sysPalette.windowText
|
||||||
|
source: '../icons/' + modelData.icon
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: label
|
||||||
|
anchors {
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: 5
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 4
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 4
|
||||||
|
}
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
font.pixelSize: 14
|
||||||
|
text: modelData.name
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
clip: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: if(Helper.getSetting("last_install_greet") != Helper.getVersion()+1) {
|
||||||
greetingPopup.open()
|
greetingPopup.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -31,8 +31,19 @@ Popup {
|
||||||
|
|
||||||
signal selected(string character)
|
signal selected(string character)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlproperty string InsertCharacter::category
|
||||||
|
Type of special character to insert.
|
||||||
|
Possible values:
|
||||||
|
- expression
|
||||||
|
- domain
|
||||||
|
- name
|
||||||
|
- all
|
||||||
|
*/
|
||||||
|
property string category: 'all'
|
||||||
|
|
||||||
width: 280
|
width: 280
|
||||||
height: insertGrid.insertChars.length/insertGrid.columns*(width/insertGrid.columns)
|
height: Math.ceil(insertGrid.insertChars.length/insertGrid.columns)*(width/insertGrid.columns)+5
|
||||||
modal: true
|
modal: true
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||||
|
|
||||||
|
@ -41,17 +52,40 @@ Popup {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
columns: 7
|
columns: 7
|
||||||
|
|
||||||
property var insertChars: [
|
property var insertCharsExpression: [
|
||||||
|
"∞","π","¹","²","³","⁴","⁵",
|
||||||
|
"⁶","⁷","⁸","⁹","⁰"
|
||||||
|
]
|
||||||
|
|
||||||
|
property var insertCharsDomain: [
|
||||||
|
"∅","∪","∩","∖","ℝ","ℕ","ℤ",
|
||||||
|
"⁺","⁻",...insertCharsExpression
|
||||||
|
]
|
||||||
|
|
||||||
|
property var insertCharsName: [
|
||||||
"α","β","γ","δ","ε","ζ","η",
|
"α","β","γ","δ","ε","ζ","η",
|
||||||
"π","θ","κ","λ","μ","ξ","ρ",
|
"π","θ","κ","λ","μ","ξ","ρ",
|
||||||
"ς","σ","τ","φ","χ","ψ","ω",
|
"ς","σ","τ","φ","χ","ψ","ω",
|
||||||
"Γ","Δ","Θ","Λ","Ξ","Π","Σ",
|
"Γ","Δ","Θ","Λ","Ξ","Π","Σ",
|
||||||
"Φ","Ψ","Ω","ₐ","ₑ","ₒ","ₓ",
|
"Φ","Ψ","Ω","ₐ","ₑ","ₒ","ₓ",
|
||||||
"ₕ","ₖ","ₗ","ₘ","ₙ","ₚ","ₛ",
|
"ₕ","ₖ","ₗ","ₘ","ₙ","ₚ","ₛ",
|
||||||
"ₜ","¹","²","³","⁴","⁵","⁶",
|
"ₜ","₁","₂","₃","₄","₅","₆",
|
||||||
"⁷","⁸","⁹","⁰","₁","₂","₃",
|
"₇","₈","₉","₀"
|
||||||
"₄","₅","₆","₇","₈","₉","₀"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
property var insertCharsAll: [
|
||||||
|
...insertCharsName, ...insertCharsDomain
|
||||||
|
]
|
||||||
|
|
||||||
|
property var insertChars: {
|
||||||
|
return {
|
||||||
|
"expression": insertCharsExpression,
|
||||||
|
"domain": insertCharsDomain,
|
||||||
|
"name": insertCharsName,
|
||||||
|
"all": insertCharsAll
|
||||||
|
}[insertPopup.category]
|
||||||
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: parent.insertChars.length
|
model: parent.insertChars.length
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,257 @@
|
||||||
|
/**
|
||||||
|
* 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 QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||||
|
import "../js/preferences/common.mjs" as S
|
||||||
|
import "../js/utils.mjs" as Utils
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmltype Preferences
|
||||||
|
\inqmlmodule eu.ad5001.LogarithmPlotter.Popup
|
||||||
|
\brief Popup to change global application preferences.
|
||||||
|
|
||||||
|
\sa LogarithmPlotter, GreetScreen
|
||||||
|
*/
|
||||||
|
Popup {
|
||||||
|
id: preferencesPopup
|
||||||
|
x: (parent.width-width)/2
|
||||||
|
y: Math.max(20, (parent.height-height)/2)
|
||||||
|
width: settingPopupRow.width + 30
|
||||||
|
height: settingPopupRow.height + 20
|
||||||
|
modal: true
|
||||||
|
focus: true
|
||||||
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
|
||||||
|
// Components for the preferences
|
||||||
|
Component {
|
||||||
|
id: boolSettingComponent
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
height: 20
|
||||||
|
text: setting.displayName
|
||||||
|
checked: setting.value()
|
||||||
|
onClicked: setting.set(this.checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: enumIntSettingComponent
|
||||||
|
|
||||||
|
// Setting when selecting data from an enum, or an object of a certain type.
|
||||||
|
Setting.ComboBoxSetting {
|
||||||
|
height: 30
|
||||||
|
label: setting.displayName
|
||||||
|
icon: `settings/${setting.icon}.svg`
|
||||||
|
currentIndex: setting.value()
|
||||||
|
model: setting.values
|
||||||
|
onActivated: function(newIndex) { setting.set(newIndex) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: stringSettingComponent
|
||||||
|
|
||||||
|
Setting.ComboBoxSetting {
|
||||||
|
height: 30
|
||||||
|
label: setting.displayName
|
||||||
|
icon: `settings/${setting.icon}.svg`
|
||||||
|
editable: true
|
||||||
|
currentIndex: find(setting.value())
|
||||||
|
model: setting.defaultValues
|
||||||
|
onAccepted: function() {
|
||||||
|
editText = Utils.parseName(editText, false)
|
||||||
|
if(find(editText) === -1) model.append(editText)
|
||||||
|
setting.set(editText)
|
||||||
|
}
|
||||||
|
onActivated: function(selectedId) {
|
||||||
|
setting.set(model[selectedId])
|
||||||
|
}
|
||||||
|
Component.onCompleted: editText = setting.value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: numberSettingComponent
|
||||||
|
|
||||||
|
Setting.TextSetting {
|
||||||
|
height: 30
|
||||||
|
isDouble: true
|
||||||
|
label: setting.displayName
|
||||||
|
min: setting.min()
|
||||||
|
icon: `settings/${setting.icon}.svg`
|
||||||
|
value: setting.value()
|
||||||
|
onChanged: function(newValue) {
|
||||||
|
if(newValue < setting.max())
|
||||||
|
setting.set(newValue)
|
||||||
|
else {
|
||||||
|
value = setting.max()
|
||||||
|
setting.set(setting.max())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: expressionSettingComponent
|
||||||
|
|
||||||
|
Setting.ExpressionEditor {
|
||||||
|
height: 30
|
||||||
|
label: setting.displayName
|
||||||
|
icon: `settings/${setting.icon}.svg`
|
||||||
|
defValue: Utils.simplifyExpression(setting.value())
|
||||||
|
variables: setting.variables
|
||||||
|
allowGraphObjects: false
|
||||||
|
property string propertyName: setting.displayName
|
||||||
|
onChanged: function(newExpr) {
|
||||||
|
try {
|
||||||
|
setting.set(newExpr)
|
||||||
|
} catch(e) {
|
||||||
|
errorDialog.showDialog(propertyName, newExpr, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: settingPopupRow
|
||||||
|
height: 300
|
||||||
|
width: categories.width + categorySeparator.width + settingView.width + 70
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
topMargin: 10
|
||||||
|
bottomMargin: 10
|
||||||
|
rightMargin: 15
|
||||||
|
leftMargin: 15
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: categories
|
||||||
|
width: 150
|
||||||
|
height: parent.height
|
||||||
|
spacing: 0
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Object.keys(Modules.Preferences.categories)
|
||||||
|
|
||||||
|
Button {
|
||||||
|
// width: 150
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: qsTranslate('settingCategory', modelData)
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
settingView.model = Modules.Preferences.categories[modelData]
|
||||||
|
settingView.name = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: closeButton
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
text: qsTr('Close')
|
||||||
|
onClicked: preferencesPopup.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: categorySeparator
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 5
|
||||||
|
}
|
||||||
|
opacity: 0.3
|
||||||
|
color: sysPalette.windowText
|
||||||
|
height: parent.height - 10
|
||||||
|
width: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: settingView
|
||||||
|
clip: true
|
||||||
|
width: 500
|
||||||
|
spacing: 10
|
||||||
|
model: Modules.Preferences.categories.general
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
ScrollBar.vertical: ScrollBar { }
|
||||||
|
property string name: qsTranslate('settingCategory', 'general')
|
||||||
|
|
||||||
|
|
||||||
|
header: Text {
|
||||||
|
id: settingCategoryName
|
||||||
|
font.pixelSize: 32
|
||||||
|
height: 48
|
||||||
|
color: sysPalette.windowText
|
||||||
|
text: settingView.name
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: bottomSeparator
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 8
|
||||||
|
opacity: 0.3
|
||||||
|
color: sysPalette.windowText
|
||||||
|
width: settingView.width
|
||||||
|
height: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Component {
|
||||||
|
Loader {
|
||||||
|
width: settingView.width * 2 / 3
|
||||||
|
property var setting: modelData
|
||||||
|
sourceComponent: {
|
||||||
|
if(setting instanceof S.BoolSetting)
|
||||||
|
return boolSettingComponent
|
||||||
|
else if(setting instanceof S.EnumIntSetting)
|
||||||
|
return enumIntSettingComponent
|
||||||
|
else if(setting instanceof S.NumberSetting)
|
||||||
|
return numberSettingComponent
|
||||||
|
else if(setting instanceof S.ExpressionSetting)
|
||||||
|
return expressionSettingComponent
|
||||||
|
else if(setting instanceof S.StringSetting)
|
||||||
|
return stringSettingComponent
|
||||||
|
else
|
||||||
|
console.log('Unknown setting type!', modelData.constructor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component.onCompleted: open()
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -8,3 +8,4 @@ GreetScreen 1.0 GreetScreen.qml
|
||||||
Changelog 1.0 Changelog.qml
|
Changelog 1.0 Changelog.qml
|
||||||
ThanksTo 1.0 ThanksTo.qml
|
ThanksTo 1.0 ThanksTo.qml
|
||||||
InsertCharacter 1.0 InsertCharacter.qml
|
InsertCharacter 1.0 InsertCharacter.qml
|
||||||
|
Preferences 1.0 Preferences.qml
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -113,9 +113,11 @@ ListView {
|
||||||
Text {
|
Text {
|
||||||
id: annotationText
|
id: annotationText
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
topPadding: 2
|
topPadding: 2
|
||||||
bottomPadding: 2
|
bottomPadding: 2
|
||||||
rightPadding: 15
|
rightPadding: 15
|
||||||
|
font.pixelSize: autocompleteText.font.pixelSize - 2
|
||||||
text: listFiltered.model[index].annotation
|
text: listFiltered.model[index].annotation
|
||||||
color: parent.selected ? sysPaletteIn.highlightedText : sysPaletteIn.windowText
|
color: parent.selected ? sysPaletteIn.highlightedText : sysPaletteIn.windowText
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -114,6 +114,7 @@ Item {
|
||||||
anchors.left: iconLabel.right
|
anchors.left: iconLabel.right
|
||||||
anchors.leftMargin: icon == "" ? 0 : 5
|
anchors.leftMargin: icon == "" ? 0 : 5
|
||||||
height: 30
|
height: 30
|
||||||
|
width: Math.max(85, implicitWidth)
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
text: qsTranslate("control", "%1: ").arg(control.label)
|
text: qsTranslate("control", "%1: ").arg(control.label)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,10 +20,9 @@ import QtQuick.Controls
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Qt.labs.platform as Native
|
import Qt.labs.platform as Native
|
||||||
import eu.ad5001.LogarithmPlotter.Popup 1.0 as P
|
import eu.ad5001.LogarithmPlotter.Popup 1.0 as P
|
||||||
import "../js/mathlib.js" as MathLib
|
import "../js/mathlib.mjs" as MathLib
|
||||||
import "../js/utils.js" as Utils
|
import "../js/utils.mjs" as Utils
|
||||||
import "../js/objects.js" as Objects
|
import "../js/parsing/parsing.mjs" as Parsing
|
||||||
import "../js/parsing/parsing.js" as Parsing
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -81,6 +80,17 @@ Item {
|
||||||
Icon path of the editor.
|
Icon path of the editor.
|
||||||
*/
|
*/
|
||||||
property string icon: ""
|
property string icon: ""
|
||||||
|
/*!
|
||||||
|
\qmlproperty bool ExpressionEditor::allowGraphObjects
|
||||||
|
If true, allows graph objects to be used as part of the expression.
|
||||||
|
*/
|
||||||
|
property bool allowGraphObjects: true
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlproperty var ExpressionEditor::errorDialog
|
||||||
|
Allows to summon the error dialog when using additional external parsing.
|
||||||
|
*/
|
||||||
|
readonly property alias errorDialog: parsingErrorDialog
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty string ExpressionEditor::openAndCloseMatches
|
\qmlproperty string ExpressionEditor::openAndCloseMatches
|
||||||
|
@ -178,8 +188,9 @@ Item {
|
||||||
id: labelItem
|
id: labelItem
|
||||||
anchors.left: iconLabel.right
|
anchors.left: iconLabel.right
|
||||||
anchors.leftMargin: icon == "" ? 0 : 5
|
anchors.leftMargin: icon == "" ? 0 : 5
|
||||||
height: parent.height
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
|
height: parent.height
|
||||||
|
width: Math.max(85, implicitWidth)
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
//color: sysPalette.windowText
|
//color: sysPalette.windowText
|
||||||
text: visible ? qsTranslate("control", "%1: ").arg(control.label) : ""
|
text: visible ? qsTranslate("control", "%1: ").arg(control.label) : ""
|
||||||
|
@ -381,7 +392,7 @@ Item {
|
||||||
id: objectPropertiesList
|
id: objectPropertiesList
|
||||||
|
|
||||||
category: qsTr("Object Properties")
|
category: qsTr("Object Properties")
|
||||||
visbilityCondition: doesObjectExist
|
visbilityCondition: control.allowGraphObjects && doesObjectExist
|
||||||
itemStartIndex: 0
|
itemStartIndex: 0
|
||||||
itemSelected: parent.itemSelected
|
itemSelected: parent.itemSelected
|
||||||
property bool isEnteringProperty: (
|
property bool isEnteringProperty: (
|
||||||
|
@ -392,9 +403,9 @@ Item {
|
||||||
property string objectName: isEnteringProperty ?
|
property string objectName: isEnteringProperty ?
|
||||||
(parent.currentToken.dot ? parent.previousToken.value : parent.previousToken2.value)
|
(parent.currentToken.dot ? parent.previousToken.value : parent.previousToken2.value)
|
||||||
: ""
|
: ""
|
||||||
property bool doesObjectExist: isEnteringProperty && (objectName in Objects.currentObjectsByName)
|
property bool doesObjectExist: isEnteringProperty && (objectName in Modules.Objects.currentObjectsByName)
|
||||||
property var objectProperties: doesObjectExist ?
|
property var objectProperties: doesObjectExist ?
|
||||||
Objects.currentObjectsByName[objectName].constructor.properties() :
|
Modules.Objects.currentObjectsByName[objectName].constructor.properties() :
|
||||||
{}
|
{}
|
||||||
categoryItems: Object.keys(objectProperties)
|
categoryItems: Object.keys(objectProperties)
|
||||||
autocompleteGenerator: (item) => {
|
autocompleteGenerator: (item) => {
|
||||||
|
@ -433,7 +444,7 @@ Item {
|
||||||
itemSelected: parent.itemSelected
|
itemSelected: parent.itemSelected
|
||||||
categoryItems: Parsing.CONSTANTS_LIST
|
categoryItems: Parsing.CONSTANTS_LIST
|
||||||
autocompleteGenerator: (item) => {return {
|
autocompleteGenerator: (item) => {return {
|
||||||
'text': item, 'annotation': '',
|
'text': item, 'annotation': Parsing.CONSTANTS[item],
|
||||||
'autocomplete': item + " ", 'cursorFinalOffset': 0
|
'autocomplete': item + " ", 'cursorFinalOffset': 0
|
||||||
}}
|
}}
|
||||||
baseText: parent.visible ? parent.currentToken.value : ""
|
baseText: parent.visible ? parent.currentToken.value : ""
|
||||||
|
@ -448,7 +459,7 @@ Item {
|
||||||
itemSelected: parent.itemSelected
|
itemSelected: parent.itemSelected
|
||||||
categoryItems: Parsing.FUNCTIONS_LIST
|
categoryItems: Parsing.FUNCTIONS_LIST
|
||||||
autocompleteGenerator: (item) => {return {
|
autocompleteGenerator: (item) => {return {
|
||||||
'text': item, 'annotation': '',
|
'text': item, 'annotation': Parsing.FUNCTIONS_USAGE[item].join(', '),
|
||||||
'autocomplete': item+'()', 'cursorFinalOffset': -1
|
'autocomplete': item+'()', 'cursorFinalOffset': -1
|
||||||
}}
|
}}
|
||||||
baseText: parent.visible ? parent.currentToken.value : ""
|
baseText: parent.visible ? parent.currentToken.value : ""
|
||||||
|
@ -458,12 +469,12 @@ Item {
|
||||||
id: executableObjectsList
|
id: executableObjectsList
|
||||||
|
|
||||||
category: qsTr("Executable Objects")
|
category: qsTr("Executable Objects")
|
||||||
visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot
|
visbilityCondition: control.allowGraphObjects && parent.currentToken.identifier && !parent.previousToken.dot
|
||||||
itemStartIndex: functionsList.itemStartIndex + functionsList.model.length
|
itemStartIndex: functionsList.itemStartIndex + functionsList.model.length
|
||||||
itemSelected: parent.itemSelected
|
itemSelected: parent.itemSelected
|
||||||
categoryItems: Objects.getObjectsName("ExecutableObject").filter(obj => obj != self)
|
categoryItems: Modules.Objects.getObjectsName("ExecutableObject").filter(obj => obj != self)
|
||||||
autocompleteGenerator: (item) => {return {
|
autocompleteGenerator: (item) => {return {
|
||||||
'text': item, 'annotation': Objects.currentObjectsByName[item] == null ? '' : Objects.currentObjectsByName[item].constructor.displayType(),
|
'text': item, 'annotation': Modules.Objects.currentObjectsByName[item] == null ? '' : Objects.currentObjectsByName[item].constructor.displayType(),
|
||||||
'autocomplete': item+'()', 'cursorFinalOffset': -1
|
'autocomplete': item+'()', 'cursorFinalOffset': -1
|
||||||
}}
|
}}
|
||||||
baseText: parent.visible ? parent.currentToken.value : ""
|
baseText: parent.visible ? parent.currentToken.value : ""
|
||||||
|
@ -473,12 +484,12 @@ Item {
|
||||||
id: objectsList
|
id: objectsList
|
||||||
|
|
||||||
category: qsTr("Objects")
|
category: qsTr("Objects")
|
||||||
visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot
|
visbilityCondition: control.allowGraphObjects && parent.currentToken.identifier && !parent.previousToken.dot
|
||||||
itemStartIndex: executableObjectsList.itemStartIndex + executableObjectsList.model.length
|
itemStartIndex: executableObjectsList.itemStartIndex + executableObjectsList.model.length
|
||||||
itemSelected: parent.itemSelected
|
itemSelected: parent.itemSelected
|
||||||
categoryItems: Object.keys(Objects.currentObjectsByName).filter(obj => obj != self)
|
categoryItems: Object.keys(Modules.Objects.currentObjectsByName).filter(obj => obj != self)
|
||||||
autocompleteGenerator: (item) => {return {
|
autocompleteGenerator: (item) => {return {
|
||||||
'text': item, 'annotation': `${Objects.currentObjectsByName[item].constructor.displayType()}`,
|
'text': item, 'annotation': `${Modules.Objects.currentObjectsByName[item].constructor.displayType()}`,
|
||||||
'autocomplete': item+'.', 'cursorFinalOffset': 0
|
'autocomplete': item+'.', 'cursorFinalOffset': 0
|
||||||
}}
|
}}
|
||||||
baseText: parent.visible ? parent.currentToken.value : ""
|
baseText: parent.visible ? parent.currentToken.value : ""
|
||||||
|
@ -507,6 +518,8 @@ Item {
|
||||||
x: Math.round((parent.width - width) / 2)
|
x: Math.round((parent.width - width) / 2)
|
||||||
y: Math.round((parent.height - height) / 2)
|
y: Math.round((parent.height - height) / 2)
|
||||||
|
|
||||||
|
category: "expression"
|
||||||
|
|
||||||
onSelected: function(c) {
|
onSelected: function(c) {
|
||||||
editor.insert(editor.cursorPosition, c)
|
editor.insert(editor.cursorPosition, c)
|
||||||
insertPopup.close()
|
insertPopup.close()
|
||||||
|
@ -536,8 +549,8 @@ Item {
|
||||||
throw new Error(qsTranslate('error', 'Object cannot be dependent on itself.'))
|
throw new Error(qsTranslate('error', 'Object cannot be dependent on itself.'))
|
||||||
// Recursive dependencies
|
// Recursive dependencies
|
||||||
let dependentOnSelfObjects = expr.requiredObjects().filter(
|
let dependentOnSelfObjects = expr.requiredObjects().filter(
|
||||||
(obj) => Objects.currentObjectsByName[obj].getDependenciesList()
|
(obj) => Modules.Objects.currentObjectsByName[obj].getDependenciesList()
|
||||||
.includes(Objects.currentObjectsByName[control.self])
|
.includes(Modules.Objects.currentObjectsByName[control.self])
|
||||||
)
|
)
|
||||||
if(dependentOnSelfObjects.length == 1)
|
if(dependentOnSelfObjects.length == 1)
|
||||||
throw new Error(qsTranslate('error', 'Circular dependency detected. Object %1 depends on %2.').arg(dependentOnSelfObjects[0].toString()).arg(control.self))
|
throw new Error(qsTranslate('error', 'Circular dependency detected. Object %1 depends on %2.').arg(dependentOnSelfObjects[0].toString()).arg(control.self))
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
*/
|
*/
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Window
|
import QtQuick.Window
|
||||||
import Qt5Compat.GraphicalEffects
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmltype Icon
|
\qmltype Icon
|
||||||
|
@ -41,20 +41,16 @@ Item {
|
||||||
\qmlproperty string Icon::source
|
\qmlproperty string Icon::source
|
||||||
Path of the icon image source.
|
Path of the icon image source.
|
||||||
*/
|
*/
|
||||||
property alias sourceSize: img.sourceSize.width
|
property alias sourceSize: img.sourceS
|
||||||
|
|
||||||
Image {
|
ColorImage {
|
||||||
id: img
|
id: img
|
||||||
height: parent.height
|
height: parent.height
|
||||||
width: parent.width
|
width: parent.width
|
||||||
visible: false
|
// visible: false
|
||||||
sourceSize.width: width*Screen.devicePixelRatio
|
property int sourceS: width*Screen.devicePixelRatio
|
||||||
sourceSize.height: width*Screen.devicePixelRatio
|
sourceSize.width: sourceS
|
||||||
}
|
sourceSize.height: sourceS
|
||||||
|
|
||||||
ColorOverlay {
|
|
||||||
anchors.fill: img
|
|
||||||
source: img
|
|
||||||
color: parent.color
|
color: parent.color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -49,6 +49,12 @@ Item {
|
||||||
If true, the input is being parsed an double before being emitting the \a changed signal.
|
If true, the input is being parsed an double before being emitting the \a changed signal.
|
||||||
*/
|
*/
|
||||||
property bool isDouble: false
|
property bool isDouble: false
|
||||||
|
/*!
|
||||||
|
\qmlproperty bool TextSetting::category
|
||||||
|
Type of special character to insert from the popup.
|
||||||
|
\sa InsertCharacter::category
|
||||||
|
*/
|
||||||
|
property alias category: insertPopup.category
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty double TextSetting::min
|
\qmlproperty double TextSetting::min
|
||||||
Minimum value for numbers that can be entered into the input.
|
Minimum value for numbers that can be entered into the input.
|
||||||
|
@ -94,14 +100,14 @@ Item {
|
||||||
id: labelItem
|
id: labelItem
|
||||||
anchors.left: iconLabel.right
|
anchors.left: iconLabel.right
|
||||||
anchors.leftMargin: icon == "" ? 0 : 5
|
anchors.leftMargin: icon == "" ? 0 : 5
|
||||||
height: parent.height
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
|
height: parent.height
|
||||||
|
width: Math.max(85, implicitWidth)
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
//color: sysPalette.windowText
|
//color: sysPalette.windowText
|
||||||
text: visible ? qsTranslate("control", "%1: ").arg(control.label) : ""
|
text: visible ? qsTranslate("control", "%1: ").arg(control.label) : ""
|
||||||
visible: control.label != ""
|
visible: control.label != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: input
|
id: input
|
||||||
|
@ -122,8 +128,10 @@ Item {
|
||||||
onEditingFinished: function() {
|
onEditingFinished: function() {
|
||||||
if(insertButton.focus || insertPopup.focus) return
|
if(insertButton.focus || insertPopup.focus) return
|
||||||
var value = text
|
var value = text
|
||||||
if(control.isInt) value = Math.max(control.min,parseInt(value).toString()=="NaN"?control.min:parseInt(value))
|
if(control.isInt)
|
||||||
if(control.isDouble) value = Math.max(control.min,parseFloat(value).toString()=="NaN"?control.min:parseFloat(value))
|
value = isNaN(parseInt(value)) ? control.min : Math.max(control.min,parseInt(value))
|
||||||
|
if(control.isDouble)
|
||||||
|
value = isNaN(parseFloat(value)) ? control.min : Math.max(control.min,parseFloat(value))
|
||||||
if(value != "" && value.toString() != defValue) {
|
if(value != "" && value.toString() != defValue) {
|
||||||
control.changed(value)
|
control.changed(value)
|
||||||
defValue = value.toString()
|
defValue = value.toString()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,11 +16,11 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick
|
|
||||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||||
import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup
|
import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup
|
||||||
import "js/utils.js" as Utils
|
import "js/utils.mjs" as Utils
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmltype Settings
|
\qmltype Settings
|
||||||
|
@ -44,89 +44,93 @@ ScrollView {
|
||||||
Zoom on the x axis of the diagram, provided from settings.
|
Zoom on the x axis of the diagram, provided from settings.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property double xzoom: 100
|
property double xzoom: Helper.getSettingInt('default_graph.xzoom')
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty double Settings::yzoom
|
\qmlproperty double Settings::yzoom
|
||||||
Zoom on the y axis of the diagram, provided from settings.
|
Zoom on the y axis of the diagram, provided from settings.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property double yzoom: 10
|
property double yzoom: Helper.getSettingInt('default_graph.yzoom')
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty double Settings::xmin
|
\qmlproperty double Settings::xmin
|
||||||
Minimum x of the diagram, provided from settings.
|
Minimum x of the diagram, provided from settings.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property double xmin: 5/10
|
property double xmin: Helper.getSettingInt('default_graph.xmin')
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty double Settings::ymax
|
\qmlproperty double Settings::ymax
|
||||||
Maximum y of the diagram, provided from settings.
|
Maximum y of the diagram, provided from settings.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property double ymax: 25
|
property double ymax: Helper.getSettingInt('default_graph.ymax')
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty string Settings::xaxisstep
|
\qmlproperty string Settings::xaxisstep
|
||||||
Step of the x axis graduation, provided from settings.
|
Step of the x axis graduation, provided from settings.
|
||||||
\note: Only available in non-logarithmic mode.
|
\note: Only available in non-logarithmic mode.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property string xaxisstep: "4"
|
property string xaxisstep: Helper.getSetting('default_graph.xaxisstep')
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty string Settings::yaxisstep
|
\qmlproperty string Settings::yaxisstep
|
||||||
Step of the y axis graduation, provided from settings.
|
Step of the y axis graduation, provided from settings.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property string yaxisstep: "4"
|
property string yaxisstep: Helper.getSetting('default_graph.yaxisstep')
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty string Settings::xlabel
|
\qmlproperty string Settings::xlabel
|
||||||
Label used on the x axis, provided from settings.
|
Label used on the x axis, provided from settings.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property string xlabel: ""
|
property string xlabel: Helper.getSetting('default_graph.xlabel')
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty string Settings::ylabel
|
\qmlproperty string Settings::ylabel
|
||||||
Label used on the y axis, provided from settings.
|
Label used on the y axis, provided from settings.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property string ylabel: ""
|
property string ylabel: Helper.getSetting('default_graph.ylabel')
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty double Settings::linewidth
|
\qmlproperty double Settings::linewidth
|
||||||
Width of lines that will be drawn into the canvas, provided from settings.
|
Width of lines that will be drawn into the canvas, provided from settings.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property double linewidth: 1
|
property double linewidth: Helper.getSettingInt('default_graph.linewidth')
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty double Settings::textsize
|
\qmlproperty double Settings::textsize
|
||||||
Font size of the text that will be drawn into the canvas, provided from settings.
|
Font size of the text that will be drawn into the canvas, provided from settings.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property double textsize: 18
|
property double textsize: Helper.getSettingInt('default_graph.textsize')
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty bool Settings::logscalex
|
\qmlproperty bool Settings::logscalex
|
||||||
true if the canvas should be in logarithmic mode, false otherwise.
|
true if the canvas should be in logarithmic mode, false otherwise.
|
||||||
Provided from settings.
|
Provided from settings.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property bool logscalex: true
|
property bool logscalex: Helper.getSettingBool('default_graph.logscalex')
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty bool Settings::showxgrad
|
\qmlproperty bool Settings::showxgrad
|
||||||
true if the x graduation should be shown, false otherwise.
|
true if the x graduation should be shown, false otherwise.
|
||||||
Provided from settings.
|
Provided from settings.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property bool showxgrad: true
|
property bool showxgrad: Helper.getSettingBool('default_graph.showxgrad')
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty bool Settings::showygrad
|
\qmlproperty bool Settings::showygrad
|
||||||
true if the y graduation should be shown, false otherwise.
|
true if the y graduation should be shown, false otherwise.
|
||||||
Provided from settings.
|
Provided from settings.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property bool showygrad: true
|
property bool showygrad: Helper.getSettingBool('default_graph.showygrad')
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty bool Settings::saveFilename
|
\qmlproperty bool Settings::saveFilename
|
||||||
Path of the currently opened file. Empty if no file is opened.
|
Path of the currently opened file. Empty if no file is opened.
|
||||||
*/
|
*/
|
||||||
property string saveFilename: ""
|
property string saveFilename: ""
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
Modules.IO.initialize(root, settings, alert)
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
spacing: 10
|
spacing: 10
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
@ -138,9 +142,9 @@ ScrollView {
|
||||||
var filePath = fdiag.currentFile.toString().substr(7)
|
var filePath = fdiag.currentFile.toString().substr(7)
|
||||||
settings.saveFilename = filePath
|
settings.saveFilename = filePath
|
||||||
if(exportMode) {
|
if(exportMode) {
|
||||||
root.saveDiagram(filePath)
|
Modules.IO.saveDiagram(filePath)
|
||||||
} else {
|
} else {
|
||||||
root.loadDiagram(filePath)
|
Modules.IO.loadDiagram(filePath)
|
||||||
if(xAxisLabel.find(settings.xlabel) == -1) xAxisLabel.model.append({text: settings.xlabel})
|
if(xAxisLabel.find(settings.xlabel) == -1) xAxisLabel.model.append({text: settings.xlabel})
|
||||||
xAxisLabel.editText = settings.xlabel
|
xAxisLabel.editText = settings.xlabel
|
||||||
if(yAxisLabel.find(settings.ylabel) == -1) yAxisLabel.model.append({text: settings.ylabel})
|
if(yAxisLabel.find(settings.ylabel) == -1) yAxisLabel.model.append({text: settings.ylabel})
|
||||||
|
@ -155,7 +159,7 @@ ScrollView {
|
||||||
height: 30
|
height: 30
|
||||||
isDouble: true
|
isDouble: true
|
||||||
label: qsTr("X Zoom")
|
label: qsTr("X Zoom")
|
||||||
min: 1
|
min: 0.1
|
||||||
icon: "settings/xzoom.svg"
|
icon: "settings/xzoom.svg"
|
||||||
width: settings.settingWidth
|
width: settings.settingWidth
|
||||||
value: settings.xzoom.toFixed(2)
|
value: settings.xzoom.toFixed(2)
|
||||||
|
@ -169,6 +173,7 @@ ScrollView {
|
||||||
id: zoomY
|
id: zoomY
|
||||||
height: 30
|
height: 30
|
||||||
isDouble: true
|
isDouble: true
|
||||||
|
min: 0.1
|
||||||
label: qsTr("Y Zoom")
|
label: qsTr("Y Zoom")
|
||||||
icon: "settings/yzoom.svg"
|
icon: "settings/yzoom.svg"
|
||||||
width: settings.settingWidth
|
width: settings.settingWidth
|
||||||
|
@ -222,10 +227,10 @@ ScrollView {
|
||||||
label: qsTr("Max X")
|
label: qsTr("Max X")
|
||||||
icon: "settings/xmax.svg"
|
icon: "settings/xmax.svg"
|
||||||
width: settings.settingWidth
|
width: settings.settingWidth
|
||||||
value: canvas.px2x(canvas.canvasSize.width).toFixed(2)
|
defValue: canvas.px2x(canvas.width).toFixed(2)
|
||||||
onChanged: function(xvaluemax) {
|
onChanged: function(xvaluemax) {
|
||||||
if(xvaluemax > settings.xmin) {
|
if(xvaluemax > settings.xmin) {
|
||||||
settings.xzoom = settings.xzoom * canvas.canvasSize.width/(canvas.x2px(xvaluemax)) // Adjusting zoom to fit. = (end)/(px of current point)
|
settings.xzoom = settings.xzoom * canvas.width/(canvas.x2px(xvaluemax)) // Adjusting zoom to fit. = (end)/(px of current point)
|
||||||
settings.changed()
|
settings.changed()
|
||||||
} else {
|
} else {
|
||||||
alert.show("Maximum x value must be superior to minimum.")
|
alert.show("Maximum x value must be superior to minimum.")
|
||||||
|
@ -241,10 +246,10 @@ ScrollView {
|
||||||
label: qsTr("Min Y")
|
label: qsTr("Min Y")
|
||||||
icon: "settings/ymin.svg"
|
icon: "settings/ymin.svg"
|
||||||
width: settings.settingWidth
|
width: settings.settingWidth
|
||||||
defValue: canvas.px2y(canvas.canvasSize.height).toFixed(2)
|
defValue: canvas.px2y(canvas.height).toFixed(2)
|
||||||
onChanged: function(yvaluemin) {
|
onChanged: function(yvaluemin) {
|
||||||
if(yvaluemin < settings.ymax) {
|
if(yvaluemin < settings.ymax) {
|
||||||
settings.yzoom = settings.yzoom * canvas.canvasSize.height/(canvas.y2px(yvaluemin)) // Adjusting zoom to fit. = (end)/(px of current point)
|
settings.yzoom = settings.yzoom * canvas.height/(canvas.y2px(yvaluemin)) // Adjusting zoom to fit. = (end)/(px of current point)
|
||||||
settings.changed()
|
settings.changed()
|
||||||
} else {
|
} else {
|
||||||
alert.show("Minimum y value must be inferior to maximum.")
|
alert.show("Minimum y value must be inferior to maximum.")
|
||||||
|
@ -255,6 +260,7 @@ ScrollView {
|
||||||
Setting.TextSetting {
|
Setting.TextSetting {
|
||||||
id: xAxisStep
|
id: xAxisStep
|
||||||
height: 30
|
height: 30
|
||||||
|
category: "expression"
|
||||||
label: qsTr("X Axis Step")
|
label: qsTr("X Axis Step")
|
||||||
icon: "settings/xaxisstep.svg"
|
icon: "settings/xaxisstep.svg"
|
||||||
width: settings.settingWidth
|
width: settings.settingWidth
|
||||||
|
@ -269,6 +275,7 @@ ScrollView {
|
||||||
Setting.TextSetting {
|
Setting.TextSetting {
|
||||||
id: yAxisStep
|
id: yAxisStep
|
||||||
height: 30
|
height: 30
|
||||||
|
category: "expression"
|
||||||
label: qsTr("Y Axis Step")
|
label: qsTr("Y Axis Step")
|
||||||
icon: "settings/yaxisstep.svg"
|
icon: "settings/yaxisstep.svg"
|
||||||
width: settings.settingWidth
|
width: settings.settingWidth
|
||||||
|
@ -440,7 +447,7 @@ ScrollView {
|
||||||
if(settings.saveFilename == "") {
|
if(settings.saveFilename == "") {
|
||||||
saveAs()
|
saveAs()
|
||||||
} else {
|
} else {
|
||||||
root.saveDiagram(settings.saveFilename)
|
Modules.IO.saveDiagram(settings.saveFilename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
/**
|
||||||
|
* 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 QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||||
|
import "js/mathlib.mjs" as MathLib
|
||||||
|
import "js/historylib.mjs" as HistoryLib
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmltype ViewPositionChangeOverlay
|
||||||
|
\inqmlmodule eu.ad5001.LogarithmPlotter
|
||||||
|
\brief Overlay used allow the user to drag the canvas' position and change the zoom level.
|
||||||
|
|
||||||
|
Provides an overlay over the canvas that detects mouse movements and changes the canvas view position
|
||||||
|
accordingly by providing new signals.
|
||||||
|
|
||||||
|
\sa LogarithmPlotter, LogGraphCanvas, Settings
|
||||||
|
*/
|
||||||
|
Item {
|
||||||
|
id: viewChangeRoot
|
||||||
|
visible: true
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlsignal ViewPositionChangeOverlay::positionChanged(int deltaX, int deltaY)
|
||||||
|
|
||||||
|
Emmited when the user dragged the canvas and the view should be refreshed.
|
||||||
|
The corresponding handler is \c onPositionChanged.
|
||||||
|
*/
|
||||||
|
signal positionChanged(int deltaX, int deltaY)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlsignal ViewPositionChangeOverlay::beginPositionChange()
|
||||||
|
|
||||||
|
Emmited when the user starts dragging the canvas.
|
||||||
|
The corresponding handler is \c onBeginPositionChange.
|
||||||
|
*/
|
||||||
|
signal beginPositionChange()
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlsignal ViewPositionChangeOverlay::endPositionChange(int deltaX, int deltaY)
|
||||||
|
|
||||||
|
Emmited when the user stops dragging the canvas.
|
||||||
|
The corresponding handler is \c onEndPositionChange.
|
||||||
|
*/
|
||||||
|
signal endPositionChange(int deltaX, int deltaY)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlproperty var ViewPositionChangeOverlay::canvas
|
||||||
|
LogGraphCanvas instance.
|
||||||
|
*/
|
||||||
|
property var canvas
|
||||||
|
/*!
|
||||||
|
\qmlproperty var ViewPositionChangeOverlay::settingsInstance
|
||||||
|
Settings instance.
|
||||||
|
*/
|
||||||
|
property var settingsInstance
|
||||||
|
/*!
|
||||||
|
\qmlproperty int ViewPositionChangeOverlay::prevX
|
||||||
|
The x coordinate (on the mousearea) at the last change of the canvas position.
|
||||||
|
*/
|
||||||
|
property int prevX
|
||||||
|
/*!
|
||||||
|
\qmlproperty int ViewPositionChangeOverlay::prevY
|
||||||
|
The y coordinate (on the mousearea) at the last change of the canvas position.
|
||||||
|
*/
|
||||||
|
property int prevY
|
||||||
|
/*!
|
||||||
|
\qmlproperty double ViewPositionChangeOverlay::baseZoomMultiplier
|
||||||
|
How much should the zoom be mutliplied/scrolled by for one scroll step (120° on the mouse wheel).
|
||||||
|
*/
|
||||||
|
property double baseZoomMultiplier: 0.1
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: dragArea
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor
|
||||||
|
property int positionChangeTimer: 0
|
||||||
|
|
||||||
|
function updatePosition(deltaX, deltaY) {
|
||||||
|
settingsInstance.xmin = (Modules.Canvas.px2x(Modules.Canvas.x2px(settingsInstance.xmin)-deltaX))
|
||||||
|
settingsInstance.ymax += deltaY/canvas.yzoom
|
||||||
|
settingsInstance.ymax = settingsInstance.ymax.toFixed(4)
|
||||||
|
settingsInstance.changed()
|
||||||
|
parent.positionChanged(deltaX, deltaY)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onPressed: function(mouse) {
|
||||||
|
prevX = mouse.x
|
||||||
|
prevY = mouse.y
|
||||||
|
parent.beginPositionChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
onPositionChanged: function(mouse) {
|
||||||
|
positionChangeTimer++
|
||||||
|
if(positionChangeTimer == 3) {
|
||||||
|
let deltaX = mouse.x - prevX
|
||||||
|
let deltaY = mouse.y - prevY
|
||||||
|
updatePosition(deltaX, deltaY)
|
||||||
|
prevX = mouse.x
|
||||||
|
prevY = mouse.y
|
||||||
|
positionChangeTimer = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onReleased: function(mouse) {
|
||||||
|
let deltaX = mouse.x - prevX
|
||||||
|
let deltaY = mouse.y - prevY
|
||||||
|
updatePosition(deltaX, deltaY)
|
||||||
|
parent.endPositionChange(deltaX, deltaY)
|
||||||
|
}
|
||||||
|
|
||||||
|
onWheel: function(wheel) {
|
||||||
|
// Scrolling
|
||||||
|
let scrollSteps = Math.round(wheel.angleDelta.y / 120)
|
||||||
|
let zoomMultiplier = Math.pow(1+baseZoomMultiplier, Math.abs(scrollSteps))
|
||||||
|
// Avoid floating-point rounding errors by removing the zoom *after*
|
||||||
|
let xZoomDelta = (settingsInstance.xzoom*zoomMultiplier - settingsInstance.xzoom)
|
||||||
|
let yZoomDelta = (settingsInstance.yzoom*zoomMultiplier - settingsInstance.yzoom)
|
||||||
|
if(scrollSteps < 0) { // Negative scroll
|
||||||
|
xZoomDelta *= -1
|
||||||
|
yZoomDelta *= -1
|
||||||
|
}
|
||||||
|
let newXZoom = (settingsInstance.xzoom+xZoomDelta).toFixed(0)
|
||||||
|
let newYZoom = (settingsInstance.yzoom+yZoomDelta).toFixed(0)
|
||||||
|
if(newXZoom == settingsInstance.xzoom) // No change, allow more precision.
|
||||||
|
newXZoom = (settingsInstance.xzoom+xZoomDelta).toFixed(4)
|
||||||
|
if(newYZoom == settingsInstance.yzoom) // No change, allow more precision.
|
||||||
|
newYZoom = (settingsInstance.yzoom+yZoomDelta).toFixed(4)
|
||||||
|
settingsInstance.xzoom = newXZoom
|
||||||
|
settingsInstance.yzoom = newYZoom
|
||||||
|
settingsInstance.changed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg6"
|
||||||
|
sodipodi:docname="remove.svg"
|
||||||
|
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs10" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview8"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="34.458333"
|
||||||
|
inkscape:cx="12"
|
||||||
|
inkscape:cy="10.505441"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1007"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg6">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid822" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<path
|
||||||
|
id="rect2"
|
||||||
|
style="fill-rule:evenodd;stroke-width:3.16228"
|
||||||
|
transform="rotate(135)"
|
||||||
|
d="M -1.4142136,-26.870058 H 1.4142136 V -7.0710678 H -1.4142136 Z"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-rule:evenodd;stroke-width:3.16228"
|
||||||
|
d="M 20,6 6,20 4,18 18,4 Z"
|
||||||
|
id="path4"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd"><path d="M14 0v10l2-1.518 2 1.518v-10h4v24h-17c-1.657 0-3-1.343-3-3v-18c0-1.657 1.343-3 3-3h9zm6 20h-14.505c-1.375 0-1.375 2 0 2h14.505v-2z"/></svg>
|
After Width: | Height: | Size: 251 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd"><path d="M12 0l-2.138 2.63-3.068-1.441-.787 3.297-3.389.032.722 3.312-3.039 1.5 2.088 2.671-2.088 2.67 3.039 1.499-.722 3.312 3.389.033.787 3.296 3.068-1.441 2.138 2.63 2.139-2.63 3.068 1.441.786-3.296 3.39-.033-.722-3.312 3.038-1.499-2.087-2.67 2.087-2.671-3.038-1.5.722-3.312-3.39-.032-.786-3.297-3.068 1.441-2.139-2.63zm0 15.5c.69 0 1.25.56 1.25 1.25s-.56 1.25-1.25 1.25-1.25-.56-1.25-1.25.56-1.25 1.25-1.25zm1-1.038v-7.462h-2v7.462h2z"/></svg>
|
After Width: | Height: | Size: 550 B |
Before Width: | Height: | Size: 370 B After Width: | Height: | Size: 370 B |
|
@ -1,48 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
version="1.1"
|
|
||||||
id="svg6"
|
|
||||||
sodipodi:docname="remove.svg"
|
|
||||||
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs10" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview8"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:zoom="34.458333"
|
|
||||||
inkscape:cx="12"
|
|
||||||
inkscape:cy="10.505441"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1007"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg6">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid822" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<path
|
|
||||||
id="rect2"
|
|
||||||
style="fill-rule:evenodd;stroke-width:3.16228"
|
|
||||||
transform="rotate(135)"
|
|
||||||
d="M -1.4142136,-26.870058 H 1.4142136 V -7.0710678 H -1.4142136 Z"
|
|
||||||
sodipodi:nodetypes="ccccc" />
|
|
||||||
<path
|
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:3.16228"
|
|
||||||
d="M 20,6 6,20 4,18 18,4 Z"
|
|
||||||
id="path4"
|
|
||||||
sodipodi:nodetypes="ccccc" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 19 B |
|
@ -0,0 +1 @@
|
||||||
|
../common/close.svg
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 19 B |
|
@ -1,44 +1,17 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="24.0px"
|
width="24.0px"
|
||||||
height="24.0px"
|
height="24.0px"
|
||||||
viewBox="0 0 24.0 24.0"
|
viewBox="0 0 24.0 24.0"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="SVGRoot"
|
id="SVGRoot"
|
||||||
sodipodi:docname="Function.svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
<defs
|
<defs
|
||||||
id="defs833" />
|
id="defs833" />
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="31.678384"
|
|
||||||
inkscape:cx="15.268708"
|
|
||||||
inkscape:cy="12.238724"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1011"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid1403" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata10">
|
id="metadata10">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
|
@ -47,8 +20,7 @@
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title />
|
<dc:date>2021-2023</dc:date>
|
||||||
<dc:date>2021</dc:date>
|
|
||||||
<dc:creator>
|
<dc:creator>
|
||||||
<cc:Agent>
|
<cc:Agent>
|
||||||
<dc:title>Ad5001</dc:title>
|
<dc:title>Ad5001</dc:title>
|
||||||
|
@ -56,7 +28,7 @@
|
||||||
</dc:creator>
|
</dc:creator>
|
||||||
<dc:rights>
|
<dc:rights>
|
||||||
<cc:Agent>
|
<cc:Agent>
|
||||||
<dc:title>(C) Ad5001 2021 - Licensed under CC4.0-BY-NC-SA</dc:title>
|
<dc:title>(C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA</dc:title>
|
||||||
</cc:Agent>
|
</cc:Agent>
|
||||||
</dc:rights>
|
</dc:rights>
|
||||||
<cc:license
|
<cc:license
|
||||||
|
@ -78,33 +50,46 @@
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<g
|
<g
|
||||||
inkscape:label="Calque 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
id="layer1">
|
||||||
<path
|
<text
|
||||||
id="rect1415"
|
xml:space="preserve"
|
||||||
style="fill:#000000;fill-rule:evenodd"
|
style="font-style:normal;font-weight:normal;font-size:17.3333px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none"
|
||||||
d="M 2,9 C 2,5 6,5 6,5 H 7 V 7 H 6 C 6,7 4,7 4,9 v 2 h 2 v 2 H 4 v 5 H 2 V 13 H 0 v -2 h 2 z"
|
x="0.012050295"
|
||||||
sodipodi:nodetypes="ccccccccccccccccc" />
|
y="17.985596"
|
||||||
<path
|
id="text1"><tspan
|
||||||
id="rect839"
|
id="tspan1"
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
x="0.012050295"
|
||||||
d="m 12,3 v 2 c 0,0 -2,0 -2,7 0,7 2,7 2,7 v 2 C 8,21 8,12 8,12 8,12 8,3 12,3 Z"
|
y="17.985596"
|
||||||
sodipodi:nodetypes="ccccccc" />
|
style="font-size:17.3333px">f</tspan></text>
|
||||||
<path
|
<text
|
||||||
id="rect857"
|
xml:space="preserve"
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
style="font-style:normal;font-weight:normal;font-size:17.3333px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none"
|
||||||
d="m 12,10 h 2 l 6,8 h -2 z"
|
x="10.913334"
|
||||||
sodipodi:nodetypes="ccccc" />
|
y="18.134649"
|
||||||
<path
|
id="text1-3"><tspan
|
||||||
id="rect857-7"
|
id="tspan1-6"
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
x="10.913334"
|
||||||
d="m 12,18 h 2 l 6,-8 h -2 z"
|
y="18.134649"
|
||||||
sodipodi:nodetypes="ccccc" />
|
style="font-size:17.3333px">x</tspan></text>
|
||||||
<path
|
<text
|
||||||
id="rect839-3"
|
xml:space="preserve"
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
style="font-style:normal;font-weight:normal;font-size:17.3333px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none"
|
||||||
d="m 20,3 v 2 c 0,0 2,0 2,7 0,7 -2,7 -2,7 v 2 c 4,0 4,-9 4,-9 0,0 0,-9 -4,-9 z"
|
x="6.3066678"
|
||||||
sodipodi:nodetypes="ccccccc" />
|
y="17.646639"
|
||||||
|
id="text2"><tspan
|
||||||
|
id="tspan2"
|
||||||
|
x="6.3066678"
|
||||||
|
y="17.646639"
|
||||||
|
style="font-size:17.3333px">(</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:17.3333px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none"
|
||||||
|
x="18.306667"
|
||||||
|
y="17.646639"
|
||||||
|
id="text2-7"><tspan
|
||||||
|
id="tspan2-5"
|
||||||
|
x="18.306667"
|
||||||
|
y="17.646639"
|
||||||
|
style="font-size:17.3333px">)</tspan></text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.2 KiB |
|
@ -1,47 +1,17 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="24.0px"
|
width="24.0px"
|
||||||
height="24.0px"
|
height="24.0px"
|
||||||
viewBox="0 0 24.0 24.0"
|
viewBox="0 0 24.0 24.0"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="SVGRoot"
|
id="SVGRoot"
|
||||||
sodipodi:docname="Gain Bode.svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
<defs
|
<defs
|
||||||
id="defs1469" />
|
id="defs1469" />
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="15.839192"
|
|
||||||
inkscape:cx="15.763196"
|
|
||||||
inkscape:cy="7.8365971"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1011"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid2039" />
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid2058" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata1472">
|
id="metadata1472">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
|
@ -50,13 +20,40 @@
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title />
|
<dc:date>2021-2023</dc:date>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Ad5001</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<dc:rights>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>(C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:rights>
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-nc-sa/4.0/" />
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-nc-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:prohibits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<g
|
<g
|
||||||
inkscape:label="Calque 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
id="layer1">
|
||||||
<rect
|
<rect
|
||||||
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:14.257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
|
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:14.257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
|
||||||
|
@ -67,15 +64,14 @@
|
||||||
y="17" />
|
y="17" />
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
style="font-style:normal;font-weight:normal;font-size:17.3333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
style="font-style:normal;font-weight:normal;font-size:17px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
||||||
x="-0.12666447"
|
x="-0.105"
|
||||||
y="12.134649"
|
y="11.959"
|
||||||
id="text839"><tspan
|
id="text839"><tspan
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan837"
|
id="tspan837"
|
||||||
x="-0.12666447"
|
x="-0.105"
|
||||||
y="12.134649"
|
y="11.959"
|
||||||
style="font-size:17.3333px">ω</tspan></text>
|
style="font-size:17px">ω</tspan></text>
|
||||||
<circle
|
<circle
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
||||||
id="path837"
|
id="path837"
|
||||||
|
@ -86,7 +82,6 @@
|
||||||
id="rect837"
|
id="rect837"
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2.10035"
|
style="fill:#000000;fill-rule:evenodd;stroke-width:2.10035"
|
||||||
transform="rotate(30)"
|
transform="rotate(30)"
|
||||||
d="m 20.686533,-6.169873 2.232051,-0.1339746 -0.06218,13.8923049 -2.23205,0.1339746 z"
|
d="m 20.686533,-6.169873 2.232051,-0.1339746 -0.06218,13.8923049 -2.23205,0.1339746 z" />
|
||||||
sodipodi:nodetypes="ccccc" />
|
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
@ -1,44 +1,17 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="24.0px"
|
width="24.0px"
|
||||||
height="24.0px"
|
height="24.0px"
|
||||||
viewBox="0 0 24.0 24.0"
|
viewBox="0 0 24.0 24.0"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="SVGRoot"
|
id="SVGRoot"
|
||||||
sodipodi:docname="Phase Bode.svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
<defs
|
<defs
|
||||||
id="defs10" />
|
id="defs10" />
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="22.4"
|
|
||||||
inkscape:cx="15.347905"
|
|
||||||
inkscape:cy="8.3727678"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1011"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid19" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata13">
|
id="metadata13">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
|
@ -47,13 +20,40 @@
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title />
|
<dc:date>2021-2023</dc:date>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Ad5001</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<dc:rights>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>(C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:rights>
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-nc-sa/4.0/" />
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-nc-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:prohibits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<g
|
<g
|
||||||
inkscape:label="Calque 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
id="layer1">
|
||||||
<rect
|
<rect
|
||||||
style="fill:#000000;stroke-width:1.1547"
|
style="fill:#000000;stroke-width:1.1547"
|
||||||
|
@ -65,8 +65,7 @@
|
||||||
<path
|
<path
|
||||||
id="rect26-3"
|
id="rect26-3"
|
||||||
style="fill:#000000;stroke-width:1.22474"
|
style="fill:#000000;stroke-width:1.22474"
|
||||||
d="m 15,2 v 14 h 2 V 4 h 7 V 2 Z"
|
d="m 15,2 v 14 h 2 V 4 h 7 V 2 Z" />
|
||||||
sodipodi:nodetypes="ccccccc" />
|
|
||||||
<circle
|
<circle
|
||||||
style="fill:#000000;stroke-width:2.09999"
|
style="fill:#000000;stroke-width:2.09999"
|
||||||
id="path45"
|
id="path45"
|
||||||
|
@ -75,14 +74,13 @@
|
||||||
r="4" />
|
r="4" />
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
style="font-size:18px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;text-anchor:middle;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
|
style="font-size:17px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;text-anchor:middle;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
x="6.4359932"
|
x="6.1339936"
|
||||||
y="11.702"
|
y="11.163"
|
||||||
id="text49"><tspan
|
id="text49"><tspan
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan47"
|
id="tspan47"
|
||||||
x="6.4359932"
|
x="6.1339936"
|
||||||
y="11.702"
|
y="11.163"
|
||||||
style="font-size:18px;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none">φ</tspan></text>
|
style="font-size:17px;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none">φ</tspan></text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.7 KiB |
|
@ -1 +1,67 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path style="opacity:1;vector-effect:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0" d="M9.98 18.12a3.5 3.5 0 0 1-3.064 3.855 3.5 3.5 0 0 1-3.887-3.022 3.5 3.5 0 0 1 2.982-3.919 3.5 3.5 0 0 1 3.95 2.94"/><text xml:space="preserve" style="font-style:normal;font-weight:400;font-size:17.3373px;line-height:1.25;font-family:sans-serif;letter-spacing:0;word-spacing:0;fill-opacity:1;stroke:none;stroke-width:1.00023" x="12.067" y="13.923" transform="scale(.99447 1.00556)"><tspan x="12.067" y="13.923" style="font-size:17.3373px;stroke-width:1.00023">A</tspan></text></svg>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<path
|
||||||
|
style="opacity:1;vector-effect:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
|
||||||
|
d="M9.98 18.12a3.5 3.5 0 0 1-3.064 3.855 3.5 3.5 0 0 1-3.887-3.022 3.5 3.5 0 0 1 2.982-3.919 3.5 3.5 0 0 1 3.95 2.94"
|
||||||
|
id="path1" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:400;font-size:17px;line-height:1.25;font-family:sans-serif;letter-spacing:0;word-spacing:0;fill-opacity:1;stroke:none;stroke-width:1.00023"
|
||||||
|
x="11.964725"
|
||||||
|
y="13.701941"
|
||||||
|
transform="scale(0.99447036,1.0055604)"
|
||||||
|
id="text1"><tspan
|
||||||
|
x="11.964725"
|
||||||
|
y="13.701941"
|
||||||
|
style="font-size:17px;stroke-width:1.00023"
|
||||||
|
id="tspan1">A</tspan></text>
|
||||||
|
<metadata
|
||||||
|
id="metadata1">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:date>2021-2023</dc:date>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Ad5001</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<dc:rights>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>(C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:rights>
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-nc-sa/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-nc-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:prohibits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 772 B After Width: | Height: | Size: 2.5 KiB |
|
@ -1,45 +1,17 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="24.0px"
|
width="24.0px"
|
||||||
height="24.0px"
|
height="24.0px"
|
||||||
viewBox="0 0 24.0 24.0"
|
viewBox="0 0 24.0 24.0"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="SVGRoot"
|
id="SVGRoot"
|
||||||
sodipodi:docname="Sequence.svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
<defs
|
<defs
|
||||||
id="defs10" />
|
id="defs10" />
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="22.4"
|
|
||||||
inkscape:cx="-1.4929284"
|
|
||||||
inkscape:cy="9.7261905"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
showgrid="true"
|
|
||||||
showguides="true"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1011"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid19" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata13">
|
id="metadata13">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
|
@ -48,56 +20,62 @@
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title />
|
<dc:date>2021-2023</dc:date>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Ad5001</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<dc:rights>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>(C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:rights>
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<g
|
<g
|
||||||
inkscape:label="Calque 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
id="layer1">
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
style="font-size:17.3333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;text-anchor:middle"
|
style="font-size:17.3333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;text-anchor:middle;-inkscape-font-specification:sans-serif;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||||
x="7.483983"
|
x="7.483983"
|
||||||
y="16.134649"
|
y="16.134649"
|
||||||
id="text908"><tspan
|
id="text908"><tspan
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan906"
|
id="tspan906"
|
||||||
x="7.483983"
|
x="7.483983"
|
||||||
y="16.134649"
|
y="16.134649"
|
||||||
style="font-size:17.3333px">u</tspan></text>
|
style="font-size:17.3333px;-inkscape-font-specification:sans-serif;font-family:sans-serif;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal">u</tspan></text>
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
style="font-size:17px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;text-anchor:middle;stroke-width:1.00003"
|
style="font-size:17px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;text-anchor:middle;stroke-width:1.00003;-inkscape-font-specification:sans-serif;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||||
x="16.365566"
|
x="16.365566"
|
||||||
y="18.663307"
|
y="18.663307"
|
||||||
id="text912"
|
id="text912"
|
||||||
transform="scale(1.0000324,0.9999676)"><tspan
|
transform="scale(1.0000324,0.9999676)"><tspan
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan910"
|
id="tspan910"
|
||||||
x="16.365566"
|
x="16.365566"
|
||||||
y="18.663307"
|
y="18.663307"
|
||||||
style="font-size:17px;stroke-width:1.00003">n</tspan></text>
|
style="font-size:17px;stroke-width:1.00003;-inkscape-font-specification:sans-serif;font-family:sans-serif;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal">n</tspan></text>
|
||||||
<g
|
<text
|
||||||
aria-label="("
|
xml:space="preserve"
|
||||||
id="text852"
|
style="font-style:normal;font-weight:normal;font-size:17.3333px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;-inkscape-font-specification:sans-serif;font-stretch:normal;font-variant:normal"
|
||||||
style="font-style:normal;font-weight:normal;font-size:12px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.4396;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
x="-0.69333196"
|
||||||
transform="matrix(1.0022756,0,0,1.2616817,-0.26079098,-9.0560687)">
|
y="17.646639"
|
||||||
<path
|
id="text2"><tspan
|
||||||
d="M 2.712,9.86 C 1.536,11.528 0.48,12.956 0.48,15.8 c 0,2.844 1.056,4.272 2.232,5.94 L 3.408,21.26 C 2.328,19.664 1.632,18.368 1.632,15.8 c 0,-2.58 0.696,-3.864 1.776,-5.46 z"
|
id="tspan2"
|
||||||
style="font-size:12px;stroke:#000000;stroke-width:0.4396;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
x="-0.69333196"
|
||||||
id="path854" />
|
y="17.646639"
|
||||||
</g>
|
style="font-size:17.3333px;-inkscape-font-specification:sans-serif;font-family:sans-serif;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal">(</tspan></text>
|
||||||
<g
|
<text
|
||||||
aria-label="("
|
xml:space="preserve"
|
||||||
id="text852-3"
|
style="font-style:normal;font-weight:normal;font-size:17.3333px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;-inkscape-font-specification:sans-serif;font-stretch:normal;font-variant:normal"
|
||||||
style="font-style:normal;font-weight:normal;font-size:12px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.4396;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
x="18.806667"
|
||||||
transform="matrix(-1.0030304,0,0,1.2658306,24.414952,-9.1000412)">
|
y="17.646639"
|
||||||
<path
|
id="text2-7"><tspan
|
||||||
d="M 2.712,9.86 C 1.536,11.528 0.48,12.956 0.48,15.8 c 0,2.844 1.056,4.272 2.232,5.94 L 3.408,21.26 C 2.328,19.664 1.632,18.368 1.632,15.8 c 0,-2.58 0.696,-3.864 1.776,-5.46 z"
|
id="tspan2-5"
|
||||||
style="font-size:12px;stroke:#000000;stroke-width:0.4396;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
x="18.806667"
|
||||||
id="path854-6" />
|
y="17.646639"
|
||||||
</g>
|
style="font-size:17.3333px;-inkscape-font-specification:sans-serif;font-family:sans-serif;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal">)</tspan></text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.5 KiB |
|
@ -1,96 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="24.0px"
|
|
||||||
height="24.0px"
|
|
||||||
viewBox="0 0 24.0 24.0"
|
|
||||||
version="1.1"
|
|
||||||
id="SVGRoot"
|
|
||||||
sodipodi:docname="Somme gains Bode.svg"
|
|
||||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
|
||||||
<defs
|
|
||||||
id="defs1469" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="63.356768"
|
|
||||||
inkscape:cx="15.947723"
|
|
||||||
inkscape:cy="5.6917309"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1011"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid2039" />
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid2058" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<metadata
|
|
||||||
id="metadata1472">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Calque 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
|
||||||
<path
|
|
||||||
id="rect838"
|
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
|
||||||
d="M 2,2 H 8 V 3 H 4 L 7,6 4,9 h 4 v 1 H 2 V 9 L 5,6 2,3 Z"
|
|
||||||
sodipodi:nodetypes="ccccccccccccc" />
|
|
||||||
<rect
|
|
||||||
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:14.257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
|
|
||||||
id="rect835"
|
|
||||||
width="14"
|
|
||||||
height="2"
|
|
||||||
x="0"
|
|
||||||
y="17" />
|
|
||||||
<circle
|
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
|
||||||
id="path837"
|
|
||||||
cx="13"
|
|
||||||
cy="18"
|
|
||||||
r="4" />
|
|
||||||
<path
|
|
||||||
id="rect837"
|
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2.10035"
|
|
||||||
transform="rotate(30)"
|
|
||||||
d="m 20.686533,-6.169873 2.232051,-0.1339746 -0.06218,13.8923049 -2.23205,0.1339746 z"
|
|
||||||
sodipodi:nodetypes="ccccc" />
|
|
||||||
<g
|
|
||||||
aria-label="G"
|
|
||||||
id="text846"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:12px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none">
|
|
||||||
<path
|
|
||||||
d="m 13,2 c 0,0 -3,0 -3,3 0,3 0,5 3,5 2,0 3,0 3,-2 V 6 h -3 v 1 h 2 v 1 c 0,1 -1,1 -2,1 -1,0 -2,0 -2,-4 0,-1 1,-2 2,-2 2,0 2,1 2,1 h 1 c 0,0 0,-2 -3,-2 z"
|
|
||||||
style="font-size:12px"
|
|
||||||
id="path848"
|
|
||||||
sodipodi:nodetypes="sssccccccsssccs" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.1 KiB |
|
@ -1,93 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="24.0px"
|
|
||||||
height="24.0px"
|
|
||||||
viewBox="0 0 24.0 24.0"
|
|
||||||
version="1.1"
|
|
||||||
id="SVGRoot"
|
|
||||||
sodipodi:docname="Somme gains Bode.svg"
|
|
||||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
|
||||||
<defs
|
|
||||||
id="defs1469" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="22.4"
|
|
||||||
inkscape:cx="22.985246"
|
|
||||||
inkscape:cy="9.8906279"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1011"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid2039" />
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid2058" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<metadata
|
|
||||||
id="metadata1472">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Calque 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
|
||||||
<path
|
|
||||||
id="path1414"
|
|
||||||
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
|
|
||||||
d="m 19.979376,19.120606 a 3.5,3.5 0 0 1 -3.06333,3.854578 3.5,3.5 0 0 1 -3.886652,-3.022533 3.5,3.5 0 0 1 2.9814,-3.918293 3.5,3.5 0 0 1 3.9495,2.939936" />
|
|
||||||
<rect
|
|
||||||
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:16.166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
|
|
||||||
id="rect835"
|
|
||||||
width="18"
|
|
||||||
height="2"
|
|
||||||
x="0"
|
|
||||||
y="18.5" />
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:16px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
|
||||||
x="-0.5703125"
|
|
||||||
y="11.875"
|
|
||||||
id="text839"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan837"
|
|
||||||
x="-0.5703125"
|
|
||||||
y="11.875"
|
|
||||||
style="font-size:16px">ΣG</tspan></text>
|
|
||||||
<rect
|
|
||||||
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:19.0663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
|
|
||||||
id="rect835-3"
|
|
||||||
width="25.038315"
|
|
||||||
height="2"
|
|
||||||
x="-10.17229"
|
|
||||||
y="23.748709"
|
|
||||||
ry="0"
|
|
||||||
transform="rotate(-60)" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.3 KiB |
|
@ -1,92 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="24.0px"
|
|
||||||
height="24.0px"
|
|
||||||
viewBox="0 0 24.0 24.0"
|
|
||||||
version="1.1"
|
|
||||||
id="SVGRoot"
|
|
||||||
sodipodi:docname="Somme phases Bode.svg"
|
|
||||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
|
||||||
<defs
|
|
||||||
id="defs10" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="22.4"
|
|
||||||
inkscape:cx="15.347905"
|
|
||||||
inkscape:cy="8.3727678"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
showgrid="true">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid19" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<metadata
|
|
||||||
id="metadata13">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Calque 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;stroke-width:1.41421"
|
|
||||||
id="rect26"
|
|
||||||
width="12"
|
|
||||||
height="2"
|
|
||||||
x="9"
|
|
||||||
y="18" />
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;stroke-width:0.912867"
|
|
||||||
id="rect26-3"
|
|
||||||
width="5"
|
|
||||||
height="2"
|
|
||||||
x="19"
|
|
||||||
y="2" />
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;stroke-width:1.5"
|
|
||||||
id="rect43"
|
|
||||||
width="2"
|
|
||||||
height="16"
|
|
||||||
x="19"
|
|
||||||
y="4" />
|
|
||||||
<circle
|
|
||||||
style="fill:#000000;stroke-width:2.09999"
|
|
||||||
id="path45"
|
|
||||||
cx="20"
|
|
||||||
cy="19"
|
|
||||||
r="3.5" />
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-size:16px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;text-anchor:middle"
|
|
||||||
x="8.7617188"
|
|
||||||
y="11.664062"
|
|
||||||
id="text49"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan47"
|
|
||||||
x="8.7617188"
|
|
||||||
y="11.664062"
|
|
||||||
style="font-size:16px">Σφ</tspan></text>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.4 KiB |
|
@ -1,19 +1,19 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="24.0px"
|
width="24.0px"
|
||||||
height="24.0px"
|
height="24.0px"
|
||||||
viewBox="0 0 24.0 24.0"
|
viewBox="0 0 24.0 24.0"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="SVGRoot"
|
id="SVGRoot"
|
||||||
sodipodi:docname="Text.svg"
|
sodipodi:docname="Text.svg"
|
||||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
<defs
|
<defs
|
||||||
id="defs1469" />
|
id="defs1469" />
|
||||||
<sodipodi:namedview
|
<sodipodi:namedview
|
||||||
|
@ -24,23 +24,38 @@
|
||||||
inkscape:pageopacity="0.0"
|
inkscape:pageopacity="0.0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="22.4"
|
inkscape:zoom="22.4"
|
||||||
inkscape:cx="13.763421"
|
inkscape:cx="13.772321"
|
||||||
inkscape:cy="16.975675"
|
inkscape:cy="8.4598214"
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
inkscape:document-rotation="0"
|
inkscape:document-rotation="0"
|
||||||
showgrid="true"
|
showgrid="true"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1011"
|
inkscape:window-height="1010"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="0"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="1">
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1">
|
||||||
<inkscape:grid
|
<inkscape:grid
|
||||||
type="xygrid"
|
type="xygrid"
|
||||||
id="grid2039" />
|
id="grid2039"
|
||||||
|
originx="0"
|
||||||
|
originy="0"
|
||||||
|
spacingy="1"
|
||||||
|
spacingx="1"
|
||||||
|
units="px"
|
||||||
|
visible="true" />
|
||||||
<inkscape:grid
|
<inkscape:grid
|
||||||
type="xygrid"
|
type="xygrid"
|
||||||
id="grid2058" />
|
id="grid2058"
|
||||||
|
originx="0"
|
||||||
|
originy="0"
|
||||||
|
spacingy="1"
|
||||||
|
spacingx="1"
|
||||||
|
units="px"
|
||||||
|
visible="true" />
|
||||||
</sodipodi:namedview>
|
</sodipodi:namedview>
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata1472">
|
id="metadata1472">
|
||||||
|
@ -50,8 +65,37 @@
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title />
|
<dc:date>2021-2023</dc:date>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Ad5001</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<dc:rights>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>(C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:rights>
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-nc-sa/4.0/" />
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-nc-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:prohibits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<g
|
<g
|
||||||
|
@ -68,10 +112,16 @@
|
||||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.670999;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.670999;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
d="m 4,3 h 8 V 5 H 9 v 14 h 3 v 2 H 4 V 19 H 7 V 5 H 4 Z"
|
d="m 4,3 h 8 V 5 H 9 v 14 h 3 v 2 H 4 V 19 H 7 V 5 H 4 Z"
|
||||||
sodipodi:nodetypes="ccccccccccccc" />
|
sodipodi:nodetypes="ccccccccccccc" />
|
||||||
<path
|
<text
|
||||||
id="rect837"
|
xml:space="preserve"
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
style="font-style:normal;font-weight:normal;font-size:17px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none"
|
||||||
d="m 16,5 h 2 v 3 h 2 v 2 h -2 v 5 c 0,2 2,2 2,2 v 2 c 0,0 -4,0 -4,-4 V 10 H 14 V 8 h 2 z"
|
x="13.844"
|
||||||
sodipodi:nodetypes="ccccccccccccccc" />
|
y="17.387978"
|
||||||
|
id="text1"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan1"
|
||||||
|
x="13.844"
|
||||||
|
y="17.387978"
|
||||||
|
style="font-size:17px">t</tspan></text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 4.1 KiB |
|
@ -1,47 +1,17 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="24.0px"
|
width="24.0px"
|
||||||
height="24.0px"
|
height="24.0px"
|
||||||
viewBox="0 0 24.0 24.0"
|
viewBox="0 0 24.0 24.0"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="SVGRoot"
|
id="SVGRoot"
|
||||||
sodipodi:docname="X Cursor.svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
<defs
|
<defs
|
||||||
id="defs1469" />
|
id="defs1469" />
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="22.4"
|
|
||||||
inkscape:cx="19.545462"
|
|
||||||
inkscape:cy="13.163586"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:window-width="1829"
|
|
||||||
inkscape:window-height="916"
|
|
||||||
inkscape:window-x="19"
|
|
||||||
inkscape:window-y="31"
|
|
||||||
inkscape:window-maximized="0">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid2039" />
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid2058" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata1472">
|
id="metadata1472">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
|
@ -50,13 +20,40 @@
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title />
|
<dc:date>2021-2023</dc:date>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Ad5001</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<dc:rights>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>(C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:rights>
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-nc-sa/4.0/" />
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-nc-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:prohibits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<g
|
<g
|
||||||
inkscape:label="Calque 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
id="layer1">
|
||||||
<g
|
<g
|
||||||
aria-label="X"
|
aria-label="X"
|
||||||
|
@ -68,18 +65,18 @@
|
||||||
id="rect12"
|
id="rect12"
|
||||||
width="2"
|
width="2"
|
||||||
height="24"
|
height="24"
|
||||||
x="17"
|
x="5"
|
||||||
y="0"
|
y="0"
|
||||||
ry="2.14841e-13" />
|
ry="2.14841e-13" />
|
||||||
<path
|
<text
|
||||||
id="rect835"
|
xml:space="preserve"
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
style="font-style:normal;font-weight:normal;font-size:17px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none"
|
||||||
d="m 5,2 h 2 l 8,14 h -2 z"
|
x="10.915"
|
||||||
sodipodi:nodetypes="ccccc" />
|
y="13.713"
|
||||||
<path
|
id="text1"><tspan
|
||||||
id="rect835-6"
|
id="tspan1"
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
x="10.915"
|
||||||
d="M 15,2 H 13 L 5,16 h 2 z"
|
y="13.713"
|
||||||
sodipodi:nodetypes="ccccc" />
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17px;font-family:sans-serif;-inkscape-font-specification:sans-serif">X</tspan></text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.9 KiB |
|
@ -0,0 +1 @@
|
||||||
|
../common/appearance.svg
|
|
@ -0,0 +1 @@
|
||||||
|
../common/label.svg
|
|
@ -0,0 +1 @@
|
||||||
|
../common/text.svg
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
// 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
|
||||||
|
.import "canvas.mjs" as CanvasAPI
|
||||||
|
.import "io.mjs" as IOAPI
|
||||||
|
.import "preferences.mjs" as PreferencesAPI
|
522
LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/canvas.mjs
Normal file
|
@ -0,0 +1,522 @@
|
||||||
|
/**
|
||||||
|
* 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 {Module} from "./modules.mjs"
|
||||||
|
import {textsup} from "./utils.mjs"
|
||||||
|
import {Expression} from "./mathlib.mjs"
|
||||||
|
|
||||||
|
|
||||||
|
class CanvasAPI extends Module {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('Canvas', [
|
||||||
|
Modules.Objects,
|
||||||
|
Modules.History
|
||||||
|
])
|
||||||
|
|
||||||
|
/** @type {HTMLCanvasElement} */
|
||||||
|
this._canvas = null
|
||||||
|
|
||||||
|
/** @type {CanvasRenderingContext2D} */
|
||||||
|
this._ctx = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Object}
|
||||||
|
* @property {function(string, string, string)} showDialog
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this._drawingErrorDialog = null
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Object.<string, {expression: Expression, value: number, maxDraw: number}>}
|
||||||
|
*/
|
||||||
|
this.axesSteps = {
|
||||||
|
x: {
|
||||||
|
expression: null,
|
||||||
|
value: -1,
|
||||||
|
maxDraw: -1
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
expression: null,
|
||||||
|
value: -1,
|
||||||
|
maxDraw: -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize(canvasObject, drawingErrorDialog) {
|
||||||
|
this._canvas = canvasObject
|
||||||
|
this._drawingErrorDialog = drawingErrorDialog
|
||||||
|
}
|
||||||
|
|
||||||
|
get width() { return this._canvas.width }
|
||||||
|
|
||||||
|
get height() { return this._canvas.height }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum x of the diagram, provided from settings.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get xmin() { return this._canvas.xmin }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zoom on the x-axis of the diagram, provided from settings.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get xzoom() { return this._canvas.xzoom }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum y of the diagram, provided from settings.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get ymax() { return this._canvas.ymax }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zoom on the y-axis of the diagram, provided from settings.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get yzoom() { return this._canvas.yzoom }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label used on the x-axis, provided from settings.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
get xlabel() { return this._canvas.xlabel }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label used on the y-axis, provided from settings.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
get ylabel() { return this._canvas.ylabel }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Width of lines that will be drawn into the canvas, provided from settings.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get linewidth() { return this._canvas.linewidth }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Font size of the text that will be drawn into the canvas, provided from settings.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get textsize() { return this._canvas.textsize }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the canvas should be in logarithmic mode, false otherwise.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
get logscalex() { return this._canvas.logscalex }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the x graduation should be shown, false otherwise.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
get showxgrad() { return this._canvas.showxgrad }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the y graduation should be shown, false otherwise.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
get showygrad() { return this._canvas.showygrad }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max power of the logarithmic scaled on the x axis in logarithmic mode.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get maxgradx() { return this._canvas.maxgradx }
|
||||||
|
|
||||||
|
//
|
||||||
|
// Methods to draw the canvas
|
||||||
|
//
|
||||||
|
|
||||||
|
requestPaint() {
|
||||||
|
this._canvas.requestPaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redraws the entire canvas
|
||||||
|
*/
|
||||||
|
redraw() {
|
||||||
|
this._ctx = this._canvas.getContext("2d")
|
||||||
|
this._computeAxes()
|
||||||
|
this._reset()
|
||||||
|
this._drawGrid()
|
||||||
|
this._drawAxes()
|
||||||
|
this._drawLabels()
|
||||||
|
this._ctx.lineWidth = this.linewidth
|
||||||
|
for(let objType in Modules.Objects.currentObjects) {
|
||||||
|
for(let obj of Modules.Objects.currentObjects[objType]){
|
||||||
|
this._ctx.strokeStyle = obj.color
|
||||||
|
this._ctx.fillStyle = obj.color
|
||||||
|
if(obj.visible)
|
||||||
|
try {
|
||||||
|
obj.draw(this)
|
||||||
|
} catch(e) {
|
||||||
|
// Drawing throws an error. Generally, it's due to a new modification (or the opening of a file)
|
||||||
|
this._drawingErrorDialog.showDialog(objType, obj.name, e.message)
|
||||||
|
Modules.History.undo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._ctx.lineWidth = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates informations for drawing gradations for axes.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_computeAxes() {
|
||||||
|
let exprY = new Expression(`x*(${this._canvas.yaxisstep})`)
|
||||||
|
let y1 = exprY.execute(1)
|
||||||
|
let exprX = new Expression(`x*(${this._canvas.xaxisstep})`)
|
||||||
|
let x1 = exprX.execute(1)
|
||||||
|
this.axesSteps = {
|
||||||
|
x: {
|
||||||
|
expression: exprX,
|
||||||
|
value: x1,
|
||||||
|
maxDraw: Math.ceil(Math.max(Math.abs(this.xmin), Math.abs(this.px2x(this.width)))/x1)
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
expression: exprY,
|
||||||
|
value: y1,
|
||||||
|
maxDraw: Math.ceil(Math.max(Math.abs(this.ymax), Math.abs(this.px2y(this.height)))/y1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the canvas to a blank one with default setting.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_reset(){
|
||||||
|
// Reset
|
||||||
|
this._ctx.fillStyle = "#FFFFFF"
|
||||||
|
this._ctx.strokeStyle = "#000000"
|
||||||
|
this._ctx.font = `${this.textsize}px sans-serif`
|
||||||
|
this._ctx.fillRect(0,0,this.width,this.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the grid.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_drawGrid() {
|
||||||
|
this._ctx.strokeStyle = "#C0C0C0"
|
||||||
|
if(this.logscalex) {
|
||||||
|
for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow++) {
|
||||||
|
for(let xmulti = 1; xmulti < 10; xmulti++) {
|
||||||
|
this.drawXLine(Math.pow(10, xpow)*xmulti)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(let x = 0; x < this.axesSteps.x.maxDraw; x+=1) {
|
||||||
|
this.drawXLine(x*this.axesSteps.x.value)
|
||||||
|
this.drawXLine(-x*this.axesSteps.x.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(let y = 0; y < this.axesSteps.y.maxDraw; y+=1) {
|
||||||
|
this.drawYLine(y*this.axesSteps.y.value)
|
||||||
|
this.drawYLine(-y*this.axesSteps.y.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the graph axes.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_drawAxes() {
|
||||||
|
this._ctx.strokeStyle = "#000000"
|
||||||
|
let axisypos = this.logscalex ? 1 : 0
|
||||||
|
this.drawXLine(axisypos)
|
||||||
|
this.drawYLine(0)
|
||||||
|
let axisypx = this.x2px(axisypos) // X coordinate of Y axis
|
||||||
|
let axisxpx = this.y2px(0) // Y coordinate of X axis
|
||||||
|
// Drawing arrows
|
||||||
|
this.drawLine(axisypx, 0, axisypx-10, 10)
|
||||||
|
this.drawLine(axisypx, 0, axisypx+10, 10)
|
||||||
|
this.drawLine(this.width, axisxpx, this.width-10, axisxpx-10)
|
||||||
|
this.drawLine(this.width, axisxpx, this.width-10, axisxpx+10)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the canvas to a blank one with default setting.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_drawLabels() {
|
||||||
|
let axisypx = this.x2px(this.logscalex ? 1 : 0) // X coordinate of Y axis
|
||||||
|
let axisxpx = this.y2px(0) // Y coordinate of X axis
|
||||||
|
// Labels
|
||||||
|
this._ctx.fillStyle = "#000000"
|
||||||
|
this._ctx.font = `${this.textsize}px sans-serif`
|
||||||
|
this._ctx.fillText(this.ylabel, axisypx+10, 24)
|
||||||
|
let textWidth = this._ctx.measureText(this.xlabel).width
|
||||||
|
this._ctx.fillText(this.xlabel, this.width-14-textWidth, axisxpx-5)
|
||||||
|
// Axis graduation labels
|
||||||
|
this._ctx.font = `${this.textsize-4}px sans-serif`
|
||||||
|
|
||||||
|
let txtMinus = this._ctx.measureText('-').width
|
||||||
|
if(this.showxgrad) {
|
||||||
|
if(this.logscalex) {
|
||||||
|
for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow+=1) {
|
||||||
|
textWidth = this._ctx.measureText("10"+textsup(xpow)).width
|
||||||
|
if(xpow !== 0)
|
||||||
|
this.drawVisibleText("10"+textsup(xpow), this.x2px(Math.pow(10,xpow))-textWidth/2, axisxpx+16+(6*(xpow===1)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(let x = 1; x < this.axesSteps.x.maxDraw; x += 1) {
|
||||||
|
let drawX = x*this.axesSteps.x.value
|
||||||
|
let txtX = this.axesSteps.x.expression.simplify(x).replace(/^\((.+)\)$/, '$1')
|
||||||
|
let textHeight = this.measureText(txtX).height
|
||||||
|
this.drawVisibleText(txtX, this.x2px(drawX)-4, axisxpx+this.textsize/2+textHeight)
|
||||||
|
this.drawVisibleText('-'+txtX, this.x2px(-drawX)-4, axisxpx+this.textsize/2+textHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(this.showygrad) {
|
||||||
|
for(let y = 0; y < this.axesSteps.y.maxDraw; y += 1) {
|
||||||
|
let drawY = y*this.axesSteps.y.value
|
||||||
|
let txtY = this.axesSteps.y.expression.simplify(y).replace(/^\((.+)\)$/, '$1')
|
||||||
|
textWidth = this._ctx.measureText(txtY).width
|
||||||
|
this.drawVisibleText(txtY, axisypx-6-textWidth, this.y2px(drawY)+4+(10*(y===0)))
|
||||||
|
if(y !== 0)
|
||||||
|
this.drawVisibleText('-'+txtY, axisypx-6-textWidth-txtMinus, this.y2px(-drawY)+4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._ctx.fillStyle = "#FFFFFF"
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Public functions
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an horizontal line at x plot coordinate.
|
||||||
|
* @param {number} x
|
||||||
|
*/
|
||||||
|
drawXLine(x) {
|
||||||
|
if(this.isVisible(x, this.ymax)) {
|
||||||
|
this.drawLine(this.x2px(x), 0, this.x2px(x), this.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an vertical line at y plot coordinate
|
||||||
|
* @param {number} y
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
drawYLine(y) {
|
||||||
|
if(this.isVisible(this.xmin, y)) {
|
||||||
|
this.drawLine(0, this.y2px(y), this.width, this.y2px(y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes multiline text onto the canvas.
|
||||||
|
* NOTE: The x and y properties here are relative to the canvas, not the plot.
|
||||||
|
* @param {string} text
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
*/
|
||||||
|
drawVisibleText(text, x, y) {
|
||||||
|
if(x > 0 && x < this.width && y > 0 && y < this.height) {
|
||||||
|
text.toString().split("\n").forEach((txt, i) => {
|
||||||
|
this._ctx.fillText(txt, x, y+(this.textsize*i))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an image onto the canvas.
|
||||||
|
* NOTE: The x, y width and height properties here are relative to the canvas, not the plot.
|
||||||
|
* @param {CanvasImageSource} image
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @param {number} width
|
||||||
|
* @param {number} height
|
||||||
|
*/
|
||||||
|
drawVisibleImage(image, x, y, width, height) {
|
||||||
|
this._canvas.markDirty(Qt.rect(x, y, width, height));
|
||||||
|
this._ctx.drawImage(image, x, y, width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Measures the width and height of a multiline text that would be drawn onto the canvas.
|
||||||
|
* @param {string} text
|
||||||
|
* @returns {{width: number, height: number}}
|
||||||
|
*/
|
||||||
|
measureText(text) {
|
||||||
|
let theight = 0
|
||||||
|
let twidth = 0
|
||||||
|
let defaultHeight = this.textsize * 1.2 // Approximate but good enough!
|
||||||
|
for(let txt of text.split("\n")) {
|
||||||
|
theight += defaultHeight
|
||||||
|
if(this._ctx.measureText(txt).width > twidth) twidth = this._ctx.measureText(txt).width
|
||||||
|
}
|
||||||
|
return {'width': twidth, 'height': theight}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an x coordinate to its relative position on the canvas.
|
||||||
|
* It supports both logarithmic and non-logarithmic scale depending on the currently selected mode.
|
||||||
|
* @param {number} x
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
x2px(x) {
|
||||||
|
return this._canvas.x2px(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an y coordinate to it's relative position on the canvas.
|
||||||
|
* The y-axis not supporting logarithmic scale, it only supports linear conversion.
|
||||||
|
* @param {number} y
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
y2px(y) {
|
||||||
|
return this._canvas.y2px(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an x px position on the canvas to it's corresponding coordinate on the plot.
|
||||||
|
* It supports both logarithmic and non-logarithmic scale depending on the currently selected mode.
|
||||||
|
* @param {number} px
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
px2x(px) {
|
||||||
|
return this._canvas.px2x(px)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an x px position on the canvas to it's corresponding coordinate on the plot.
|
||||||
|
* It supports both logarithmic and non logarithmic scale depending on the currently selected mode.
|
||||||
|
* @param {number} px
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
px2y(px) {
|
||||||
|
return this._canvas.px2y(px)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a plot point (x, y) is visible or not on the canvas.
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isVisible(x, y) {
|
||||||
|
return (this.x2px(x) >= 0 && this.x2px(x) <= this.width) && (this.y2px(y) >= 0 && this.y2px(y) <= this.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a line from plot point (x1, y1) to plot point (x2, y2).
|
||||||
|
* @param {number} x1
|
||||||
|
* @param {number} y1
|
||||||
|
* @param {number} x2
|
||||||
|
* @param {number} y2
|
||||||
|
*/
|
||||||
|
drawLine(x1, y1, x2, y2) {
|
||||||
|
this._ctx.beginPath();
|
||||||
|
this._ctx.moveTo(x1, y1);
|
||||||
|
this._ctx.lineTo(x2, y2);
|
||||||
|
this._ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a dashed line from plot point (x1, y1) to plot point (x2, y2).
|
||||||
|
* @param {number} x1
|
||||||
|
* @param {number} y1
|
||||||
|
* @param {number} x2
|
||||||
|
* @param {number} y2
|
||||||
|
* @param {number} dashPxSize
|
||||||
|
*/
|
||||||
|
drawDashedLine(x1, y1, x2, y2, dashPxSize = 6) {
|
||||||
|
this._ctx.setLineDash([dashPxSize/2, dashPxSize]);
|
||||||
|
this.drawLine(x1, y1, x2, y2)
|
||||||
|
this._ctx.setLineDash([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders latex markup ltxText to an image and loads it. Returns a dictionary with three values: source, width and height.
|
||||||
|
* @param {string} ltxText
|
||||||
|
* @param {string} color
|
||||||
|
* @param {function({width: number, height: number, source: string})} callback
|
||||||
|
*/
|
||||||
|
renderLatexImage(ltxText, color, callback) {
|
||||||
|
let [ltxSrc, ltxWidth, ltxHeight] = Latex.render(ltxText, this.textsize, color).split(",")
|
||||||
|
let imgData = {
|
||||||
|
"source": ltxSrc,
|
||||||
|
"width": parseFloat(ltxWidth),
|
||||||
|
"height": parseFloat(ltxHeight)
|
||||||
|
};
|
||||||
|
if(!this._canvas.isImageLoaded(ltxSrc) && !this._canvas.isImageLoading(ltxSrc)){
|
||||||
|
// Wait until the image is loaded to callback.
|
||||||
|
this._canvas.loadImage(ltxSrc)
|
||||||
|
this._canvas.imageLoaders[ltxSrc] = [callback, imgData]
|
||||||
|
} else {
|
||||||
|
// Callback directly
|
||||||
|
callback(imgData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Context methods
|
||||||
|
//
|
||||||
|
|
||||||
|
get font() { return this._ctx.font }
|
||||||
|
set font(value) { return this._ctx.font = value }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an act on the canvas centered on a point.
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @param {number} radius
|
||||||
|
* @param {number} startAngle
|
||||||
|
* @param {number} endAngle
|
||||||
|
* @param {boolean} counterclockwise
|
||||||
|
*/
|
||||||
|
arc(x, y, radius, startAngle, endAngle, counterclockwise=false) {
|
||||||
|
this._ctx.beginPath()
|
||||||
|
this._ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise)
|
||||||
|
this._ctx.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a filled circle centered on a point.
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @param {number} radius
|
||||||
|
*/
|
||||||
|
disc(x, y, radius) {
|
||||||
|
this._ctx.beginPath();
|
||||||
|
this._ctx.arc(x, y, radius, 0, 2 * Math.PI)
|
||||||
|
this._ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a filled rectangle onto the canvas.
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @param {number} w
|
||||||
|
* @param {number} h
|
||||||
|
*/
|
||||||
|
fillRect(x, y, w, h) {
|
||||||
|
this._ctx.fillRect(x, y, w, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {CanvasAPI} */
|
||||||
|
Modules.Canvas = Modules.Canvas || new CanvasAPI()
|
||||||
|
export const API = Modules.Canvas
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,13 +16,10 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import EditedProperty from "editproperty.mjs"
|
||||||
|
import Objects from "../objects.mjs"
|
||||||
|
|
||||||
.import "editproperty.js" as EP
|
export default class ColorChanged extends EditedProperty {
|
||||||
.import "../objects.js" as Objects
|
|
||||||
|
|
||||||
|
|
||||||
class ColorChanged extends EP.EditedProperty {
|
|
||||||
// Action used everytime when an object's color is changed
|
// Action used everytime when an object's color is changed
|
||||||
type(){return 'ColorChanged'}
|
type(){return 'ColorChanged'}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,16 +16,36 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import { Module } from "../modules.mjs"
|
||||||
|
import Latex from "../math/latex.mjs"
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
var themeTextColor;
|
|
||||||
var imageDepth = 2;
|
|
||||||
var fontSize = 14;
|
|
||||||
|
|
||||||
|
|
||||||
class Action {
|
class HistoryCommonAPI extends Module {
|
||||||
|
constructor() {
|
||||||
|
super('History', [
|
||||||
|
Modules.Latex
|
||||||
|
])
|
||||||
|
// History QML object
|
||||||
|
this.history = null;
|
||||||
|
this.themeTextColor = "#ff0000";
|
||||||
|
this.imageDepth = 2;
|
||||||
|
this.fontSize = 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() { this.history.undo() }
|
||||||
|
redo() { this.history.redo() }
|
||||||
|
clear() { this.history.clear() }
|
||||||
|
addToHistory(action) { this.history.addToHistory(action) }
|
||||||
|
unserialize(...data) { this.history.unserialize(...data) }
|
||||||
|
serialize() { return this.history.serialize() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {HistoryCommonAPI} */
|
||||||
|
Modules.History = Modules.History || new HistoryCommonAPI()
|
||||||
|
|
||||||
|
export const API = Modules.History
|
||||||
|
|
||||||
|
export class Action {
|
||||||
/**
|
/**
|
||||||
* Type of the action.
|
* Type of the action.
|
||||||
*
|
*
|
||||||
|
@ -48,15 +68,11 @@ class Action {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undoes the action.
|
* Undoes the action.
|
||||||
*
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
*/
|
||||||
undo() {}
|
undo() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redoes the action.
|
* Redoes the action.
|
||||||
*
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
*/
|
||||||
redo() {}
|
redo() {}
|
||||||
|
|
||||||
|
@ -64,7 +80,7 @@ class Action {
|
||||||
* Export the action to a serializable format.
|
* Export the action to a serializable format.
|
||||||
* NOTE: These arguments will be reinputed in the constructor in this order.
|
* NOTE: These arguments will be reinputed in the constructor in this order.
|
||||||
*
|
*
|
||||||
* @returns {string}
|
* @returns {string[]}
|
||||||
*/
|
*/
|
||||||
export() {
|
export() {
|
||||||
return [this.targetName, this.targetType]
|
return [this.targetName, this.targetType]
|
||||||
|
@ -86,7 +102,7 @@ class Action {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
getIconRichText(type) {
|
getIconRichText(type) {
|
||||||
return `<img source="../icons/objects/${type}.svg" style="color: ${themeTextColor};" width=18 height=18></img>`
|
return `<img source="../icons/objects/${type}.svg" style="color: ${Modules.History.themeTextColor};" width=18 height=18></img>`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,12 +114,17 @@ class Action {
|
||||||
renderLatexAsHtml(latexString) {
|
renderLatexAsHtml(latexString) {
|
||||||
if(!Latex.enabled)
|
if(!Latex.enabled)
|
||||||
throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.")
|
throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.")
|
||||||
let latexInfo = Latex.Renderer.render(latexString, imageDepth*(fontSize+2), themeTextColor).split(",")
|
let imgDepth = Modules.History.imageDepth
|
||||||
return `<img src="${latexInfo[0]}" width="${parseInt(latexInfo[1])/imageDepth}" height="${parseInt(latexInfo[2])/imageDepth}" style="vertical-align: middle"></img>`
|
let [src, width, height] = Latex.render(
|
||||||
|
latexString,
|
||||||
|
imgDepth * (Modules.History.fontSize + 2),
|
||||||
|
Modules.History.themeTextColor
|
||||||
|
).split(",")
|
||||||
|
return `<img src="${src}" width="${parseInt(width)/imgDepth}" height="${parseInt(height)/imgDepth}" style="vertical-align: middle"/>`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string with the HTML-formated description of the action.
|
* Returns a string with the HTML-formatted description of the action.
|
||||||
*
|
*
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,12 +16,10 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import Objects from "../objects.mjs"
|
||||||
|
import { Action } from "common.mjs"
|
||||||
|
|
||||||
.import "../objects.js" as Objects
|
export default class CreateNewObject extends Action {
|
||||||
.import "common.js" as C
|
|
||||||
|
|
||||||
class CreateNewObject extends C.Action {
|
|
||||||
// Action used for the creation of an object
|
// Action used for the creation of an object
|
||||||
type(){return 'CreateNewObject'}
|
type(){return 'CreateNewObject'}
|
||||||
|
|
||||||
|
@ -37,10 +35,6 @@ class CreateNewObject extends C.Action {
|
||||||
|
|
||||||
undo() {
|
undo() {
|
||||||
Objects.deleteObject(this.targetName)
|
Objects.deleteObject(this.targetName)
|
||||||
//let targetIndex = Objects.getObjectsName(this.targetType).indexOf(this.targetName)
|
|
||||||
//delete Objects.currentObjectsByName[this.targetName]
|
|
||||||
//Objects.currentObjects[this.targetType][targetIndex].delete()
|
|
||||||
//Objects.currentObjects[this.targetType].splice(targetIndex, 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
redo() {
|
redo() {
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,14 +16,14 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import Objects from "../objects.mjs"
|
||||||
|
import CreateNewObject from "create.mjs"
|
||||||
.import "../objects.js" as Objects
|
|
||||||
.import "create.js" as Create
|
|
||||||
|
|
||||||
|
|
||||||
class DeleteObject extends Create.CreateNewObject {
|
export default class DeleteObject extends CreateNewObject {
|
||||||
// Action used at the deletion of an object. Basicly the same thing as creating a new object, except Redo & Undo are reversed.
|
/**
|
||||||
|
* Action used at the deletion of an object. Basically the same thing as creating a new object, except Redo & Undo are reversed.
|
||||||
|
*/
|
||||||
type(){return 'DeleteObject'}
|
type(){return 'DeleteObject'}
|
||||||
|
|
||||||
icon(){return 'delete'}
|
icon(){return 'delete'}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,15 +16,13 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import Objects from "../objects.mjs"
|
||||||
|
import Latex from "../math/latex.mjs"
|
||||||
|
import * as MathLib from "../mathlib.mjs"
|
||||||
|
import { Action } from "common.mjs"
|
||||||
|
import { DrawableObject } from "../objs/common.mjs"
|
||||||
|
|
||||||
.import "../objects.js" as Objects
|
export default class EditedProperty extends Action {
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
.import "../mathlib.js" as MathLib
|
|
||||||
.import "../objs/common.js" as Common
|
|
||||||
.import "common.js" as C
|
|
||||||
|
|
||||||
class EditedProperty extends C.Action {
|
|
||||||
// Action used everytime an object's property has been changed
|
// Action used everytime an object's property has been changed
|
||||||
type(){return 'EditedProperty'}
|
type(){return 'EditedProperty'}
|
||||||
|
|
||||||
|
@ -33,7 +31,16 @@ class EditedProperty extends C.Action {
|
||||||
color(darkVer=false){
|
color(darkVer=false){
|
||||||
return darkVer ? 'darkslateblue' : 'cyan';
|
return darkVer ? 'darkslateblue' : 'cyan';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} targetName - Name of the object to target
|
||||||
|
* @param {string} targetType - Type of the object to target.
|
||||||
|
* @param {string} targetProperty - Property being changed
|
||||||
|
* @param {any} previousValue - Previous value before change
|
||||||
|
* @param {any} newValue - New value after change
|
||||||
|
* @param {boolean} valueIsExpressionNeedingImport - True if the value needs to be imported. (e.g expressions)
|
||||||
|
*/
|
||||||
constructor(targetName = "", targetType = "Point", targetProperty = "visible", previousValue = false, newValue = true, valueIsExpressionNeedingImport = false) {
|
constructor(targetName = "", targetType = "Point", targetProperty = "visible", previousValue = false, newValue = true, valueIsExpressionNeedingImport = false) {
|
||||||
super(targetName, targetType)
|
super(targetName, targetType)
|
||||||
this.targetProperty = targetProperty
|
this.targetProperty = targetProperty
|
||||||
|
@ -42,10 +49,10 @@ class EditedProperty extends C.Action {
|
||||||
this.newValue = newValue
|
this.newValue = newValue
|
||||||
this.propertyType = Objects.types[targetType].properties()[targetProperty]
|
this.propertyType = Objects.types[targetType].properties()[targetProperty]
|
||||||
if(valueIsExpressionNeedingImport) {
|
if(valueIsExpressionNeedingImport) {
|
||||||
if(typeof this.propertyType == 'object' && this.propertyType.type == "Expression") {
|
if(typeof this.propertyType == 'object' && this.propertyType.type === "Expression") {
|
||||||
this.previousValue = new MathLib.Expression(this.previousValue);
|
this.previousValue = new MathLib.Expression(this.previousValue);
|
||||||
this.newValue = new MathLib.Expression(this.newValue);
|
this.newValue = new MathLib.Expression(this.newValue);
|
||||||
} else if(this.propertyType == "Domain") {
|
} else if(this.propertyType === "Domain") {
|
||||||
this.previousValue = MathLib.parseDomain(this.previousValue);
|
this.previousValue = MathLib.parseDomain(this.previousValue);
|
||||||
this.newValue = MathLib.parseDomain(this.newValue);
|
this.newValue = MathLib.parseDomain(this.newValue);
|
||||||
} else {
|
} else {
|
||||||
|
@ -70,7 +77,7 @@ class EditedProperty extends C.Action {
|
||||||
export() {
|
export() {
|
||||||
if(this.previousValue instanceof MathLib.Expression) {
|
if(this.previousValue instanceof MathLib.Expression) {
|
||||||
return [this.targetName, this.targetType, this.targetProperty, this.previousValue.toEditableString(), this.newValue.toEditableString(), true]
|
return [this.targetName, this.targetType, this.targetProperty, this.previousValue.toEditableString(), this.newValue.toEditableString(), true]
|
||||||
} else if(this.previousValue instanceof Common.DrawableObject) {
|
} else if(this.previousValue instanceof DrawableObject) {
|
||||||
return [this.targetName, this.targetType, this.targetProperty, this.previousValue.name, this.newValue.name, true]
|
return [this.targetName, this.targetType, this.targetProperty, this.previousValue.name, this.newValue.name, true]
|
||||||
} else {
|
} else {
|
||||||
return [this.targetName, this.targetType, this.targetProperty, this.previousValue, this.newValue, false]
|
return [this.targetName, this.targetType, this.targetProperty, this.previousValue, this.newValue, false]
|
||||||
|
@ -110,7 +117,7 @@ class EditedProperty extends C.Action {
|
||||||
// HTML
|
// HTML
|
||||||
this.prevHTML = '<tt style="background: rgba(128,128,128,0.1);"> '+this.prevString+' </tt>'
|
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>'
|
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.prevHTML= this.renderLatexAsHtml(this.previousValue.latexMarkup)
|
||||||
this.nextHTML= this.renderLatexAsHtml(this.newValue.latexMarkup)
|
this.nextHTML= this.renderLatexAsHtml(this.newValue.latexMarkup)
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,19 +16,16 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import EditedProperty from "editproperty.mjs"
|
||||||
|
import Objects from "../objects.mjs"
|
||||||
.import "editproperty.js" as EP
|
|
||||||
.import "../objects.js" as Objects
|
|
||||||
|
|
||||||
|
|
||||||
class NameChanged extends EP.EditedProperty {
|
export default class NameChanged extends EditedProperty {
|
||||||
// Action used everytime an object's property has been changed
|
// Action used everytime an object's property has been changed
|
||||||
type(){return 'NameChanged'}
|
type(){return 'NameChanged'}
|
||||||
|
|
||||||
icon(){return 'name'}
|
icon(){return 'name'}
|
||||||
|
|
||||||
|
|
||||||
color(darkVer=false){return darkVer ? 'darkorange' : 'orange'}
|
color(darkVer=false){return darkVer ? 'darkorange' : 'orange'}
|
||||||
|
|
||||||
constructor(targetName = "", targetType = "Point", newName = "") {
|
constructor(targetName = "", targetType = "Point", newName = "") {
|
||||||
|
@ -45,10 +42,6 @@ class NameChanged extends EP.EditedProperty {
|
||||||
|
|
||||||
redo() {
|
redo() {
|
||||||
Objects.renameObject(this.previousValue, this.newValue)
|
Objects.renameObject(this.previousValue, this.newValue)
|
||||||
//let obj = Objects.currentObjectsByName[this.previousValue]
|
|
||||||
//obj.name = this.newValue
|
|
||||||
//Objects.currentObjectsByName[this.newValue] = obj
|
|
||||||
//delete Objects.currentObjectsByName[this.previousValue]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,16 +16,14 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import Objects from "../objects.mjs"
|
||||||
|
import Latex from "../math/latex.mjs"
|
||||||
|
import * as MathLib from "../mathlib.mjs"
|
||||||
|
import { escapeHTML } from "../utils.mjs"
|
||||||
|
import { Action } from "common.mjs"
|
||||||
|
import { DrawableObject } from "../objs/common.mjs"
|
||||||
|
|
||||||
.import "../objects.js" as Objects
|
export default class EditedPosition extends Action {
|
||||||
.import "../mathlib.js" as MathLib
|
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
.import "../utils.js" as Utils
|
|
||||||
.import "../objs/common.js" as Common
|
|
||||||
.import "common.js" as C
|
|
||||||
|
|
||||||
class EditedPosition extends C.Action {
|
|
||||||
// Action used for objects that have a X and Y expression properties (points, texts...)
|
// Action used for objects that have a X and Y expression properties (points, texts...)
|
||||||
type(){return 'EditedPosition'}
|
type(){return 'EditedPosition'}
|
||||||
|
|
||||||
|
@ -68,8 +66,8 @@ class EditedPosition extends C.Action {
|
||||||
this.prevHTML = this.renderLatexAsHtml(`\\left(${this.previousXValue.latexMarkup},${this.previousYValue.latexMarkup}\\right)`)
|
this.prevHTML = this.renderLatexAsHtml(`\\left(${this.previousXValue.latexMarkup},${this.previousYValue.latexMarkup}\\right)`)
|
||||||
this.nextHTML = this.renderLatexAsHtml(`\\left(${this.newXValue.latexMarkup},${this.newYValue.latexMarkup}\\right)`)
|
this.nextHTML = this.renderLatexAsHtml(`\\left(${this.newXValue.latexMarkup},${this.newYValue.latexMarkup}\\right)`)
|
||||||
} else {
|
} else {
|
||||||
this.prevHTML = '<tt style="background: rgba(128,128,128,0.1);"> '+Utils.escapeHTML(this.prevString)+' </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);"> '+Utils.escapeHTML(this.nextString)+' </tt>'
|
this.nextHTML = '<tt style="background: rgba(128,128,128,0.1);"> '+escapeHTML(this.nextString)+' </tt>'
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,13 +16,11 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import EditedProperty from "editproperty.mjs"
|
||||||
|
import Objects from "../objects.mjs"
|
||||||
.import "editproperty.js" as EP
|
|
||||||
.import "../objects.js" as Objects
|
|
||||||
|
|
||||||
|
|
||||||
class EditedVisibility extends EP.EditedProperty {
|
export default class EditedVisibility extends EditedProperty {
|
||||||
// Action used when an object's shown or hidden.
|
// Action used when an object's shown or hidden.
|
||||||
type(){return 'EditedVisibility'}
|
type(){return 'EditedVisibility'}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -18,30 +18,27 @@
|
||||||
|
|
||||||
// This library helps containing actions to be undone or redone (in other words, editing history)
|
// This library helps containing actions to be undone or redone (in other words, editing history)
|
||||||
// Each type of event is repertoried as an action that can be listed for everything that's undoable.
|
// Each type of event is repertoried as an action that can be listed for everything that's undoable.
|
||||||
.pragma library
|
|
||||||
|
|
||||||
.import "history/common.js" as Common
|
import { Action as A } from "history/common.mjs"
|
||||||
.import "history/create.js" as Create
|
import Create from "history/create.mjs"
|
||||||
.import "history/delete.js" as Delete
|
import Delete from "history/delete.mjs"
|
||||||
.import "history/editproperty.js" as EP
|
import EP from "history/editproperty.mjs"
|
||||||
.import "history/position.js" as Pos
|
import Pos from "history/position.mjs"
|
||||||
.import "history/visibility.js" as V
|
import V from "history/visibility.mjs"
|
||||||
.import "history/name.js" as Name
|
import Name from "history/name.mjs"
|
||||||
.import "history/color.js" as Color
|
import Color from "history/color.mjs"
|
||||||
|
|
||||||
var history = null;
|
|
||||||
|
|
||||||
|
|
||||||
var Action = Common.Action
|
export const Action = A
|
||||||
var CreateNewObject = Create.CreateNewObject
|
export const CreateNewObject = Create
|
||||||
var DeleteObject = Delete.DeleteObject
|
export const DeleteObject = Delete
|
||||||
var EditedProperty = EP.EditedProperty
|
export const EditedProperty = EP
|
||||||
var EditedPosition = Pos.EditedPosition
|
export const EditedPosition = Pos
|
||||||
var EditedVisibility = V.EditedVisibility
|
export const EditedVisibility = V
|
||||||
var NameChanged = Name.NameChanged
|
export const NameChanged = Name
|
||||||
var ColorChanged = Color.ColorChanged
|
export const ColorChanged = Color
|
||||||
|
|
||||||
var Actions = {
|
export const Actions = {
|
||||||
"Action": Action,
|
"Action": Action,
|
||||||
"CreateNewObject": CreateNewObject,
|
"CreateNewObject": CreateNewObject,
|
||||||
"DeleteObject": DeleteObject,
|
"DeleteObject": DeleteObject,
|
169
LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/io.mjs
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
/**
|
||||||
|
* 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 {Module} from "./modules.mjs"
|
||||||
|
|
||||||
|
class IOAPI extends Module {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('IO', [
|
||||||
|
Modules.Objects,
|
||||||
|
Modules.History
|
||||||
|
])
|
||||||
|
/**
|
||||||
|
* Path of the currently opened file. Empty if no file is opened.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.saveFileName = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes module with QML elements.
|
||||||
|
* @param {LogarithmPlotter} rootElement
|
||||||
|
* @param {Settings} settings
|
||||||
|
* @param {{show: function(string)}} alert
|
||||||
|
*/
|
||||||
|
initialize(rootElement, settings, alert) {
|
||||||
|
this.rootElement = rootElement
|
||||||
|
this.settings = settings
|
||||||
|
this.alert = alert
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the diagram to a certain \c filename.
|
||||||
|
* @param {string} filename
|
||||||
|
*/
|
||||||
|
saveDiagram(filename) {
|
||||||
|
// Add extension if necessary
|
||||||
|
if(['lpf'].indexOf(filename.split('.')[filename.split('.').length-1]) === -1)
|
||||||
|
filename += '.lpf'
|
||||||
|
this.saveFilename = filename
|
||||||
|
let objs = {}
|
||||||
|
for(let objType in Modules.Objects.currentObjects){
|
||||||
|
objs[objType] = []
|
||||||
|
for(let obj of Modules.Objects.currentObjects[objType]) {
|
||||||
|
objs[objType].push(obj.export())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let settings = {
|
||||||
|
"xzoom": this.settings.xzoom,
|
||||||
|
"yzoom": this.settings.yzoom,
|
||||||
|
"xmin": this.settings.xmin,
|
||||||
|
"ymax": this.settings.ymax,
|
||||||
|
"xaxisstep": this.settings.xaxisstep,
|
||||||
|
"yaxisstep": this.settings.yaxisstep,
|
||||||
|
"xaxislabel": this.settings.xlabel,
|
||||||
|
"yaxislabel": this.settings.ylabel,
|
||||||
|
"logscalex": this.settings.logscalex,
|
||||||
|
"linewidth": this.settings.linewidth,
|
||||||
|
"showxgrad": this.settings.showxgrad,
|
||||||
|
"showygrad": this.settings.showygrad,
|
||||||
|
"textsize": this.settings.textsize,
|
||||||
|
"history": Modules.History.serialize(),
|
||||||
|
"width": this.rootElement.width,
|
||||||
|
"height": this.rootElement.height,
|
||||||
|
"objects": objs,
|
||||||
|
"type": "logplotv1"
|
||||||
|
}
|
||||||
|
Helper.write(filename, JSON.stringify(settings))
|
||||||
|
this.alert.show(qsTranslate('io', "Saved plot to '%1'.").arg(filename.split("/").pop()))
|
||||||
|
Modules.History.history.saved = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the diagram from a certain \c filename.
|
||||||
|
* @param {string} filename
|
||||||
|
*/
|
||||||
|
loadDiagram(filename) {
|
||||||
|
let basename = filename.split("/").pop()
|
||||||
|
this.alert.show(qsTranslate('io', "Loading file '%1'.").arg(basename))
|
||||||
|
let data = JSON.parse(Helper.load(filename))
|
||||||
|
let error = "";
|
||||||
|
if(Object.keys(data).includes("type") && data["type"] === "logplotv1") {
|
||||||
|
Modules.History.clear()
|
||||||
|
// Importing settings
|
||||||
|
this.settings.saveFilename = filename
|
||||||
|
this.settings.xzoom = parseFloat(data["xzoom"]) || 100
|
||||||
|
this.settings.yzoom = parseFloat(data["yzoom"]) || 10
|
||||||
|
this.settings.xmin = parseFloat(data["xmin"]) || 5/10
|
||||||
|
this.settings.ymax = parseFloat(data["ymax"]) || 24
|
||||||
|
this.settings.xaxisstep = data["xaxisstep"] || "4"
|
||||||
|
this.settings.yaxisstep = data["yaxisstep"] || "4"
|
||||||
|
this.settings.xlabel = data["xaxislabel"] || ""
|
||||||
|
this.settings.ylabel = data["yaxislabel"] || ""
|
||||||
|
this.settings.logscalex = data["logscalex"] === true
|
||||||
|
if("showxgrad" in data)
|
||||||
|
this.settings.showxgrad = data["showxgrad"]
|
||||||
|
if("showygrad" in data)
|
||||||
|
this.settings.textsize = data["showygrad"]
|
||||||
|
if("linewidth" in data)
|
||||||
|
this.settings.linewidth = data["linewidth"]
|
||||||
|
if("textsize" in data)
|
||||||
|
this.settings.textsize = data["textsize"]
|
||||||
|
this.rootElement.height = parseFloat(data["height"]) || 500
|
||||||
|
this.rootElement.width = parseFloat(data["width"]) || 1000
|
||||||
|
|
||||||
|
// Importing objects
|
||||||
|
Modules.Objects.currentObjects = {}
|
||||||
|
for(let key of Object.keys(Modules.Objects.currentObjectsByName)) {
|
||||||
|
delete Modules.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(Modules.Objects.types).indexOf(objType) > -1) {
|
||||||
|
Modules.Objects.currentObjects[objType] = []
|
||||||
|
for(let objData of data['objects'][objType]) {
|
||||||
|
/** @type {DrawableObject} */
|
||||||
|
let obj = Modules.Objects.types[objType].import(...objData)
|
||||||
|
Modules.Objects.currentObjects[objType].push(obj)
|
||||||
|
Modules.Objects.currentObjectsByName[obj.name] = obj
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error += qsTranslate('io', "Unknown object type: %1.").arg(objType) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updating object dependencies.
|
||||||
|
for(let objName in Modules.Objects.currentObjectsByName)
|
||||||
|
Modules.Objects.currentObjectsByName[objName].update()
|
||||||
|
|
||||||
|
// Importing history
|
||||||
|
if("history" in data)
|
||||||
|
Modules.History.unserialize(...data["history"])
|
||||||
|
|
||||||
|
// Refreshing sidebar
|
||||||
|
this.rootElement.updateObjectsLists()
|
||||||
|
} else {
|
||||||
|
error = qsTranslate('io', "Invalid file provided.")
|
||||||
|
}
|
||||||
|
if(error !== "") {
|
||||||
|
console.log(error)
|
||||||
|
this.alert.show(qsTranslate('io', "Could not save file: ") + error)
|
||||||
|
// TODO: Error handling
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Modules.Canvas.redraw()
|
||||||
|
this.alert.show(qsTranslate('io', "Loaded file '%1'.").arg(basename))
|
||||||
|
Modules.History.history.saved = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {IOAPI} */
|
||||||
|
Modules.IO = Modules.IO || new IOAPI()
|
|
@ -26,7 +26,8 @@ var ADDITIONAL_VARCHARS = [
|
||||||
"ₕ","ₖ","ₗ","ₘ","ₙ","ₚ","ₛ",
|
"ₕ","ₖ","ₗ","ₘ","ₙ","ₚ","ₛ",
|
||||||
"ₜ","¹","²","³","⁴","⁵","⁶",
|
"ₜ","¹","²","³","⁴","⁵","⁶",
|
||||||
"⁷","⁸","⁹","⁰","₁","₂","₃",
|
"⁷","⁸","⁹","⁰","₁","₂","₃",
|
||||||
"₄","₅","₆","₇","₈","₉","₀"
|
"₄","₅","₆","₇","₈","₉","₀",
|
||||||
|
"∞","π"
|
||||||
]
|
]
|
||||||
|
|
||||||
function Instruction(type, value) {
|
function Instruction(type, value) {
|
||||||
|
@ -520,7 +521,7 @@ Expression.prototype.substitute = function (variable, expr) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Expression.prototype.evaluate = function (values) {
|
Expression.prototype.evaluate = function (values) {
|
||||||
values = values || {};
|
values = Object.assign({}, values, this.parser.consts)
|
||||||
return evaluate(this.tokens, this, values);
|
return evaluate(this.tokens, this, values);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -540,8 +541,9 @@ Expression.prototype.variables = function (options) {
|
||||||
var vars = [];
|
var vars = [];
|
||||||
getSymbols(this.tokens, vars, options);
|
getSymbols(this.tokens, vars, options);
|
||||||
var functions = this.functions;
|
var functions = this.functions;
|
||||||
|
var consts = this.parser.consts
|
||||||
return vars.filter(function (name) {
|
return vars.filter(function (name) {
|
||||||
return !(name in functions);
|
return !(name in functions) && !(name in consts);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -579,7 +581,7 @@ function TokenStream(parser, expression) {
|
||||||
this.unaryOps = parser.unaryOps;
|
this.unaryOps = parser.unaryOps;
|
||||||
this.binaryOps = parser.binaryOps;
|
this.binaryOps = parser.binaryOps;
|
||||||
this.ternaryOps = parser.ternaryOps;
|
this.ternaryOps = parser.ternaryOps;
|
||||||
this.consts = parser.consts;
|
this.builtinConsts = parser.builtinConsts;
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
this.savedPosition = 0;
|
this.savedPosition = 0;
|
||||||
this.savedCurrent = null;
|
this.savedCurrent = null;
|
||||||
|
@ -691,7 +693,7 @@ TokenStream.prototype.isConst = function () {
|
||||||
var i = startPos;
|
var i = startPos;
|
||||||
for (; i < this.expression.length; i++) {
|
for (; i < this.expression.length; i++) {
|
||||||
var c = this.expression.charAt(i);
|
var c = this.expression.charAt(i);
|
||||||
if (c.toUpperCase() === c.toLowerCase()) {
|
if (c.toUpperCase() === c.toLowerCase() && !ADDITIONAL_VARCHARS.includes(c)) {
|
||||||
if (i === this.pos || (c !== '_' && c !== '.' && (c < '0' || c > '9'))) {
|
if (i === this.pos || (c !== '_' && c !== '.' && (c < '0' || c > '9'))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -699,8 +701,8 @@ TokenStream.prototype.isConst = function () {
|
||||||
}
|
}
|
||||||
if (i > startPos) {
|
if (i > startPos) {
|
||||||
var str = this.expression.substring(startPos, i);
|
var str = this.expression.substring(startPos, i);
|
||||||
if (str in this.consts) {
|
if (str in this.builtinConsts) {
|
||||||
this.current = this.newToken(TNUMBER, this.consts[str]);
|
this.current = this.newToken(TNUMBER, this.builtinConsts[str]);
|
||||||
this.pos += str.length;
|
this.pos += str.length;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1115,7 +1117,7 @@ ParserState.prototype.parseExpression = function (instr) {
|
||||||
if (this.parseUntilEndStatement(instr, exprInstr)) {
|
if (this.parseUntilEndStatement(instr, exprInstr)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.parseVariableAssignmentExpression(exprInstr);
|
this.parseConditionalExpression(exprInstr);
|
||||||
if (this.parseUntilEndStatement(instr, exprInstr)) {
|
if (this.parseUntilEndStatement(instr, exprInstr)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1155,37 +1157,6 @@ ParserState.prototype.parseArrayList = function (instr) {
|
||||||
return argCount;
|
return argCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
ParserState.prototype.parseVariableAssignmentExpression = function (instr) {
|
|
||||||
this.parseConditionalExpression(instr);
|
|
||||||
while (this.accept(TOP, '=')) {
|
|
||||||
var varName = instr.pop();
|
|
||||||
var varValue = [];
|
|
||||||
var lastInstrIndex = instr.length - 1;
|
|
||||||
if (varName.type === IFUNCALL) {
|
|
||||||
if (!this.tokens.isOperatorEnabled('()=')) {
|
|
||||||
throw new Error(qsTranslate('error', 'Function definition is not permitted.'));
|
|
||||||
}
|
|
||||||
for (var i = 0, len = varName.value + 1; i < len; i++) {
|
|
||||||
var index = lastInstrIndex - i;
|
|
||||||
if (instr[index].type === IVAR) {
|
|
||||||
instr[index] = new Instruction(IVARNAME, instr[index].value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.parseVariableAssignmentExpression(varValue);
|
|
||||||
instr.push(new Instruction(IEXPR, varValue));
|
|
||||||
instr.push(new Instruction(IFUNDEF, varName.value));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (varName.type !== IVAR && varName.type !== IMEMBER) {
|
|
||||||
throw new Error(qsTranslate('error', 'Expected variable for assignment.'));
|
|
||||||
}
|
|
||||||
this.parseVariableAssignmentExpression(varValue);
|
|
||||||
instr.push(new Instruction(IVARNAME, varName.value));
|
|
||||||
instr.push(new Instruction(IEXPR, varValue));
|
|
||||||
instr.push(binaryInstruction('='));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ParserState.prototype.parseConditionalExpression = function (instr) {
|
ParserState.prototype.parseConditionalExpression = function (instr) {
|
||||||
this.parseOrExpression(instr);
|
this.parseOrExpression(instr);
|
||||||
while (this.accept(TOP, '?')) {
|
while (this.accept(TOP, '?')) {
|
||||||
|
@ -1791,12 +1762,12 @@ class Parser {
|
||||||
join: arrayJoin
|
join: arrayJoin
|
||||||
};
|
};
|
||||||
|
|
||||||
this.consts = {
|
// These constants will automatically be replaced the MOMENT they are parsed.
|
||||||
E: Math.E,
|
// (Original consts from the parser)
|
||||||
PI: Math.PI,
|
this.builtinConsts = {};
|
||||||
'true': true,
|
// These consts will only be replaced when the expression is evaluated.
|
||||||
'false': false
|
this.consts = {}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(expr) {
|
parse(expr) {
|
|
@ -0,0 +1,119 @@
|
||||||
|
/**
|
||||||
|
* 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 "../../modules.mjs" as M
|
||||||
|
|
||||||
|
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 M.Module {
|
||||||
|
constructor() {
|
||||||
|
super('ExprParser', [
|
||||||
|
/** @type {ObjectsAPI} */
|
||||||
|
Modules.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} */
|
||||||
|
Modules.ExprParser = Modules.ExprParser || new ExprParserAPI()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
/**
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Type polyfills for IDEs.
|
||||||
|
// Never directly imported.
|
||||||
|
|
||||||
|
Modules = Modules || {}
|
||||||
|
/** @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): string} */
|
||||||
|
QT_TR_NOOP = QT_TR_NOOP || function(string) { throw new Error('QT_TR_NOOP not implemented.'); }
|
||||||
|
/** @type {function(string|boolean|int): string} */
|
||||||
|
String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); }
|
||||||
|
|
||||||
|
const Qt = {
|
||||||
|
/**
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @param {number} width
|
||||||
|
* @param {number} height
|
||||||
|
* @returns {{x, width, y, height}}
|
||||||
|
*/
|
||||||
|
rect: function(x, y, width, height) {
|
||||||
|
return {x: x, y: y, width: width, height: height};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Typehints for Helper. */
|
||||||
|
const Helper = {
|
||||||
|
/** @type {function(string): boolean} */
|
||||||
|
getSettingBool: (setting) => true,
|
||||||
|
/** @type {function(string): int} */
|
||||||
|
getSettingInt: (setting) => 0,
|
||||||
|
/** @type {function(string): string} */
|
||||||
|
getSetting: (setting) => '',
|
||||||
|
/** @type {function(string, boolean)} */
|
||||||
|
setSettingBool: (setting, value) => {},
|
||||||
|
/** @type {function(string, int)} */
|
||||||
|
setSettingInt: (setting, value) => 0,
|
||||||
|
/** @type {function(string, string)} */
|
||||||
|
setSetting: (setting, value) => '',
|
||||||
|
/** @type {function(string, string)} */
|
||||||
|
write: (filename, data) => {},
|
||||||
|
/** @type {function(string): string} */
|
||||||
|
load: (filename) => '',
|
||||||
|
}
|
||||||
|
|
||||||
|
const Latex = {
|
||||||
|
/** @type {function(string, number, string): string} */
|
||||||
|
render: (latex_markup, font_size, color) => '',
|
||||||
|
}
|
|
@ -1,56 +0,0 @@
|
||||||
/**
|
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
|
||||||
* Copyright (C) 2023 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
|
|
||||||
|
|
||||||
const DERIVATION_PRECISION = 0.1
|
|
||||||
|
|
||||||
var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manually
|
|
||||||
"pi": Math.PI,
|
|
||||||
"π": Math.PI,
|
|
||||||
"inf": Infinity,
|
|
||||||
"infinity": Infinity,
|
|
||||||
"Infinity": Infinity,
|
|
||||||
"∞": Infinity,
|
|
||||||
"e": Math.E,
|
|
||||||
"E": Math.E
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentVars = {}
|
|
||||||
var currentObjectsByName = {} // Mirror of currentObjectsByName in objects.js
|
|
||||||
|
|
||||||
const parser = new ExprEval.Parser()
|
|
||||||
|
|
||||||
parser.consts = Object.assign({}, parser.consts, evalVariables)
|
|
||||||
|
|
||||||
// Function definition
|
|
||||||
parser.functions.integral = function(a, b, f, variable) {
|
|
||||||
// https://en.wikipedia.org/wiki/Simpson%27s_rule
|
|
||||||
// Simpler, faster than tokenizing the expression
|
|
||||||
f = parser.parse(f).toJSFunction(variable, currentVars)
|
|
||||||
return (b-a)/6*(f(a)+4*f((a+b)/2)+f(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
parser.functions.derivative = function(f, variable, x) {
|
|
||||||
f = parser.parse(f).toJSFunction(variable, currentVars)
|
|
||||||
return (f(x+DERIVATION_PRECISION/2)-f(x-DERIVATION_PRECISION/2))/DERIVATION_PRECISION
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,21 +16,19 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import { Expression, executeExpression } from "expression.mjs"
|
||||||
|
|
||||||
.import "expression.js" as Expr
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main abstract domain class
|
* Main abstract domain class
|
||||||
* It doesn't represent any kind of domain and is meant to be extended.
|
* It doesn't represent any kind of domain and is meant to be extended.
|
||||||
*/
|
*/
|
||||||
class Domain {
|
export class Domain {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether x is included in the domain.
|
* Checks whether x is included in the domain.
|
||||||
* @param {number} x - The x value.
|
* @param {number} x - The x value.
|
||||||
* @return {bool} true if included, false otherwise.
|
* @return {boolean} true if included, false otherwise.
|
||||||
*/
|
*/
|
||||||
includes(x) { return false }
|
includes(x) { return false }
|
||||||
|
|
||||||
|
@ -72,11 +70,13 @@ class Domain {
|
||||||
case "RP":
|
case "RP":
|
||||||
case "R+":
|
case "R+":
|
||||||
case "ℝ⁺":
|
case "ℝ⁺":
|
||||||
|
case "ℝ+":
|
||||||
return Domain.RP
|
return Domain.RP
|
||||||
break;
|
break;
|
||||||
case "RM":
|
case "RM":
|
||||||
case "R-":
|
case "R-":
|
||||||
case "ℝ⁻":
|
case "ℝ⁻":
|
||||||
|
case "ℝ-":
|
||||||
return Domain.RM
|
return Domain.RM
|
||||||
break;
|
break;
|
||||||
case "RPE":
|
case "RPE":
|
||||||
|
@ -85,6 +85,8 @@ class Domain {
|
||||||
case "R*+":
|
case "R*+":
|
||||||
case "ℝ*⁺":
|
case "ℝ*⁺":
|
||||||
case "ℝ⁺*":
|
case "ℝ⁺*":
|
||||||
|
case "ℝ*+":
|
||||||
|
case "ℝ+*":
|
||||||
return Domain.RPE
|
return Domain.RPE
|
||||||
break;
|
break;
|
||||||
case "RME":
|
case "RME":
|
||||||
|
@ -93,16 +95,21 @@ class Domain {
|
||||||
case "R*-":
|
case "R*-":
|
||||||
case "ℝ⁻*":
|
case "ℝ⁻*":
|
||||||
case "ℝ*⁻":
|
case "ℝ*⁻":
|
||||||
|
case "ℝ-*":
|
||||||
|
case "ℝ*-":
|
||||||
return Domain.RME
|
return Domain.RME
|
||||||
break;
|
break;
|
||||||
case "ℕ":
|
case "ℕ":
|
||||||
case "N":
|
case "N":
|
||||||
case "ZP":
|
case "ZP":
|
||||||
|
case "Z+":
|
||||||
case "ℤ⁺":
|
case "ℤ⁺":
|
||||||
|
case "ℤ+":
|
||||||
return Domain.N
|
return Domain.N
|
||||||
break;
|
break;
|
||||||
case "NLOG":
|
case "NLOG":
|
||||||
case "ℕˡᵒᵍ":
|
case "ℕˡᵒᵍ":
|
||||||
|
case "ℕLOG":
|
||||||
return Domain.NLog
|
return Domain.NLog
|
||||||
break;
|
break;
|
||||||
case "NE":
|
case "NE":
|
||||||
|
@ -111,12 +118,15 @@ class Domain {
|
||||||
case "N+":
|
case "N+":
|
||||||
case "ℕ*":
|
case "ℕ*":
|
||||||
case "ℕ⁺":
|
case "ℕ⁺":
|
||||||
|
case "ℕ+":
|
||||||
case "ZPE":
|
case "ZPE":
|
||||||
case "ZEP":
|
case "ZEP":
|
||||||
case "Z+*":
|
case "Z+*":
|
||||||
case "Z*+":
|
case "Z*+":
|
||||||
case "ℤ⁺*":
|
case "ℤ⁺*":
|
||||||
case "ℤ*⁺":
|
case "ℤ*⁺":
|
||||||
|
case "ℤ+*":
|
||||||
|
case "ℤ*+":
|
||||||
return Domain.NE
|
return Domain.NE
|
||||||
break;
|
break;
|
||||||
case "Z":
|
case "Z":
|
||||||
|
@ -126,6 +136,7 @@ class Domain {
|
||||||
case "ZM":
|
case "ZM":
|
||||||
case "Z-":
|
case "Z-":
|
||||||
case "ℤ⁻":
|
case "ℤ⁻":
|
||||||
|
case "ℤ-":
|
||||||
return Domain.ZM
|
return Domain.ZM
|
||||||
break;
|
break;
|
||||||
case "ZME":
|
case "ZME":
|
||||||
|
@ -134,6 +145,8 @@ class Domain {
|
||||||
case "Z*-":
|
case "Z*-":
|
||||||
case "ℤ⁻*":
|
case "ℤ⁻*":
|
||||||
case "ℤ*⁻":
|
case "ℤ*⁻":
|
||||||
|
case "ℤ-*":
|
||||||
|
case "ℤ*-":
|
||||||
return Domain.ZME
|
return Domain.ZME
|
||||||
break;
|
break;
|
||||||
case "ZE":
|
case "ZE":
|
||||||
|
@ -151,7 +164,7 @@ class Domain {
|
||||||
/**
|
/**
|
||||||
* Represents an empty set.
|
* Represents an empty set.
|
||||||
*/
|
*/
|
||||||
class EmptySet extends Domain {
|
export class EmptySet extends Domain {
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.displayName = "∅"
|
this.displayName = "∅"
|
||||||
|
@ -172,12 +185,12 @@ class EmptySet extends Domain {
|
||||||
/**
|
/**
|
||||||
* Domain classes for ranges (e.g ]0;3[, [1;2[ ...)
|
* Domain classes for ranges (e.g ]0;3[, [1;2[ ...)
|
||||||
*/
|
*/
|
||||||
class Range extends Domain {
|
export class Range extends Domain {
|
||||||
constructor(begin, end, openBegin, openEnd) {
|
constructor(begin, end, openBegin, openEnd) {
|
||||||
super()
|
super()
|
||||||
if(typeof begin == 'number' || typeof begin == 'string') begin = new Expr.Expression(begin.toString())
|
if(typeof begin == 'number' || typeof begin == 'string') begin = new Expression(begin.toString())
|
||||||
this.begin = begin
|
this.begin = begin
|
||||||
if(typeof end == 'number' || typeof end == 'string') end = new Expr.Expression(end.toString())
|
if(typeof end == 'number' || typeof end == 'string') end = new Expression(end.toString())
|
||||||
this.end = end
|
this.end = end
|
||||||
this.openBegin = openBegin
|
this.openBegin = openBegin
|
||||||
this.openEnd = openEnd
|
this.openEnd = openEnd
|
||||||
|
@ -186,7 +199,7 @@ class Range extends Domain {
|
||||||
}
|
}
|
||||||
|
|
||||||
includes(x) {
|
includes(x) {
|
||||||
if(typeof x == 'string') x = Expr.executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
return ((this.openBegin && x > this.begin.execute()) || (!this.openBegin && x >= this.begin.execute())) &&
|
return ((this.openBegin && x > this.begin.execute()) || (!this.openBegin && x >= this.begin.execute())) &&
|
||||||
((this.openEnd && x < this.end.execute()) || (!this.openEnd && x <= this.end.execute()))
|
((this.openEnd && x < this.end.execute()) || (!this.openEnd && x <= this.end.execute()))
|
||||||
}
|
}
|
||||||
|
@ -214,9 +227,9 @@ class Range extends Domain {
|
||||||
}
|
}
|
||||||
|
|
||||||
static import(frm) {
|
static import(frm) {
|
||||||
var openBegin = frm.trim().charAt(0) == "]"
|
let openBegin = frm.trim().charAt(0) === "]"
|
||||||
var openEnd = frm.trim().charAt(frm.length -1) == "["
|
let openEnd = frm.trim().charAt(frm.length -1) === "["
|
||||||
var [begin, end] = frm.substr(1, frm.length-2).split(";")
|
let [begin, end] = frm.substr(1, frm.length-2).split(";")
|
||||||
return new Range(begin.trim(), end.trim(), openBegin, openEnd)
|
return new Range(begin.trim(), end.trim(), openBegin, openEnd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,17 +237,16 @@ class Range extends Domain {
|
||||||
/**
|
/**
|
||||||
* Domain classes for special domains (N, Z, ...)
|
* Domain classes for special domains (N, Z, ...)
|
||||||
*/
|
*/
|
||||||
class SpecialDomain extends Domain {
|
export class SpecialDomain extends Domain {
|
||||||
/**
|
/**
|
||||||
* @constructs SpecialDomain
|
* @constructs SpecialDomain
|
||||||
* @param {string} displayName
|
* @param {string} displayName
|
||||||
* @param {function} isValid - function returning true when number is in domain false when it isn't.
|
* @param {function} isValid - function returning true when number is in domain false when it isn't.
|
||||||
* @param {function} next - function provides the next positive value in the domain after the one given.
|
* @param {function} next - function provides the next positive value in the domain after the one given.
|
||||||
* @param {function} previous - function provides the previous positive value in the domain before the one given.
|
* @param {function} previous - function provides the previous positive value in the domain before the one given.
|
||||||
* @param {bool} moveSupported - Only true if next and previous functions are valid.
|
* @param {boolean} moveSupported - Only true if next and previous functions are valid.
|
||||||
* @param items
|
|
||||||
*/
|
*/
|
||||||
constructor(displayName, isValid, next = x => true, previous = x => true,
|
constructor(displayName, isValid, next = () => true, previous = () => true,
|
||||||
moveSupported = true) {
|
moveSupported = true) {
|
||||||
super()
|
super()
|
||||||
this.displayName = displayName
|
this.displayName = displayName
|
||||||
|
@ -245,17 +257,17 @@ class SpecialDomain extends Domain {
|
||||||
}
|
}
|
||||||
|
|
||||||
includes(x) {
|
includes(x) {
|
||||||
if(typeof x == 'string') x = Expr.executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
return this.isValid(x)
|
return this.isValid(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
next(x) {
|
next(x) {
|
||||||
if(typeof x == 'string') x = Expr.executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
return this.nextValue(x)
|
return this.nextValue(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
previous(x) {
|
previous(x) {
|
||||||
if(typeof x == 'string') x = Expr.executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
return this.prevValue(x)
|
return this.prevValue(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,14 +297,14 @@ class SpecialDomain extends Domain {
|
||||||
/**
|
/**
|
||||||
* Domain classes for sets (e.g {0;3}, {0;1;2;pi} ...)
|
* Domain classes for sets (e.g {0;3}, {0;1;2;pi} ...)
|
||||||
*/
|
*/
|
||||||
class DomainSet extends SpecialDomain {
|
export class DomainSet extends SpecialDomain {
|
||||||
constructor(values) {
|
constructor(values) {
|
||||||
super('', x => true, x => x, true)
|
super('', x => true, x => x, true)
|
||||||
var newVals = {}
|
let newVals = {}
|
||||||
this.executedValues = []
|
this.executedValues = []
|
||||||
for(var value of values) {
|
for(let value of values) {
|
||||||
var expr = new Expr.Expression(value.toString())
|
let expr = new Expression(value.toString())
|
||||||
var ex = expr.execute()
|
let ex = expr.execute()
|
||||||
newVals[ex] = expr
|
newVals[ex] = expr
|
||||||
this.executedValues.push(ex)
|
this.executedValues.push(ex)
|
||||||
}
|
}
|
||||||
|
@ -303,30 +315,30 @@ class DomainSet extends SpecialDomain {
|
||||||
}
|
}
|
||||||
|
|
||||||
includes(x) {
|
includes(x) {
|
||||||
if(typeof x == 'string') x = Expr.executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
for(var value of this.values)
|
for(let value of this.values)
|
||||||
if(x == value.execute()) return true
|
if(x === value.execute()) return true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
next(x) {
|
next(x) {
|
||||||
if(typeof x == 'string') x = Expr.executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
if(x < this.executedValues[0]) return this.executedValues[0]
|
if(x < this.executedValues[0]) return this.executedValues[0]
|
||||||
for(var i = 1; i < this.values.length; i++) {
|
for(let i = 1; i < this.values.length; i++) {
|
||||||
var prevValue = this.executedValues[i-1]
|
let prevValue = this.executedValues[i-1]
|
||||||
var value = this.executedValues[i]
|
let value = this.executedValues[i]
|
||||||
if(x >= prevValue && x < value) return value
|
if(x >= prevValue && x < value) return value
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
previous(x) {
|
previous(x) {
|
||||||
if(typeof x == 'string') x = Expr.executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
if(x > this.executedValues[this.executedValues.length-1])
|
if(x > this.executedValues[this.executedValues.length-1])
|
||||||
return this.executedValues[this.executedValues.length-1]
|
return this.executedValues[this.executedValues.length-1]
|
||||||
for(var i = 1; i < this.values.length; i++) {
|
for(let i = 1; i < this.values.length; i++) {
|
||||||
var prevValue = this.executedValues[i-1]
|
let prevValue = this.executedValues[i-1]
|
||||||
var value = this.executedValues[i]
|
let value = this.executedValues[i]
|
||||||
if(x > prevValue && x <= value) return prevValue
|
if(x > prevValue && x <= value) return prevValue
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
@ -339,56 +351,56 @@ class DomainSet extends SpecialDomain {
|
||||||
union(domain) {
|
union(domain) {
|
||||||
if(domain instanceof EmptySet) return this
|
if(domain instanceof EmptySet) return this
|
||||||
if(domain instanceof DomainSet) {
|
if(domain instanceof DomainSet) {
|
||||||
var newValues = []
|
let newValues = []
|
||||||
var values = this.values.concat(domain.values).filter(function(val){
|
let values = this.values.concat(domain.values).filter(function(val){
|
||||||
newValues.push(val.execute())
|
newValues.push(val.execute())
|
||||||
return newValues.indexOf(val.execute()) == newValues.length - 1
|
return newValues.indexOf(val.execute()) === newValues.length - 1
|
||||||
})
|
})
|
||||||
return new DomainSet(values)
|
return new DomainSet(values)
|
||||||
}
|
}
|
||||||
var notIncludedValues = []
|
let notIncludedValues = []
|
||||||
for(var value in this.values) {
|
for(let i = 0; i < this.values.length; i++) {
|
||||||
var value = this.executedValues[i]
|
let value = this.executedValues[i]
|
||||||
if(domain instanceof Range) {
|
if(domain instanceof Range) {
|
||||||
if(domain.begin.execute() == value && domain.openBegin) {
|
if(domain.begin.execute() === value && domain.openBegin) {
|
||||||
domain.openBegin = false
|
domain.openBegin = false
|
||||||
}
|
}
|
||||||
if(domain.end.execute() == value && domain.openEnd) {
|
if(domain.end.execute() === value && domain.openEnd) {
|
||||||
domain.openEnd = false
|
domain.openEnd = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!domain.includes(value))
|
if(!domain.includes(value))
|
||||||
notIncludedValues.push(this.values[i].toEditableString())
|
notIncludedValues.push(this.values[i].toEditableString())
|
||||||
}
|
}
|
||||||
if(notIncludedValues.length == 0) return domain
|
if(notIncludedValues.length === 0) return domain
|
||||||
return new UnionDomain(domain, new DomainSet(notIncludedValues))
|
return new UnionDomain(domain, new DomainSet(notIncludedValues))
|
||||||
}
|
}
|
||||||
|
|
||||||
intersection(domain) {
|
intersection(domain) {
|
||||||
if(domain instanceof EmptySet) return domain
|
if(domain instanceof EmptySet) return domain
|
||||||
if(domain instanceof DomainSet) {
|
if(domain instanceof DomainSet) {
|
||||||
var domValues = domain.values.map(expr => expr.execute())
|
let domValues = domain.values.map(expr => expr.execute())
|
||||||
this.values = this.values.filter(function(val){
|
this.values = this.values.filter(function(val){
|
||||||
return domValues.indexOf(val.execute()) >= 0
|
return domValues.indexOf(val.execute()) >= 0
|
||||||
})
|
})
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
var includedValues = []
|
let includedValues = []
|
||||||
for(var i in this.values) {
|
for(let i in this.values) {
|
||||||
var value = this.executedValues[i]
|
let value = this.executedValues[i]
|
||||||
if(domain instanceof Range) {
|
if(domain instanceof Range) {
|
||||||
if(domain.begin.execute() == value && !domain.openBegin) {
|
if(domain.begin.execute() === value && !domain.openBegin) {
|
||||||
domain.openBegin = false
|
domain.openBegin = false
|
||||||
}
|
}
|
||||||
if(domain.end.execute() == value && !domain.openEnd) {
|
if(domain.end.execute() === value && !domain.openEnd) {
|
||||||
domain.openEnd = false
|
domain.openEnd = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(domain.includes(value))
|
if(domain.includes(value))
|
||||||
includedValues.push(this.values[i].toEditableString())
|
includedValues.push(this.values[i].toEditableString())
|
||||||
}
|
}
|
||||||
if(includedValues.length == 0) return new EmptySet()
|
if(includedValues.length === 0) return new EmptySet()
|
||||||
if(includedValues.length == this.values.length) return this
|
if(includedValues.length === this.values.length) return this
|
||||||
return new IntersectionDomain(domain, new DomainSet(includedValues))
|
return new IntersectionDomain(domain, new DomainSet(includedValues))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,7 +412,7 @@ class DomainSet extends SpecialDomain {
|
||||||
/**
|
/**
|
||||||
* Domain representing the union between two domains.
|
* Domain representing the union between two domains.
|
||||||
*/
|
*/
|
||||||
class UnionDomain extends Domain {
|
export class UnionDomain extends Domain {
|
||||||
constructor(dom1, dom2) {
|
constructor(dom1, dom2) {
|
||||||
super()
|
super()
|
||||||
this.dom1 = dom1
|
this.dom1 = dom1
|
||||||
|
@ -435,10 +447,10 @@ class UnionDomain extends Domain {
|
||||||
}
|
}
|
||||||
|
|
||||||
static import(frm) {
|
static import(frm) {
|
||||||
var domains = frm.trim().split("∪")
|
let domains = frm.trim().split("∪")
|
||||||
if(domains.length == 1) domains = frm.trim().split("U") // Fallback
|
if(domains.length === 1) domains = frm.trim().split("U") // Fallback
|
||||||
var dom1 = parseDomain(domains.pop())
|
let dom2 = parseDomain(domains.pop())
|
||||||
var dom2 = parseDomain(domains.join('∪'))
|
let dom1 = parseDomain(domains.join('∪'))
|
||||||
return dom1.union(dom2)
|
return dom1.union(dom2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -446,7 +458,7 @@ class UnionDomain extends Domain {
|
||||||
/**
|
/**
|
||||||
* Domain representing the intersection between two domains.
|
* Domain representing the intersection between two domains.
|
||||||
*/
|
*/
|
||||||
class IntersectionDomain extends Domain {
|
export class IntersectionDomain extends Domain {
|
||||||
constructor(dom1, dom2) {
|
constructor(dom1, dom2) {
|
||||||
super()
|
super()
|
||||||
this.dom1 = dom1
|
this.dom1 = dom1
|
||||||
|
@ -481,9 +493,9 @@ class IntersectionDomain extends Domain {
|
||||||
}
|
}
|
||||||
|
|
||||||
static import(frm) {
|
static import(frm) {
|
||||||
var domains = frm.trim().split("∩")
|
let domains = frm.trim().split("∩")
|
||||||
var dom1 = parseDomain(domains.pop())
|
let dom1 = parseDomain(domains.pop())
|
||||||
var dom2 = parseDomain(domains.join('∩'))
|
let dom2 = parseDomain(domains.join('∩'))
|
||||||
return dom1.intersection(dom2)
|
return dom1.intersection(dom2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -491,7 +503,7 @@ class IntersectionDomain extends Domain {
|
||||||
/**
|
/**
|
||||||
* Domain representing the minus between two domains.
|
* Domain representing the minus between two domains.
|
||||||
*/
|
*/
|
||||||
class MinusDomain extends Domain {
|
export class MinusDomain extends Domain {
|
||||||
constructor(dom1, dom2) {
|
constructor(dom1, dom2) {
|
||||||
super()
|
super()
|
||||||
this.dom1 = dom1
|
this.dom1 = dom1
|
||||||
|
@ -509,10 +521,10 @@ class MinusDomain extends Domain {
|
||||||
}
|
}
|
||||||
|
|
||||||
static import(frm) {
|
static import(frm) {
|
||||||
var domains = frm.trim().split("∖")
|
let domains = frm.trim().split("∖")
|
||||||
if(domains.length == 1) domains = frm.trim().split("\\") // Fallback
|
if(domains.length === 1) domains = frm.trim().split("\\") // Fallback
|
||||||
var dom1 = parseDomain(domains.shift())
|
let dom1 = parseDomain(domains.shift())
|
||||||
var dom2 = parseDomain(domains.join('∪'))
|
let dom2 = parseDomain(domains.join('∪'))
|
||||||
return new MinusDomain(dom1, dom2)
|
return new MinusDomain(dom1, dom2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -536,53 +548,53 @@ Domain.RPE.latexMarkup = "\\mathbb{R}^{+*}"
|
||||||
Domain.RME = new Range(-Infinity,0,true,true)
|
Domain.RME = new Range(-Infinity,0,true,true)
|
||||||
Domain.RME.displayName = "ℝ⁻*"
|
Domain.RME.displayName = "ℝ⁻*"
|
||||||
Domain.RME.latexMarkup = "\\mathbb{R}^{+*}"
|
Domain.RME.latexMarkup = "\\mathbb{R}^{+*}"
|
||||||
Domain.N = new SpecialDomain('ℕ', x => x%1==0 && x >= 0,
|
Domain.N = new SpecialDomain('ℕ', x => x%1===0 && x >= 0,
|
||||||
x => Math.max(Math.floor(x)+1, 0),
|
x => Math.max(Math.floor(x)+1, 0),
|
||||||
x => Math.max(Math.ceil(x)-1, 0))
|
x => Math.max(Math.ceil(x)-1, 0))
|
||||||
Domain.N.latexMarkup = "\\mathbb{N}"
|
Domain.N.latexMarkup = "\\mathbb{N}"
|
||||||
Domain.NE = new SpecialDomain('ℕ*', x => x%1==0 && x > 0,
|
Domain.NE = new SpecialDomain('ℕ*', x => x%1===0 && x > 0,
|
||||||
x => Math.max(Math.floor(x)+1, 1),
|
x => Math.max(Math.floor(x)+1, 1),
|
||||||
x => Math.max(Math.ceil(x)-1, 1))
|
x => Math.max(Math.ceil(x)-1, 1))
|
||||||
Domain.NE.latexMarkup = "\\mathbb{N}^{*}"
|
Domain.NE.latexMarkup = "\\mathbb{N}^{*}"
|
||||||
Domain.Z = new SpecialDomain('ℤ', x => x%1==0, x => Math.floor(x)+1, x => Math.ceil(x)-1)
|
Domain.Z = new SpecialDomain('ℤ', x => x%1===0, x => Math.floor(x)+1, x => Math.ceil(x)-1)
|
||||||
Domain.Z.latexMarkup = "\\mathbb{Z}"
|
Domain.Z.latexMarkup = "\\mathbb{Z}"
|
||||||
Domain.ZE = new SpecialDomain('ℤ*', x => x%1==0 && x != 0,
|
Domain.ZE = new SpecialDomain('ℤ*', x => x%1===0 && x !== 0,
|
||||||
x => Math.floor(x)+1 == 0 ? Math.floor(x)+2 : Math.floor(x)+1,
|
x => Math.floor(x)+1 === 0 ? Math.floor(x)+2 : Math.floor(x)+1,
|
||||||
x => Math.ceil(x)-1 == 0 ? Math.ceil(x)-2 : Math.ceil(x)-1)
|
x => Math.ceil(x)-1 === 0 ? Math.ceil(x)-2 : Math.ceil(x)-1)
|
||||||
Domain.ZE.latexMarkup = "\\mathbb{Z}^{*}"
|
Domain.ZE.latexMarkup = "\\mathbb{Z}^{*}"
|
||||||
Domain.ZM = new SpecialDomain('ℤ⁻', x => x%1==0 && x <= 0,
|
Domain.ZM = new SpecialDomain('ℤ⁻', x => x%1===0 && x <= 0,
|
||||||
x => Math.min(Math.floor(x)+1, 0),
|
x => Math.min(Math.floor(x)+1, 0),
|
||||||
x => Math.min(Math.ceil(x)-1, 0))
|
x => Math.min(Math.ceil(x)-1, 0))
|
||||||
Domain.ZM.latexMarkup = "\\mathbb{Z}^{-}"
|
Domain.ZM.latexMarkup = "\\mathbb{Z}^{-}"
|
||||||
Domain.ZME = new SpecialDomain('ℤ⁻*', x => x%1==0 && x < 0,
|
Domain.ZME = new SpecialDomain('ℤ⁻*', x => x%1===0 && x < 0,
|
||||||
x => Math.min(Math.floor(x)+1, -1),
|
x => Math.min(Math.floor(x)+1, -1),
|
||||||
x => Math.min(Math.ceil(x)-1, -1))
|
x => Math.min(Math.ceil(x)-1, -1))
|
||||||
Domain.ZME.latexMarkup = "\\mathbb{Z}^{-*}"
|
Domain.ZME.latexMarkup = "\\mathbb{Z}^{-*}"
|
||||||
Domain.NLog = new SpecialDomain('ℕˡᵒᵍ',
|
Domain.NLog = new SpecialDomain('ℕˡᵒᵍ',
|
||||||
x => x/Math.pow(10, x.toString().length-1) % 1 == 0 && x > 0,
|
x => x/Math.pow(10, x.toString().length-1) % 1 === 0 && x > 0,
|
||||||
function(x) {
|
function(x) {
|
||||||
var x10pow = Math.pow(10, x.toString().length-1)
|
let x10pow = Math.pow(10, x.toString().length-1)
|
||||||
return Math.max(1, (Math.floor(x/x10pow)+1)*x10pow)
|
return Math.max(1, (Math.floor(x/x10pow)+1)*x10pow)
|
||||||
},
|
},
|
||||||
function(x) {
|
function(x) {
|
||||||
var x10pow = Math.pow(10, x.toString().length-1)
|
let x10pow = Math.pow(10, x.toString().length-1)
|
||||||
return Math.max(1, (Math.ceil(x/x10pow)-1)*x10pow)
|
return Math.max(1, (Math.ceil(x/x10pow)-1)*x10pow)
|
||||||
})
|
})
|
||||||
Domain.NLog.latexMarkup = "\\mathbb{N}^{log}"
|
Domain.NLog.latexMarkup = "\\mathbb{N}^{log}"
|
||||||
|
|
||||||
var refedDomains = []
|
let refedDomains = []
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a domain, that can use parenthesises.
|
* Parses a domain, that can use parentheses.
|
||||||
* e.g (N ∪ [-1;0[) ∩ (Z \ {0;3})
|
* e.g (N ∪ [-1;0[) ∩ (Z \ {0;3})
|
||||||
* @param {string} domain - string of the domain to be parsed.
|
* @param {string} domain - string of the domain to be parsed.
|
||||||
* @returns {Domain} Parsed domain.
|
* @returns {Domain} Parsed domain.
|
||||||
*/
|
*/
|
||||||
function parseDomain(domain) {
|
export function parseDomain(domain) {
|
||||||
if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain)
|
if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain)
|
||||||
var domStr
|
let domStr
|
||||||
while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) {
|
while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) {
|
||||||
var dom = parseDomainSimple(domStr[1].trim());
|
let dom = parseDomainSimple(domStr[1].trim());
|
||||||
domain = domain.replace(domStr[0], 'D' + refedDomains.length)
|
domain = domain.replace(domStr[0], 'D' + refedDomains.length)
|
||||||
refedDomains.push(dom)
|
refedDomains.push(dom)
|
||||||
}
|
}
|
||||||
|
@ -590,20 +602,20 @@ function parseDomain(domain) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a domain, without parenthesises.
|
* Parses a domain, without parentheses.
|
||||||
* e.g N ∪ [-1;0[, Z \ {0;3}, N+*...
|
* e.g N ∪ [-1;0[, Z \ {0;3}, N+*...
|
||||||
* @param {string} domain - string of the domain to be parsed.
|
* @param {string} domain - string of the domain to be parsed.
|
||||||
* @returns {Domain} Parsed domain.
|
* @returns {Domain} Parsed domain.
|
||||||
*/
|
*/
|
||||||
function parseDomainSimple(domain) {
|
export function parseDomainSimple(domain) {
|
||||||
domain = domain.trim()
|
domain = domain.trim()
|
||||||
if(domain.includes("U") || domain.includes("∪")) return UnionDomain.import(domain)
|
if(domain.includes("U") || domain.includes("∪")) return UnionDomain.import(domain)
|
||||||
if(domain.includes("∩")) return IntersectionDomain.import(domain)
|
if(domain.includes("∩")) return IntersectionDomain.import(domain)
|
||||||
if(domain.includes("∖") || domain.includes("\\")) return MinusDomain.import(domain)
|
if(domain.includes("∖") || domain.includes("\\")) return MinusDomain.import(domain)
|
||||||
if(domain.charAt(0) == "{" && domain.charAt(domain.length -1) == "}") return DomainSet.import(domain)
|
if(domain.charAt(0) === "{" && domain.charAt(domain.length -1) === "}") return DomainSet.import(domain)
|
||||||
if(domain.includes("]") || domain.includes("[")) return Range.import(domain)
|
if(domain.includes("]") || domain.includes("[")) return Range.import(domain)
|
||||||
if(["R", "ℝ", "N", "ℕ", "Z", "ℤ"].some(str => domain.toUpperCase().includes(str)))
|
if(["R", "ℝ", "N", "ℕ", "Z", "ℤ"].some(str => domain.toUpperCase().includes(str)))
|
||||||
return Domain.import(domain)
|
return Domain.import(domain)
|
||||||
if(domain[0] == 'D') return refedDomains[parseInt(domain.substr(1))]
|
if(domain[0] === 'D') return refedDomains[parseInt(domain.substr(1))]
|
||||||
return new EmptySet()
|
return new EmptySet()
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,21 +16,25 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
|
||||||
|
|
||||||
.import "common.js" as C
|
import Latex from "latex.mjs"
|
||||||
.import "latex.js" as Latex
|
import * as Utils from "../utils.mjs"
|
||||||
.import "../utils.js" as Utils
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents any kind of x-based or non variable based expression.
|
* Represents any kind of x-based or non variable based expression.
|
||||||
*/
|
*/
|
||||||
class Expression {
|
export class Expression {
|
||||||
constructor(expr) {
|
constructor(expr) {
|
||||||
this.expr = expr
|
if(!Modules.ExprParser)
|
||||||
this.calc = C.parser.parse(expr).simplify()
|
throw new Error('Expression parser not initialized.')
|
||||||
|
if(!Modules.Objects)
|
||||||
|
throw new Error('Objects API not initialized.')
|
||||||
|
this.expr = Utils.exponentsToExpression(expr)
|
||||||
|
this.calc = Modules.ExprParser.parse(this.expr).simplify()
|
||||||
this.cached = this.isConstant()
|
this.cached = this.isConstant()
|
||||||
this.cachedValue = this.cached && this.allRequirementsFullfilled() ? this.calc.evaluate(C.currentObjectsByName) : null
|
this.cachedValue = null
|
||||||
|
if(this.cached && this.allRequirementsFullfilled())
|
||||||
|
this.cachedValue = this.calc.evaluate(Modules.Objects.currentObjectsByName)
|
||||||
this.latexMarkup = Latex.expression(this.calc.tokens)
|
this.latexMarkup = Latex.expression(this.calc.tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,37 +44,37 @@ class Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
requiredObjects() {
|
requiredObjects() {
|
||||||
return this.calc.variables().filter(objName => objName != "x" && objName != "n")
|
return this.calc.variables().filter(objName => objName !== "x" && objName !== "n")
|
||||||
}
|
}
|
||||||
|
|
||||||
allRequirementsFullfilled() {
|
allRequirementsFullfilled() {
|
||||||
return this.requiredObjects().every(objName => objName in C.currentObjectsByName)
|
return this.requiredObjects().every(objName => objName in Modules.Objects.currentObjectsByName)
|
||||||
}
|
}
|
||||||
|
|
||||||
undefinedVariables() {
|
undefinedVariables() {
|
||||||
return this.requiredObjects().filter(objName => !(objName in C.currentObjectsByName))
|
return this.requiredObjects().filter(objName => !(objName in Modules.Objects.currentObjectsByName))
|
||||||
}
|
}
|
||||||
|
|
||||||
recache() {
|
recache() {
|
||||||
if(this.cached)
|
if(this.cached)
|
||||||
this.cachedValue = this.calc.evaluate(C.currentObjectsByName)
|
this.cachedValue = this.calc.evaluate(Modules.Objects.currentObjectsByName)
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x = 1) {
|
execute(x = 1) {
|
||||||
if(this.cached) {
|
if(this.cached) {
|
||||||
if(this.cachedValue == null)
|
if(this.cachedValue == null)
|
||||||
this.cachedValue = this.calc.evaluate(C.currentObjectsByName)
|
this.cachedValue = this.calc.evaluate(Modules.Objects.currentObjectsByName)
|
||||||
return this.cachedValue
|
return this.cachedValue
|
||||||
}
|
}
|
||||||
C.currentVars = Object.assign({'x': x}, C.currentObjectsByName)
|
Modules.ExprParser.currentVars = Object.assign({'x': x}, Modules.Objects.currentObjectsByName)
|
||||||
return this.calc.evaluate(C.currentVars)
|
return this.calc.evaluate(Modules.ExprParser.currentVars)
|
||||||
}
|
}
|
||||||
|
|
||||||
simplify(x) {
|
simplify(x) {
|
||||||
var expr = this.calc.substitute('x', x).simplify()
|
let expr = this.calc.substitute('x', x).simplify()
|
||||||
if(expr.evaluate() == 0) return '0'
|
if(expr.evaluate() === 0) return '0'
|
||||||
var str = Utils.makeExpressionReadable(expr.toString());
|
let str = Utils.makeExpressionReadable(expr.toString());
|
||||||
if(str != undefined && str.match(/^\d*\.\d+$/)) {
|
if(str !== undefined && str.match(/^\d*\.\d+$/)) {
|
||||||
if(str.split('.')[1].split('0').length > 7) {
|
if(str.split('.')[1].split('0').length > 7) {
|
||||||
// Likely rounding error
|
// Likely rounding error
|
||||||
str = parseFloat(str.substring(0, str.length-1)).toString();
|
str = parseFloat(str.substring(0, str.length-1)).toString();
|
||||||
|
@ -89,11 +93,11 @@ class Expression {
|
||||||
|
|
||||||
toString(forceSign=false) {
|
toString(forceSign=false) {
|
||||||
let str = Utils.makeExpressionReadable(this.calc.toString())
|
let str = Utils.makeExpressionReadable(this.calc.toString())
|
||||||
if(str[0] != '-' && forceSign) str = '+' + str
|
if(str[0] !== '-' && forceSign) str = '+' + str
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeExpression(expr){
|
export function executeExpression(expr){
|
||||||
return (new Expression(expr.toString())).execute()
|
return (new Expression(expr.toString())).execute()
|
||||||
}
|
}
|
|
@ -1,276 +0,0 @@
|
||||||
/**
|
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
|
||||||
* Copyright (C) 2023 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":
|
|
||||||
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}';
|
|
||||||
break;
|
|
||||||
case "integral":
|
|
||||||
return '\\int\\limits_{' + args[0] + '}^{' + args[1] + '}' + args[2].substr(1, args[2].length-2) + ' d' + args[3].substr(1, args[3].length-2);
|
|
||||||
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"]
|
|
||||||
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 { Module } from '../modules.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"]
|
||||||
|
|
||||||
|
|
||||||
|
class LatexAPI extends Module {
|
||||||
|
constructor() {
|
||||||
|
super('Latex', [
|
||||||
|
/** @type {ExprParserAPI} */
|
||||||
|
Modules.ExprParser
|
||||||
|
])
|
||||||
|
/**
|
||||||
|
* true if latex has been enabled by the user, false otherwise.
|
||||||
|
*/
|
||||||
|
this.enabled = Helper.getSettingBool("enable_latex")
|
||||||
|
/**
|
||||||
|
* Mirror method for Python object.
|
||||||
|
* @type {function(string, number, string): string}.
|
||||||
|
*/
|
||||||
|
this.render = Latex.render
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 Modules.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(Modules.ExprParser.Internals.escapeValue).join(', ') + ']');
|
||||||
|
} else {
|
||||||
|
nstack.push(Modules.ExprParser.Internals.escapeValue(item.value));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Modules.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 Modules.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 Modules.ExprParser.Internals.IVAR:
|
||||||
|
case Modules.ExprParser.Internals.IVARNAME:
|
||||||
|
nstack.push(this.variable(item.value.toString()));
|
||||||
|
break;
|
||||||
|
case Modules.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 Modules.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 Modules.ExprParser.Internals.IFUNDEF:
|
||||||
|
nstack.push(this.par(n1 + '(' + args.join(', ') + ') = ' + n2));
|
||||||
|
break;
|
||||||
|
case Modules.ExprParser.Internals.IMEMBER:
|
||||||
|
n1 = nstack.pop();
|
||||||
|
nstack.push(n1 + '.' + item.value);
|
||||||
|
break;
|
||||||
|
case Modules.ExprParser.Internals.IARRAY:
|
||||||
|
argCount = item.value;
|
||||||
|
args = [];
|
||||||
|
while (argCount-- > 0) {
|
||||||
|
args.unshift(nstack.pop());
|
||||||
|
}
|
||||||
|
nstack.push('[' + args.join(', ') + ']');
|
||||||
|
break;
|
||||||
|
case Modules.ExprParser.Internals.IEXPR:
|
||||||
|
nstack.push('(' + this.expression(item.value) + ')');
|
||||||
|
break;
|
||||||
|
case Modules.ExprParser.Internals.IENDSTATEMENT:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new EvalError('invalid Expression');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nstack.length > 1) {
|
||||||
|
nstack = [ nstack.join(';') ]
|
||||||
|
}
|
||||||
|
return String(nstack[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {LatexAPI} */
|
||||||
|
Modules.Latex = Modules.Latex || new LatexAPI()
|
||||||
|
|
||||||
|
export default Modules.Latex
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,18 +16,14 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import * as Expr from "expression.mjs"
|
||||||
|
import * as Utils from "../utils.mjs"
|
||||||
.import "common.js" as C
|
import Latex from "../math/latex.mjs"
|
||||||
.import "expression.js" as Expr
|
|
||||||
.import "../utils.js" as Utils
|
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents mathematical object for sequences.
|
* Represents mathematical object for sequences.
|
||||||
*/
|
*/
|
||||||
class Sequence extends Expr.Expression {
|
export class Sequence extends Expr.Expression {
|
||||||
constructor(name, baseValues = {}, valuePlus = 1, expr = "") {
|
constructor(name, baseValues = {}, valuePlus = 1, expr = "") {
|
||||||
// u[n+valuePlus] = expr
|
// u[n+valuePlus] = expr
|
||||||
super(expr)
|
super(expr)
|
||||||
|
@ -35,9 +31,9 @@ class Sequence extends Expr.Expression {
|
||||||
this.baseValues = baseValues
|
this.baseValues = baseValues
|
||||||
this.calcValues = Object.assign({}, baseValues)
|
this.calcValues = Object.assign({}, baseValues)
|
||||||
this.latexValues = Object.assign({}, baseValues)
|
this.latexValues = Object.assign({}, baseValues)
|
||||||
for(var n in this.calcValues)
|
for(let n in this.calcValues)
|
||||||
if(['string', 'number'].includes(typeof this.calcValues[n])) {
|
if(['string', 'number'].includes(typeof this.calcValues[n])) {
|
||||||
let parsed = C.parser.parse(this.calcValues[n].toString()).simplify()
|
let parsed = Modules.ExprParser.parse(this.calcValues[n].toString()).simplify()
|
||||||
this.latexValues[n] = Latex.expression(parsed.tokens)
|
this.latexValues[n] = Latex.expression(parsed.tokens)
|
||||||
this.calcValues[n] = parsed.evaluate()
|
this.calcValues[n] = parsed.evaluate()
|
||||||
}
|
}
|
||||||
|
@ -45,7 +41,7 @@ class Sequence extends Expr.Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
isConstant() {
|
isConstant() {
|
||||||
return this.expr.indexOf("n") == -1
|
return this.expr.indexOf("n") === -1
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(n = 1) {
|
execute(n = 1) {
|
||||||
|
@ -63,21 +59,25 @@ class Sequence extends Expr.Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
cache(n = 1) {
|
cache(n = 1) {
|
||||||
var str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString())
|
let str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString())
|
||||||
var expr = C.parser.parse(str).simplify()
|
let expr = Modules.ExprParser.parse(str).simplify()
|
||||||
C.currentVars = Object.assign(
|
// Cache values required for this one.
|
||||||
|
if(!this.calcValues[n-this.valuePlus] && n-this.valuePlus > 0)
|
||||||
|
this.cache(n-this.valuePlus)
|
||||||
|
// Setting current variables
|
||||||
|
Modules.ExprParser.currentVars = Object.assign(
|
||||||
{'n': n-this.valuePlus}, // Just in case, add n (for custom functions)
|
{'n': n-this.valuePlus}, // Just in case, add n (for custom functions)
|
||||||
C.currentObjectsByName
|
Modules.Objects.currentObjectsByName,
|
||||||
|
{[this.name]: this.calcValues}
|
||||||
)
|
)
|
||||||
C.currentVars[this.name] = this.calcValues
|
this.calcValues[n] = expr.evaluate(Modules.ExprParser.currentVars)
|
||||||
this.calcValues[n] = expr.evaluate(C.currentVars)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toString(forceSign=false) {
|
toString(forceSign=false) {
|
||||||
var str = Utils.makeExpressionReadable(this.calc.toString())
|
let str = Utils.makeExpressionReadable(this.calc.toString())
|
||||||
if(str[0] != '-' && forceSign) str = '+' + str
|
if(str[0] !== '-' && forceSign) str = '+' + str
|
||||||
var subtxt = this.valuePlus == 0 ? 'ₙ' : Utils.textsub('n+' + this.valuePlus)
|
let subtxt = this.valuePlus === 0 ? 'ₙ' : Utils.textsub('n+' + this.valuePlus)
|
||||||
var ret = `${this.name}${subtxt} = ${str}${this.baseValues.length == 0 ? '' : "\n"}`
|
let ret = `${this.name}${subtxt} = ${str}${this.baseValues.length === 0 ? '' : "\n"}`
|
||||||
ret += Object.keys(this.baseValues).map(
|
ret += Object.keys(this.baseValues).map(
|
||||||
n => `${this.name}${Utils.textsub(n)} = ${this.baseValues[n]}`
|
n => `${this.name}${Utils.textsub(n)} = ${this.baseValues[n]}`
|
||||||
).join('; ')
|
).join('; ')
|
||||||
|
@ -85,10 +85,10 @@ class Sequence extends Expr.Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
toLatexString(forceSign=false) {
|
toLatexString(forceSign=false) {
|
||||||
var str = this.latexMarkup
|
let str = this.latexMarkup
|
||||||
if(str[0] != '-' && forceSign) str = '+' + str
|
if(str[0] !== '-' && forceSign) str = '+' + str
|
||||||
var subtxt = '_{n' + (this.valuePlus == 0 ? '' : '+' + this.valuePlus) + '}'
|
let subtxt = '_{n' + (this.valuePlus === 0 ? '' : '+' + this.valuePlus) + '}'
|
||||||
var ret = `\\begin{array}{l}${Latex.variable(this.name)}${subtxt} = ${str}${this.latexValues.length == 0 ? '' : "\n"}\\\\`
|
let ret = `\\begin{array}{l}${Latex.variable(this.name)}${subtxt} = ${str}${this.latexValues.length === 0 ? '' : "\n"}\\\\`
|
||||||
ret += Object.keys(this.latexValues).map(
|
ret += Object.keys(this.latexValues).map(
|
||||||
n => `${this.name}_{${n}} = ${this.latexValues[n]}`
|
n => `${this.name}_{${n}} = ${this.latexValues[n]}`
|
||||||
).join('; ') + "\\end{array}"
|
).join('; ') + "\\end{array}"
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,27 +16,25 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
|
||||||
|
|
||||||
.import "common.js" as C
|
import * as Expr from "math/expression.mjs"
|
||||||
.import "point.js" as P
|
import * as Seq from "math/sequence.mjs"
|
||||||
.import "text.js" as T
|
import * as Dom from "math/domain.mjs"
|
||||||
.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)
|
export const Expression = Expr.Expression
|
||||||
C.registerObject(F.Function)
|
export const executeExpression = Expr.executeExpression
|
||||||
C.registerObject(GB.GainBode)
|
export const Sequence = Seq.Sequence
|
||||||
C.registerObject(PB.PhaseBode)
|
|
||||||
C.registerObject(SGB.SommeGainsBode)
|
// Domains
|
||||||
C.registerObject(SPB.SommePhasesBode)
|
export const Domain = Dom.Domain
|
||||||
C.registerObject(X.XCursor)
|
export const EmptySet = Dom.EmptySet
|
||||||
C.registerObject(S.Sequence)
|
export const Range = Dom.Range
|
||||||
C.registerObject(R.RepartitionFunction)
|
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
|
||||||
|
|
||||||
|
export const parseDomain = Dom.parseDomain
|
||||||
|
export const parseDomainSimple = Dom.parseDomainSimple
|
|
@ -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 Module {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} name - Name of the API
|
||||||
|
* @param {(Module|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 {(Module|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,96 +0,0 @@
|
||||||
/**
|
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
|
||||||
* Copyright (C) 2023 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
|
@ -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 { Module } from './modules.mjs'
|
||||||
|
|
||||||
|
class ObjectsAPI extends Module {
|
||||||
|
|
||||||
|
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} */
|
||||||
|
Modules.Objects = Modules.Objects || new ObjectsAPI()
|
||||||
|
|
||||||
|
export default Modules.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(Modules.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)
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,42 +16,72 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import { getRandomColor, textsub } from "../utils.mjs"
|
||||||
|
import Objects from "../objects.mjs"
|
||||||
.import "../utils.js" as Utils
|
import Latex from "../math/latex.mjs"
|
||||||
.import "../objects.js" as Objects
|
import {Module} from "../modules.mjs"
|
||||||
.import "../math/latex.js" as Latex
|
import {ensureTypeSafety, serializesByPropertyType} from "../parameters.mjs"
|
||||||
.import "../parameters.js" as P
|
|
||||||
.import "../math/common.js" as C
|
|
||||||
|
|
||||||
// This file contains the default data to be imported from all other objects
|
// This file contains the default data to be imported from all other objects
|
||||||
|
|
||||||
/**
|
class ObjectsCommonAPI extends Module {
|
||||||
* Creates a new name for an object, based on the \c allowedLetters.
|
|
||||||
* If variables with each of the allowedLetters is created, a subscript
|
constructor() {
|
||||||
* number is added to the name.
|
super('ObjectsCommon', [
|
||||||
* @param {string} prefix - Prefix to the name.
|
Modules.Objects,
|
||||||
* @return {string} New unused name for a new object.
|
Modules.ExprParser,
|
||||||
*/
|
Modules.Latex
|
||||||
function getNewName(allowedLetters, prefix='') {
|
])
|
||||||
// Allows to get a new name, based on the allowed letters,
|
}
|
||||||
// as well as adding a sub number when needs be.
|
|
||||||
var newid = 0
|
/**
|
||||||
var ret
|
* Creates a new name for an object, based on the allowedLetters.
|
||||||
do {
|
* If variables with each of the allowedLetters is created, a subscript
|
||||||
var letter = allowedLetters[newid % allowedLetters.length]
|
* number is added to the name.
|
||||||
var num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length)
|
* @param {string} allowedLetters
|
||||||
ret = prefix + letter + (num > 0 ? Utils.textsub(num-1) : '')
|
* @param {string} prefix - Prefix to the name.
|
||||||
newid += 1
|
* @return {string} New unused name for a new object.
|
||||||
} while(ret in Objects.currentObjectsByName)
|
*/
|
||||||
return ret
|
getNewName(allowedLetters, prefix='') {
|
||||||
|
// Allows to get a new name, based on the allowed letters,
|
||||||
|
// as well as adding a sub number when needs be.
|
||||||
|
let newid = 0
|
||||||
|
let ret
|
||||||
|
do {
|
||||||
|
let letter = allowedLetters[newid % allowedLetters.length]
|
||||||
|
let num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length)
|
||||||
|
ret = prefix + letter + (num > 0 ? textsub(num-1) : '')
|
||||||
|
newid += 1
|
||||||
|
} while(ret in Objects.currentObjectsByName)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the object 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} */
|
||||||
|
Modules.ObjectsCommon = Modules.ObjectsCommon || new ObjectsCommonAPI()
|
||||||
|
|
||||||
|
export const API = Modules.ObjectsCommon
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to extend for every type of object that
|
* Class to extend for every type of object that
|
||||||
* can be drawn on the canvas.
|
* can be drawn on the canvas.
|
||||||
*/
|
*/
|
||||||
class DrawableObject {
|
export class DrawableObject {
|
||||||
/**
|
/**
|
||||||
* Base name of the object. Needs to be constant over time.
|
* Base name of the object. Needs to be constant over time.
|
||||||
* @return {string} Type of the object.
|
* @return {string} Type of the object.
|
||||||
|
@ -73,7 +103,7 @@ class DrawableObject {
|
||||||
/**
|
/**
|
||||||
* True if this object can be created by the user, false if it can only
|
* True if this object can be created by the user, false if it can only
|
||||||
* be instantiated by other objects
|
* be instantiated by other objects
|
||||||
* @return {bool}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
static createable() {return true}
|
static createable() {return true}
|
||||||
|
|
||||||
|
@ -89,30 +119,44 @@ class DrawableObject {
|
||||||
* If the property is to be translated, the key should be passed
|
* If the property is to be translated, the key should be passed
|
||||||
* through the QT_TRANSLATE_NOOP macro in that form:
|
* through the QT_TRANSLATE_NOOP macro in that form:
|
||||||
* [QT_TRANSLATE_NOOP('prop','key')]
|
* [QT_TRANSLATE_NOOP('prop','key')]
|
||||||
* Enums that are translated should be indexed in parameters.js and
|
* Enums that are translated should be indexed in parameters.mjs and
|
||||||
* then be linked directly here.
|
* then be linked directly here.
|
||||||
*
|
*
|
||||||
* @return {Dictionary}
|
* @return {Object.<string,string|Enum|List|ObjectType|Dictionary>}
|
||||||
*/
|
*/
|
||||||
static properties() {return {}}
|
static properties() {return {}}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if this object can be executed, so that an y value might be computed
|
* True if this object can be executed, so that an y value might be computed
|
||||||
* for an x value. Only ExecutableObjects have that property set to true.
|
* for an x value. Only ExecutableObjects have that property set to true.
|
||||||
* @return {bool}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
static executable() {return false}
|
static executable() {return false}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports the object from its serialized form.
|
||||||
|
* @return {DrawableObject}
|
||||||
|
*/
|
||||||
|
static import(name, visible, color, labelContent, ...args) {
|
||||||
|
let importedArgs = [name.toString(), visible === true, color.toString(), labelContent]
|
||||||
|
console.log('Importing', this.type(), name, args)
|
||||||
|
for(let [name, propType] of Object.entries(this.properties()))
|
||||||
|
if(!name.startsWith('comment')) {
|
||||||
|
importedArgs.push(ensureTypeSafety(propType, args[importedArgs.length-4]))
|
||||||
|
}
|
||||||
|
return new this(...importedArgs)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base constructor for the object.
|
* Base constructor for the object.
|
||||||
* @param {string} name - Name of the object
|
* @param {string} name - Name of the object
|
||||||
* @param {bool} visible - true if the object is visible, false otherwise.
|
* @param {boolean} visible - true if the object is visible, false otherwise.
|
||||||
* @param {color} color - Color of the object (can be string or QColor)
|
* @param {color|string} color - Color of the object (can be string or QColor)
|
||||||
* @param {Enum} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label.
|
* @param {Enum} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label.
|
||||||
* @constructor()
|
* @constructor
|
||||||
*/
|
*/
|
||||||
constructor(name, visible = true, color = null, labelContent = 'name + value') {
|
constructor(name, visible = true, color = null, labelContent = 'name + value') {
|
||||||
if(color == null) color = Utils.getRandomColor()
|
if(color == null) color = getRandomColor()
|
||||||
this.type = this.constructor.type()
|
this.type = this.constructor.type()
|
||||||
this.name = name
|
this.name = name
|
||||||
this.visible = visible
|
this.visible = visible
|
||||||
|
@ -121,15 +165,18 @@ class DrawableObject {
|
||||||
this.requiredBy = []
|
this.requiredBy = []
|
||||||
this.requires = []
|
this.requires = []
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serilizes the object in an array that can be JSON serialized.
|
* Serializes the object in an array that can be JSON serialized.
|
||||||
* These parameters will be re-entered in the constructor when restored.
|
* These parameters will be re-entered in the constructor when restored.
|
||||||
* @return {array}
|
* @return {array}
|
||||||
*/
|
*/
|
||||||
export() {
|
export() {
|
||||||
// Should return what will be inputed as arguments when a file is loaded (serializable form)
|
let exportList = [this.name, this.visible, this.color.toString(), this.labelContent]
|
||||||
return [this.name, this.visible, this.color.toString(), this.labelContent]
|
for(let [name, propType] of Object.entries(this.constructor.properties()))
|
||||||
|
if(!name.startsWith('comment'))
|
||||||
|
exportList.push(serializesByPropertyType(propType, this[name]))
|
||||||
|
return exportList
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,7 +192,7 @@ class DrawableObject {
|
||||||
* Latex markuped version of the readable string.
|
* Latex markuped version of the readable string.
|
||||||
* Every non latin character should be passed as latex symbols and formulas
|
* Every non latin character should be passed as latex symbols and formulas
|
||||||
* should be in latex form.
|
* should be in latex form.
|
||||||
* See ../latex.js for helper methods.
|
* See ../latex.mjs for helper methods.
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
getLatexString() {
|
getLatexString() {
|
||||||
|
@ -153,7 +200,7 @@ class DrawableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Readable string content of the label depending on the value of the \c latexContent.
|
* Readable string content of the label depending on the value of the latexContent.
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
getLabel() {
|
getLabel() {
|
||||||
|
@ -169,10 +216,10 @@ class DrawableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Latex markup string content of the label depending on the value of the \c latexContent.
|
* Latex markup string content of the label depending on the value of the latexContent.
|
||||||
* Every non latin character should be passed as latex symbols and formulas
|
* Every non latin character should be passed as latex symbols and formulas
|
||||||
* should be in latex form.
|
* should be in latex form.
|
||||||
* See ../latex.js for helper methods.
|
* See ../latex.mjs for helper methods.
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
getLatexLabel() {
|
getLatexLabel() {
|
||||||
|
@ -204,25 +251,26 @@ class DrawableObject {
|
||||||
update() {
|
update() {
|
||||||
// Refreshing dependencies.
|
// Refreshing dependencies.
|
||||||
for(let obj of this.requires)
|
for(let obj of this.requires)
|
||||||
obj.requiredBy = obj.requiredBy.filter(dep => dep != this)
|
obj.requiredBy = obj.requiredBy.filter(dep => dep !== this)
|
||||||
// Checking objects this one depends on
|
// Checking objects this one depends on
|
||||||
this.requires = []
|
this.requires = []
|
||||||
|
let currentObjectsByName = Objects.currentObjectsByName
|
||||||
let properties = this.constructor.properties()
|
let properties = this.constructor.properties()
|
||||||
for(let property in properties)
|
for(let property in properties)
|
||||||
if(typeof properties[property] == 'object' && 'type' in properties[property])
|
if(typeof properties[property] == 'object' && 'type' in properties[property])
|
||||||
if(properties[property].type == 'Expression' && this[property] != null) {
|
if(properties[property].type === 'Expression' && this[property] != null) {
|
||||||
// Expressions with dependencies
|
// Expressions with dependencies
|
||||||
for(let objName of this[property].requiredObjects()) {
|
for(let objName of this[property].requiredObjects()) {
|
||||||
if(objName in C.currentObjectsByName && !this.requires.includes(C.currentObjectsByName[objName])) {
|
if(objName in currentObjectsByName && !this.requires.includes(currentObjectsByName[objName])) {
|
||||||
this.requires.push(C.currentObjectsByName[objName])
|
this.requires.push(currentObjectsByName[objName])
|
||||||
C.currentObjectsByName[objName].requiredBy.push(this)
|
currentObjectsByName[objName].requiredBy.push(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(this[property].cached && this[property].requiredObjects().length > 0)
|
if(this[property].cached && this[property].requiredObjects().length > 0)
|
||||||
// Recalculate
|
// Recalculate
|
||||||
this[property].recache()
|
this[property].recache()
|
||||||
|
|
||||||
} else if(properties[property].type == 'ObjectType' && this[property] != null) {
|
} else if(properties[property].type === 'ObjectType' && this[property] != null) {
|
||||||
// Object dependency
|
// Object dependency
|
||||||
this.requires.push(this[property])
|
this.requires.push(this[property])
|
||||||
this[property].requiredBy.push(this)
|
this[property].requiredBy.push(this)
|
||||||
|
@ -239,24 +287,26 @@ class DrawableObject {
|
||||||
for(let toRemove of this.requiredBy) { // Normally, there should be none here, but better leave nothing just in case.
|
for(let toRemove of this.requiredBy) { // Normally, there should be none here, but better leave nothing just in case.
|
||||||
Objects.deleteObject(toRemove.name)
|
Objects.deleteObject(toRemove.name)
|
||||||
}
|
}
|
||||||
|
for(let toRemoveFrom of this.requires) {
|
||||||
|
toRemoveFrom.requiredBy = toRemoveFrom.requiredBy.filter(o => o !== this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract method. Draw the object onto the \c canvas with the 2D context \c ctx.
|
* Abstract method. Draw the object onto the canvas with the.
|
||||||
* @param {Canvas} canvas
|
* @param {CanvasAPI} canvas
|
||||||
* @param {Context2D} ctx
|
|
||||||
*/
|
*/
|
||||||
draw(canvas, ctx) {}
|
draw(canvas) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applicates a \c drawFunction with two position arguments depending on
|
* Applicates a drawFunction with two position arguments depending on
|
||||||
* both the \c posX and \c posY of where the label should be displayed,
|
* both the posX and posY of where the label should be displayed,
|
||||||
* and the \c labelPosition which declares the label should be displayed
|
* and the labelPosition which declares the label should be displayed
|
||||||
* relatively to that position.
|
* relatively to that position.
|
||||||
*
|
*
|
||||||
* @param {string|Enum} labelPosition - Position of the label relative to the marked position
|
* @param {string|Enum} labelPosition - Position of the label relative to the marked position
|
||||||
* @param {number} offset - Margin between the position and the object to be drawn
|
* @param {number} offset - Margin between the position and the object to be drawn
|
||||||
* @param {Dictionary} size - Size of the label item, containing two properties, "width" and "height"
|
* @param {Object.<string, int>} size - Size of the label item, containing two properties, "width" and "height"
|
||||||
* @param {number} posX - Component of the marked position on the x-axis
|
* @param {number} posX - Component of the marked position on the x-axis
|
||||||
* @param {number} posY - Component of the marked position on the y-axis
|
* @param {number} posY - Component of the marked position on the y-axis
|
||||||
* @param {function} drawFunction - Function with two arguments (x, y) that will be called to draw the label
|
* @param {function} drawFunction - Function with two arguments (x, y) that will be called to draw the label
|
||||||
|
@ -300,27 +350,26 @@ class DrawableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically draw text (by default the label of the object on the \c canvas with
|
* Automatically draw text (by default the label of the object on the canvas
|
||||||
* the 2D context \c ctx depending on user settings.
|
* depending on user settings.
|
||||||
* This method takes into account both the \c posX and \c posY of where the label
|
* This method takes into account both the posX and posY of where the label
|
||||||
* should be displayed, including the \c labelPosition relative to it.
|
* should be displayed, including the labelPosition relative to it.
|
||||||
* The text is get both through the \c getLatexFunction and \c getTextFunction
|
* The text is get both through the getLatexFunction and getTextFunction
|
||||||
* depending on whether to use latex.
|
* depending on whether to use latex.
|
||||||
* Then, it's displayed using the \c drawFunctionLatex (x,y,imageData) and
|
* Then, it's displayed using the drawFunctionLatex (x,y,imageData) and
|
||||||
* \c drawFunctionText (x,y,text) depending on whether to use latex.
|
* drawFunctionText (x,y,text) depending on whether to use latex.
|
||||||
*
|
*
|
||||||
* @param {Canvas} canvas
|
* @param {CanvasAPI} canvas
|
||||||
* @param {Context2D} ctx
|
|
||||||
* @param {string|Enum} labelPosition - Position of the label relative to the marked position
|
* @param {string|Enum} labelPosition - Position of the label relative to the marked position
|
||||||
* @param {number} posX - Component of the marked position on the x-axis
|
* @param {number} posX - Component of the marked position on the x-axis
|
||||||
* @param {number} posY - Component of the marked position on the y-axis
|
* @param {number} posY - Component of the marked position on the y-axis
|
||||||
* @param {bool} forceText - Force the rendering of the label as text
|
* @param {boolean} forceText - Force the rendering of the label as text
|
||||||
* @param {function|null} getLatexFunction - Function (no argument) to get the latex markup to be displayed
|
* @param {function|null} getLatexFunction - Function (no argument) to get the latex markup to be displayed
|
||||||
* @param {function|null} getTextFunction - Function (no argument) to get the text to be displayed
|
* @param {function|null} getTextFunction - Function (no argument) to get the text to be displayed
|
||||||
* @param {function|null} drawFunctionLatex - Function (x,y,imageData) to display the latex image
|
* @param {function|null} drawFunctionLatex - Function (x,y,imageData) to display the latex image
|
||||||
* @param {function|null} drawFunctionText - Function (x,y,text,textSize) to display the text
|
* @param {function|null} drawFunctionText - Function (x,y,text,textSize) to display the text
|
||||||
*/
|
*/
|
||||||
drawLabel(canvas, ctx, labelPosition, posX, posY, forceText = false,
|
drawLabel(canvas, labelPosition, posX, posY,forceText = false,
|
||||||
getLatexFunction = null, getTextFunction = null, drawFunctionLatex = null, drawFunctionText = null) {
|
getLatexFunction = null, getTextFunction = null, drawFunctionLatex = null, drawFunctionText = null) {
|
||||||
// Default functions
|
// Default functions
|
||||||
if(getLatexFunction == null)
|
if(getLatexFunction == null)
|
||||||
|
@ -328,25 +377,25 @@ class DrawableObject {
|
||||||
if(getTextFunction == null)
|
if(getTextFunction == null)
|
||||||
getTextFunction = this.getLabel.bind(this)
|
getTextFunction = this.getLabel.bind(this)
|
||||||
if(drawFunctionLatex == null)
|
if(drawFunctionLatex == null)
|
||||||
drawFunctionLatex = (x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, y, ltxImg.width, ltxImg.height)
|
drawFunctionLatex = (x,y,ltxImg) => canvas.drawVisibleImage(ltxImg.source, x, y, ltxImg.width, ltxImg.height)
|
||||||
if(drawFunctionText == null)
|
if(drawFunctionText == null)
|
||||||
drawFunctionText = (x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, y+textSize.height) // Positioned from left bottom
|
drawFunctionText = (x,y,text,textSize) => canvas.drawVisibleText(text, x, y+textSize.height) // Positioned from left bottom
|
||||||
// Drawing the label
|
// Drawing the label
|
||||||
let offset
|
let offset
|
||||||
if(!forceText && Latex.enabled) { // TODO: Check for user setting with Latex.
|
if(!forceText && Latex.enabled) {
|
||||||
// With latex
|
// With latex
|
||||||
let drawLblCb = function(canvas, ctx, ltxImg) {
|
let drawLblCb = ((ltxImg) => {
|
||||||
this.drawPositionDivergence(labelPosition, 8+ctx.lineWidth/2, ltxImg, posX, posY, (x,y) => drawFunctionLatex(x,y,ltxImg))
|
this.drawPositionDivergence(labelPosition, 8+canvas.linewidth/2, ltxImg, posX, posY, (x,y) => drawFunctionLatex(x,y,ltxImg))
|
||||||
}
|
}).bind(this)
|
||||||
let ltxLabel = getLatexFunction();
|
let ltxLabel = getLatexFunction();
|
||||||
if(ltxLabel != "")
|
if(ltxLabel !== "")
|
||||||
canvas.renderLatexImage(ltxLabel, this.color, drawLblCb.bind(this))
|
canvas.renderLatexImage(ltxLabel, this.color, drawLblCb.bind(this))
|
||||||
} else {
|
} else {
|
||||||
// Without latex
|
// Without latex
|
||||||
let text = getTextFunction()
|
let text = getTextFunction()
|
||||||
ctx.font = `${canvas.textsize}px sans-serif`
|
canvas.font = `${canvas.textsize}px sans-serif`
|
||||||
let textSize = canvas.measureText(ctx, text)
|
let textSize = canvas.measureText(text)
|
||||||
this.drawPositionDivergence(labelPosition, 8+ctx.lineWidth/2, textSize, posX, posY, (x,y) => drawFunctionText(x,y,text,textSize))
|
this.drawPositionDivergence(labelPosition, 8+canvas.linewidth/2, textSize, posX, posY, (x,y) => drawFunctionText(x,y,text,textSize))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,6 +404,7 @@ class DrawableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to be extended for every object on which
|
* Class to be extended for every object on which
|
||||||
* an y for a x can be computed with the execute function.
|
* an y for a x can be computed with the execute function.
|
||||||
|
@ -362,7 +412,7 @@ class DrawableObject {
|
||||||
* return null. However, theses same x values will
|
* return null. However, theses same x values will
|
||||||
* return false when passed to canExecute.
|
* return false when passed to canExecute.
|
||||||
*/
|
*/
|
||||||
class ExecutableObject extends DrawableObject {
|
export class ExecutableObject extends DrawableObject {
|
||||||
/**
|
/**
|
||||||
* Returns the corresponding y value for an x value.
|
* Returns the corresponding y value for an x value.
|
||||||
* If the object isn't defined on the given x, then
|
* If the object isn't defined on the given x, then
|
||||||
|
@ -376,38 +426,25 @@ class ExecutableObject extends DrawableObject {
|
||||||
* Returns false if the object isn't defined on the given x, true otherwise.
|
* Returns false if the object isn't defined on the given x, true otherwise.
|
||||||
*
|
*
|
||||||
* @param {number} x
|
* @param {number} x
|
||||||
* @returns {bool}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
canExecute(x = 1) {return true}
|
canExecute(x = 1) {return true}
|
||||||
/**
|
/**
|
||||||
* Returns the simplified expression string for a given x.
|
* Returns the simplified expression string for a given x.
|
||||||
*
|
*
|
||||||
* @param {number} x
|
* @param {number} x
|
||||||
* @returns {bool}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
simplify(x = 1) {return '0'}
|
simplify(x = 1) {return '0'}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if this object can be executed, so that an y value might be computed
|
* True if this object can be executed, so that an y value might be computed
|
||||||
* for an x value. Only ExecutableObjects have that property set to true.
|
* for an x value. Only ExecutableObjects have that property set to true.
|
||||||
* @return {bool}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
static executable() {return true}
|
static executable() {return true}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers the object \c obj in the object list.
|
|
||||||
* @param {DrawableObject} obj - Object to be registered.
|
|
||||||
*/
|
|
||||||
function registerObject(obj) {
|
|
||||||
// Registers an object to be used in LogarithmPlotter.
|
|
||||||
// This function is called from autoload.js
|
|
||||||
if(obj.prototype instanceof DrawableObject) {
|
|
||||||
Objects.types[obj.type()] = obj
|
|
||||||
} else {
|
|
||||||
console.error("Could not register object " + (obj.type()) + ", as it isn't a DrawableObject.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,16 +16,14 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import { textsub } from "../utils.mjs"
|
||||||
|
import { API as Common, ExecutableObject } from "common.mjs"
|
||||||
.import "common.js" as Common
|
import { parseDomain, Expression, SpecialDomain } from "../mathlib.mjs"
|
||||||
.import "../utils.js" as Utils
|
import * as P from "../parameters.mjs"
|
||||||
.import "../mathlib.js" as MathLib
|
import Latex from "../math/latex.mjs"
|
||||||
.import "../parameters.js" as P
|
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
|
||||||
class Function extends Common.ExecutableObject {
|
export default class Function extends ExecutableObject {
|
||||||
static type(){return 'Function'}
|
static type(){return 'Function'}
|
||||||
static displayType(){return qsTr('Function')}
|
static displayType(){return qsTr('Function')}
|
||||||
static displayTypeMultiple(){return qsTr('Functions')}
|
static displayTypeMultiple(){return qsTr('Functions')}
|
||||||
|
@ -37,8 +35,8 @@ class Function extends Common.ExecutableObject {
|
||||||
'comment',
|
'comment',
|
||||||
'Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5}'
|
'Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5}'
|
||||||
),
|
),
|
||||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
|
||||||
[QT_TRANSLATE_NOOP('prop','displayMode')]: P.Enum.FunctionDisplayType,
|
[QT_TRANSLATE_NOOP('prop','displayMode')]: P.Enum.FunctionDisplayType,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||||
'comment2': QT_TRANSLATE_NOOP(
|
'comment2': QT_TRANSLATE_NOOP(
|
||||||
'comment',
|
'comment',
|
||||||
|
@ -54,11 +52,11 @@ class Function extends Common.ExecutableObject {
|
||||||
drawPoints = true, drawDashedLines = true) {
|
drawPoints = true, drawDashedLines = true) {
|
||||||
if(name == null) name = Common.getNewName('fghjqlmnopqrstuvwabcde')
|
if(name == null) name = Common.getNewName('fghjqlmnopqrstuvwabcde')
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
if(typeof expression == 'number' || typeof expression == 'string') expression = new MathLib.Expression(expression.toString())
|
if(typeof expression == 'number' || typeof expression == 'string') expression = new Expression(expression.toString())
|
||||||
this.expression = expression
|
this.expression = expression
|
||||||
if(typeof definitionDomain == 'string') definitionDomain = MathLib.parseDomain(definitionDomain)
|
if(typeof definitionDomain == 'string') definitionDomain = parseDomain(definitionDomain)
|
||||||
this.definitionDomain = definitionDomain
|
this.definitionDomain = definitionDomain
|
||||||
if(typeof destinationDomain == 'string') destinationDomain = MathLib.parseDomain(destinationDomain)
|
if(typeof destinationDomain == 'string') destinationDomain = parseDomain(destinationDomain)
|
||||||
this.destinationDomain = destinationDomain
|
this.destinationDomain = destinationDomain
|
||||||
this.displayMode = displayMode
|
this.displayMode = displayMode
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
|
@ -68,28 +66,22 @@ class Function extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
if(this.displayMode == 'application') {
|
if(this.displayMode === 'application') {
|
||||||
return `${this.name}: ${this.definitionDomain} ⟶ ${this.destinationDomain}\n ${' '.repeat(this.name.length)}x ⟼ ${this.expression.toString()}`
|
return `${this.name}: ${this.definitionDomain} ⟶ ${this.destinationDomain}\n ${' '.repeat(this.name.length)}x ⟼ ${this.expression.toString()}`
|
||||||
} else {
|
} else {
|
||||||
return `${this.name}(x) = ${this.expression.toString()}\nD${Utils.textsub(this.name)} = ${this.definitionDomain}`
|
return `${this.name}(x) = ${this.expression.toString()}\nD${textsub(this.name)} = ${this.definitionDomain}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatexString() {
|
getLatexString() {
|
||||||
if(this.displayMode == 'application') {
|
if(this.displayMode === 'application') {
|
||||||
return `${Latex.variable(this.name)}:\\begin{array}{llll}${this.definitionDomain.latexMarkup}\\textrm{ } & \\rightarrow & \\textrm{ }${this.destinationDomain.latexMarkup}\\\\
|
return `${Latex.variable(this.name)}:\\begin{array}{llll}${this.definitionDomain.latexMarkup}\\textrm{ } & \\rightarrow & \\textrm{ }${this.destinationDomain.latexMarkup}\\\\
|
||||||
x\\textrm{ } & \\mapsto & \\textrm{ }${this.expression.latexMarkup}\\end{array}`
|
x\\textrm{ } & \\mapsto & \\textrm{ }${this.expression.latexMarkup}\\end{array}`
|
||||||
} else {
|
} else {
|
||||||
return `\\begin{array}{l}${Latex.variable(this.name)}(x) = ${this.expression.latexMarkup}\\\\ D_{${this.name}} = ${this.definitionDomain.latexMarkup}\\end{array}`
|
return `\\begin{array}{l}${Latex.variable(this.name)}(x) = ${this.expression.latexMarkup}\\\\ D_{${this.name}} = ${this.definitionDomain.latexMarkup}\\end{array}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
execute(x = 1) {
|
||||||
if(this.definitionDomain.includes(x))
|
if(this.definitionDomain.includes(x))
|
||||||
return this.expression.execute(x)
|
return this.expression.execute(x)
|
||||||
|
@ -106,36 +98,38 @@ class Function extends Common.ExecutableObject {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
Function.drawFunction(canvas, ctx, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines)
|
Function.drawFunction(canvas, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines)
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
|
|
||||||
static drawFunction(canvas, ctx, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) {
|
/**
|
||||||
// Reusable in other objects.
|
* Reusable in other objects.
|
||||||
// Drawing small traits every 0.2px
|
* Drawing small traits every few pixels
|
||||||
var pxprecision = 1
|
*/
|
||||||
var previousX = canvas.px2x(0)
|
static drawFunction(canvas, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) {
|
||||||
var previousY;
|
let pxprecision = 10
|
||||||
if(definitionDomain instanceof MathLib.SpecialDomain && definitionDomain.moveSupported) {
|
let previousX = canvas.px2x(0)
|
||||||
|
let previousY = null;
|
||||||
|
if(definitionDomain instanceof SpecialDomain && definitionDomain.moveSupported) {
|
||||||
// Point based functions.
|
// Point based functions.
|
||||||
previousX = definitionDomain.next(previousX)
|
previousX = definitionDomain.next(previousX)
|
||||||
if(previousX === null) previousX = definitionDomain.next(canvas.px2x(0))
|
if(previousX === null) previousX = definitionDomain.next(canvas.px2x(0))
|
||||||
previousY = expr.execute(previousX)
|
previousY = expr.execute(previousX)
|
||||||
if(!drawPoints && !drawDash) return
|
if(!drawPoints && !drawDash) return
|
||||||
while(previousX !== null && canvas.x2px(previousX) < canvas.canvasSize.width) {
|
while(previousX !== null && canvas.x2px(previousX) < canvas.width) {
|
||||||
// Reconverted for canvas to fix for logarithmic scales.
|
// Reconverted for canvas to fix for logarithmic scales.
|
||||||
var currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX)+pxprecision));
|
let currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX)+pxprecision));
|
||||||
var currentY = expr.execute(currentX)
|
let currentY = expr.execute(currentX)
|
||||||
if(currentX === null) break;
|
if(currentX === null) break;
|
||||||
if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) &&
|
if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) &&
|
||||||
(destinationDomain.includes(currentY) || destinationDomain.includes(previousY))) {
|
(destinationDomain.includes(currentY) || destinationDomain.includes(previousY))) {
|
||||||
if(drawDash)
|
if(drawDash)
|
||||||
canvas.drawDashedLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
|
canvas.drawDashedLine(canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
|
||||||
if(drawPoints) {
|
if(drawPoints) {
|
||||||
ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
|
canvas.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
|
||||||
ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
|
canvas.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
previousX = currentX
|
previousX = currentX
|
||||||
|
@ -143,21 +137,52 @@ class Function extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
if(drawPoints) {
|
if(drawPoints) {
|
||||||
// Drawing the last cross
|
// Drawing the last cross
|
||||||
ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
|
canvas.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
|
||||||
ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
|
canvas.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
previousY = expr.execute(previousX)
|
// Use max precision if function is trigonometrical on log scale.
|
||||||
for(var px = pxprecision; px < canvas.canvasSize.width; px += pxprecision) {
|
let exprString = expr.expr
|
||||||
var currentX = canvas.px2x(px)
|
if(exprString.includes("sin") || exprString.includes("cos") || exprString.includes("tan"))
|
||||||
var currentY = expr.execute(currentX)
|
pxprecision = (canvas.logscalex || exprString.includes("tan")) ? 1 : 3
|
||||||
if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) &&
|
// Calculate the previousY at the start of the canvas
|
||||||
(destinationDomain.includes(currentY) || destinationDomain.includes(previousY)) &&
|
if(definitionDomain.includes(previousX))
|
||||||
Math.abs(previousY-currentY)<100) {
|
previousY = expr.execute(previousX)
|
||||||
canvas.drawLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
|
for(let px = pxprecision; px < canvas.width; px += pxprecision) {
|
||||||
|
let currentX = canvas.px2x(px)
|
||||||
|
if(!definitionDomain.includes(previousX) && definitionDomain.includes(currentX)) {
|
||||||
|
// Should draw up to currentX, but NOT at previousX.
|
||||||
|
// Need to find the starting point.
|
||||||
|
let tmpPx = px-pxprecision
|
||||||
|
do {
|
||||||
|
tmpPx++;
|
||||||
|
previousX = canvas.px2x(tmpPx)
|
||||||
|
} while(!definitionDomain.includes(previousX))
|
||||||
|
// Recaclulate previousY
|
||||||
|
previousY = expr.execute(previousX)
|
||||||
|
} else if(!definitionDomain.includes(currentX)) {
|
||||||
|
// Next x is NOT in the definition domain.
|
||||||
|
// Augmenting the pixel precision until this condition is fulfilled.
|
||||||
|
let tmpPx = px
|
||||||
|
do {
|
||||||
|
tmpPx--;
|
||||||
|
currentX = canvas.px2x(tmpPx)
|
||||||
|
} 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.height))
|
||||||
|
if(definitionDomain.includes(previousX) && definitionDomain.includes(currentX)) {
|
||||||
|
let currentY = expr.execute(currentX)
|
||||||
|
if(destinationDomain.includes(currentY)) {
|
||||||
|
if(previousY != null && destinationDomain.includes(previousY) && Math.abs(previousY-currentY) < maxvariation) {
|
||||||
|
canvas.drawLine(canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
previousX = currentX
|
previousY = currentY
|
||||||
previousY = currentY
|
} else {
|
||||||
|
previousY = null // Last y was invalid, so let's not draw anything from it.
|
||||||
|
}
|
||||||
|
previousX = canvas.px2x(px)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,19 +16,19 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import { parseDomain, executeExpression, Expression, EmptySet, Domain } from "../mathlib.mjs"
|
||||||
|
import * as P from "../parameters.mjs"
|
||||||
|
import Objects from "../objects.mjs"
|
||||||
|
import Latex from "../math/latex.mjs"
|
||||||
|
|
||||||
.import "common.js" as Common
|
import { API as Common, ExecutableObject } from "common.mjs"
|
||||||
.import "function.js" as F
|
import Function from "function.mjs"
|
||||||
.import "../objects.js" as Objects
|
|
||||||
.import "../utils.js" as Utils
|
import { API as HistoryAPI } from "../history/common.mjs"
|
||||||
.import "../mathlib.js" as MathLib
|
import { CreateNewObject } from "../historylib.mjs"
|
||||||
.import "../historylib.js" as HistoryLib
|
|
||||||
.import "../parameters.js" as P
|
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
|
||||||
class GainBode extends Common.ExecutableObject {
|
export default class GainBode extends ExecutableObject {
|
||||||
static type(){return 'Gain Bode'}
|
static type(){return 'Gain Bode'}
|
||||||
static displayType(){return qsTr('Bode Magnitude')}
|
static displayType(){return qsTr('Bode Magnitude')}
|
||||||
static displayTypeMultiple(){return qsTr('Bode Magnitudes')}
|
static displayTypeMultiple(){return qsTr('Bode Magnitudes')}
|
||||||
|
@ -40,11 +40,11 @@ class GainBode extends Common.ExecutableObject {
|
||||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||||
[QT_TRANSLATE_NOOP('prop','omGraduation')]: 'boolean'
|
[QT_TRANSLATE_NOOP('prop','omGraduation')]: 'boolean'
|
||||||
}}
|
}}
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
om_0 = '', pass = 'high', gain = '20', labelPosition = 'above', labelX = 1, omGraduation = false) {
|
om_0 = '', pass = 'high', gain = '20', labelPosition = 'above', labelX = 1, omGraduation = false) {
|
||||||
if(name == null) name = Common.getNewName('G')
|
if(name == null) name = Common.getNewName('G')
|
||||||
if(name == 'G') name = 'G₀' // G is reserved for sum of BODE magnitudes (Somme gains Bode).
|
if(name === 'G') name = 'G₀' // G is reserved for sum of BODE magnitudes (Somme gains Bode).
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
if(typeof om_0 == "string") {
|
if(typeof om_0 == "string") {
|
||||||
// Point name or create one
|
// Point name or create one
|
||||||
|
@ -52,15 +52,16 @@ class GainBode extends Common.ExecutableObject {
|
||||||
if(om_0 == null) {
|
if(om_0 == null) {
|
||||||
// Create new point
|
// Create new point
|
||||||
om_0 = Objects.createNewRegisteredObject('Point', [Common.getNewName('ω'), true, this.color, 'name'])
|
om_0 = Objects.createNewRegisteredObject('Point', [Common.getNewName('ω'), true, this.color, 'name'])
|
||||||
HistoryLib.history.addToHistory(new HistoryLib.CreateNewObject(om_0.name, 'Point', om_0.export()))
|
HistoryAPI.addToHistory(new CreateNewObject(om_0.name, 'Point', om_0.export()))
|
||||||
om_0.update()
|
om_0.update()
|
||||||
labelPosition = 'below'
|
labelPosition = 'below'
|
||||||
}
|
}
|
||||||
om_0.requiredBy.push(this)
|
om_0.requiredBy.push(this)
|
||||||
}
|
}
|
||||||
|
/** @type {Point} */
|
||||||
this.om_0 = om_0
|
this.om_0 = om_0
|
||||||
this.pass = pass
|
this.pass = pass
|
||||||
if(typeof gain == 'number' || typeof gain == 'string') gain = new MathLib.Expression(gain.toString())
|
if(typeof gain == 'number' || typeof gain == 'string') gain = new Expression(gain.toString())
|
||||||
this.gain = gain
|
this.gain = gain
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
this.labelX = labelX
|
this.labelX = labelX
|
||||||
|
@ -68,27 +69,22 @@ class GainBode extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
let pass = this.pass == "low" ? qsTr("low-pass") : qsTr("high-pass");
|
let pass = this.pass === "low" ? qsTr("low-pass") : qsTr("high-pass");
|
||||||
return `${this.name}: ${pass}; ${this.om_0.name} = ${this.om_0.x}\n ${' '.repeat(this.name.length)}${this.gain.toString(true)} dB/dec`
|
return `${this.name}: ${pass}; ${this.om_0.name} = ${this.om_0.x}\n ${' '.repeat(this.name.length)}${this.gain.toString(true)} dB/dec`
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatexString() {
|
getLatexString() {
|
||||||
let pass = this.pass == "low" ? qsTr("low-pass") : qsTr("high-pass");
|
let pass = this.pass === "low" ? qsTr("low-pass") : qsTr("high-pass");
|
||||||
return `\\mathrm{${Latex.variable(this.name)}:}\\begin{array}{l}
|
return `\\mathrm{${Latex.variable(this.name)}:}\\begin{array}{l}
|
||||||
\\textsf{${pass}};${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup} \\\\
|
\\textsf{${pass}};${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup} \\\\
|
||||||
${this.gain.latexMarkup}\\textsf{ dB/dec}
|
${this.gain.latexMarkup}\\textsf{ dB/dec}
|
||||||
\\end{array}`
|
\\end{array}`
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
execute(x=1) {
|
||||||
if(typeof x == 'string') x = MathLib.executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
if((this.pass == 'high' && x < this.om_0.x) || (this.pass == 'low' && x > this.om_0.x)) {
|
if((this.pass === 'high' && x < this.om_0.x) || (this.pass === 'low' && x > this.om_0.x)) {
|
||||||
var dbfn = new MathLib.Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
|
let dbfn = new Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
|
||||||
return dbfn.execute(x)
|
return dbfn.execute(x)
|
||||||
} else {
|
} else {
|
||||||
return this.om_0.y.execute()
|
return this.om_0.y.execute()
|
||||||
|
@ -96,10 +92,10 @@ class GainBode extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
simplify(x = 1) {
|
simplify(x = 1) {
|
||||||
var xval = x
|
let xval = x
|
||||||
if(typeof x == 'string') xval = MathLib.executeExpression(x)
|
if(typeof x == 'string') xval = executeExpression(x)
|
||||||
if((this.pass == 'high' && xval < this.om_0.x) || (this.pass == 'low' && xval > this.om_0.x)) {
|
if((this.pass === 'high' && xval < this.om_0.x.execute()) || (this.pass === 'low' && xval > this.om_0.x.execute())) {
|
||||||
var dbfn = new MathLib.Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
|
let dbfn = new Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
|
||||||
return dbfn.simplify(x)
|
return dbfn.simplify(x)
|
||||||
} else {
|
} else {
|
||||||
return this.om_0.y.toString()
|
return this.om_0.y.toString()
|
||||||
|
@ -110,34 +106,34 @@ class GainBode extends Common.ExecutableObject {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
var base = [canvas.x2px(this.om_0.x), canvas.y2px(this.om_0.y)]
|
let base = [canvas.x2px(this.om_0.x.execute()), canvas.y2px(this.om_0.y.execute())]
|
||||||
var dbfn = new MathLib.Expression(`${this.gain.execute()}*(log10(x)-log10(${this.om_0.x}))+${this.om_0.y}`)
|
let dbfn = new Expression(`${this.gain.execute()}*(log10(x)-log10(${this.om_0.x}))+${this.om_0.y}`)
|
||||||
var inDrawDom = new MathLib.EmptySet()
|
let inDrawDom = new EmptySet()
|
||||||
|
|
||||||
if(this.pass == 'high') {
|
if(this.pass === 'high') {
|
||||||
// High pass, linear line from begining, then constant to the end.
|
// High pass, linear line from beginning, then constant to the end.
|
||||||
canvas.drawLine(ctx, base[0], base[1], canvas.canvasSize.width, base[1])
|
canvas.drawLine(base[0], base[1], canvas.width, base[1])
|
||||||
inDrawDom = MathLib.parseDomain(`]-inf;${this.om_0.x}[`)
|
inDrawDom = parseDomain(`]-inf;${this.om_0.x}[`)
|
||||||
} else {
|
} else {
|
||||||
// Low pass, constant from the beginning, linear line to the end.
|
// Low pass, constant from the beginning, linear line to the end.
|
||||||
canvas.drawLine(ctx, base[0], base[1], 0, base[1])
|
canvas.drawLine(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, dbfn, inDrawDom, Domain.R)
|
||||||
// Dashed line representing break in function
|
// Dashed line representing break in function
|
||||||
var xpos = canvas.x2px(this.om_0.x.execute())
|
let xpos = canvas.x2px(this.om_0.x.execute())
|
||||||
var dashPxSize = 10
|
let dashPxSize = 10
|
||||||
for(var i = 0; i < canvas.canvasSize.height && this.omGraduation; i += dashPxSize*2)
|
for(let i = 0; i < canvas.height && this.omGraduation; i += dashPxSize*2)
|
||||||
canvas.drawLine(ctx, xpos, i, xpos, i+dashPxSize)
|
canvas.drawLine(xpos, i, xpos, i+dashPxSize)
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
super.update()
|
super.update()
|
||||||
if(Objects.currentObjects['Somme gains Bode'] != undefined && Objects.currentObjects['Somme gains Bode'].length > 0) {
|
if(Objects.currentObjects['Somme gains Bode'] !== undefined && Objects.currentObjects['Somme gains Bode'].length > 0) {
|
||||||
Objects.currentObjects['Somme gains Bode'][0].recalculateCache()
|
Objects.currentObjects['Somme gains Bode'][0].recalculateCache()
|
||||||
} else {
|
} else {
|
||||||
Objects.createNewRegisteredObject('Somme gains Bode')
|
Objects.createNewRegisteredObject('Somme gains Bode')
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,17 +16,17 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import { executeExpression, Expression } from "../mathlib.mjs"
|
||||||
|
import * as P from "../parameters.mjs"
|
||||||
|
import Objects from "../objects.mjs"
|
||||||
|
import Latex from "../math/latex.mjs"
|
||||||
|
|
||||||
.import "common.js" as Common
|
import { API as Common, ExecutableObject } from "common.mjs"
|
||||||
.import "../objects.js" as Objects
|
import { API as HistoryAPI } from "../history/common.mjs"
|
||||||
.import "../mathlib.js" as MathLib
|
import { CreateNewObject } from "../historylib.mjs"
|
||||||
.import "../historylib.js" as HistoryLib
|
|
||||||
.import "../parameters.js" as P
|
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
|
||||||
class PhaseBode extends Common.ExecutableObject {
|
export default class PhaseBode extends ExecutableObject {
|
||||||
static type(){return 'Phase Bode'}
|
static type(){return 'Phase Bode'}
|
||||||
static displayType(){return qsTr('Bode Phase')}
|
static displayType(){return qsTr('Bode Phase')}
|
||||||
static displayTypeMultiple(){return qsTr('Bode Phases')}
|
static displayTypeMultiple(){return qsTr('Bode Phases')}
|
||||||
|
@ -41,9 +41,9 @@ class PhaseBode extends Common.ExecutableObject {
|
||||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
om_0 = '', phase = 90, unit = '°', labelPosition = 'above', labelX = 1) {
|
om_0 = '', phase = 90, unit = '°', labelPosition = 'above', labelX = 1) {
|
||||||
if(name == null) name = Common.getNewName('φ')
|
if(name == null) name = Common.getNewName('φ')
|
||||||
if(name == 'φ') name = 'φ₀' // φ is reserved for sum of BODE phases (Somme phases Bode).
|
if(name === 'φ') name = 'φ₀' // φ is reserved for sum of BODE phases (Somme phases Bode).
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
if(typeof phase == 'number' || typeof phase == 'string') phase = new MathLib.Expression(phase.toString())
|
if(typeof phase == 'number' || typeof phase == 'string') phase = new Expression(phase.toString())
|
||||||
this.phase = phase
|
this.phase = phase
|
||||||
if(typeof om_0 == "string") {
|
if(typeof om_0 == "string") {
|
||||||
// Point name or create one
|
// Point name or create one
|
||||||
|
@ -52,22 +52,18 @@ class PhaseBode extends Common.ExecutableObject {
|
||||||
// Create new point
|
// Create new point
|
||||||
om_0 = Objects.createNewRegisteredObject('Point', [Common.getNewName('ω'), this.color, 'name'])
|
om_0 = Objects.createNewRegisteredObject('Point', [Common.getNewName('ω'), this.color, 'name'])
|
||||||
om_0.labelPosition = this.phase.execute() >= 0 ? 'above' : 'below'
|
om_0.labelPosition = this.phase.execute() >= 0 ? 'above' : 'below'
|
||||||
HistoryLib.history.addToHistory(new HistoryLib.CreateNewObject(om_0.name, 'Point', om_0.export()))
|
HistoryAPI.history.addToHistory(new CreateNewObject(om_0.name, 'Point', om_0.export()))
|
||||||
labelPosition = 'below'
|
labelPosition = 'below'
|
||||||
}
|
}
|
||||||
om_0.requiredBy.push(this)
|
om_0.requiredBy.push(this)
|
||||||
}
|
}
|
||||||
|
/** @type {Point} */
|
||||||
this.om_0 = om_0
|
this.om_0 = om_0
|
||||||
this.unit = unit
|
this.unit = unit
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
this.labelX = labelX
|
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() {
|
getReadableString() {
|
||||||
return `${this.name}: ${this.phase.toString(true)}${this.unit} at ${this.om_0.name} = ${this.om_0.x}`
|
return `${this.name}: ${this.phase.toString(true)}${this.unit} at ${this.om_0.name} = ${this.om_0.x}`
|
||||||
}
|
}
|
||||||
|
@ -77,7 +73,7 @@ class PhaseBode extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x=1) {
|
execute(x=1) {
|
||||||
if(typeof x == 'string') x = MathLib.executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
if(x < this.om_0.x) {
|
if(x < this.om_0.x) {
|
||||||
return this.om_0.y.execute()
|
return this.om_0.y.execute()
|
||||||
} else {
|
} else {
|
||||||
|
@ -86,13 +82,13 @@ class PhaseBode extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
simplify(x = 1) {
|
simplify(x = 1) {
|
||||||
var xval = x
|
let xval = x
|
||||||
if(typeof x == 'string') xval = MathLib.executeExpression(x)
|
if(typeof x == 'string') xval = executeExpression(x)
|
||||||
if(xval < this.om_0.x) {
|
if(xval < this.om_0.x) {
|
||||||
return this.om_0.y.toString()
|
return this.om_0.y.toString()
|
||||||
} else {
|
} else {
|
||||||
var newExp = this.om_0.y.toEditableString() + ' + ' + this.phase.toEditableString()
|
let newExp = this.om_0.y.toEditableString() + ' + ' + this.phase.toEditableString()
|
||||||
return (new MathLib.Expression(newExp)).toString()
|
return (new Expression(newExp)).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,25 +96,25 @@ class PhaseBode extends Common.ExecutableObject {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
var baseX = canvas.x2px(this.om_0.x.execute())
|
let baseX = canvas.x2px(this.om_0.x.execute())
|
||||||
var omy = this.om_0.y.execute()
|
let omy = this.om_0.y.execute()
|
||||||
var augmt = this.phase.execute()
|
let augmt = this.phase.execute()
|
||||||
var baseY = canvas.y2px(omy)
|
let baseY = canvas.y2px(omy)
|
||||||
var augmtY = canvas.y2px(omy+augmt)
|
let augmtY = canvas.y2px(omy+augmt)
|
||||||
// Before change line.
|
// Before change line.
|
||||||
canvas.drawLine(ctx, 0, baseY, Math.min(baseX, canvas.canvasSize.height), baseY)
|
canvas.drawLine(0, baseY, Math.min(baseX, canvas.height), baseY)
|
||||||
// Transition line.
|
// Transition line.
|
||||||
canvas.drawLine(ctx, baseX, baseY, baseX, augmtY)
|
canvas.drawLine(baseX, baseY, baseX, augmtY)
|
||||||
// After change line
|
// After change line
|
||||||
canvas.drawLine(ctx, Math.max(0, baseX), augmtY, canvas.canvasSize.width, augmtY)
|
canvas.drawLine(Math.max(0, baseX), augmtY, canvas.width, augmtY)
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
if(Objects.currentObjects['Somme phases Bode'] != undefined && Objects.currentObjects['Somme phases Bode'].length > 0) {
|
if(Objects.currentObjects['Somme phases Bode'] !== undefined && Objects.currentObjects['Somme phases Bode'].length > 0) {
|
||||||
Objects.currentObjects['Somme phases Bode'][0].recalculateCache()
|
Objects.currentObjects['Somme phases Bode'][0].recalculateCache()
|
||||||
} else {
|
} else {
|
||||||
Objects.createNewRegisteredObject('Somme phases Bode')
|
Objects.createNewRegisteredObject('Somme phases Bode')
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,15 +16,13 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import { Expression } from "../mathlib.mjs"
|
||||||
|
import * as P from "../parameters.mjs"
|
||||||
.import "common.js" as Common
|
import Latex from "../math/latex.mjs"
|
||||||
.import "../mathlib.js" as MathLib
|
import { API as Common, DrawableObject } from "common.mjs"
|
||||||
.import "../parameters.js" as P
|
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
|
||||||
class Point extends Common.DrawableObject {
|
export default class Point extends DrawableObject {
|
||||||
static type(){return 'Point'}
|
static type(){return 'Point'}
|
||||||
static displayType(){return qsTr('Point')}
|
static displayType(){return qsTr('Point')}
|
||||||
static displayTypeMultiple(){return qsTr('Points')}
|
static displayTypeMultiple(){return qsTr('Points')}
|
||||||
|
@ -40,9 +38,9 @@ class Point extends Common.DrawableObject {
|
||||||
x = 1, y = 0, labelPosition = 'above', pointStyle = '●') {
|
x = 1, y = 0, labelPosition = 'above', pointStyle = '●') {
|
||||||
if(name == null) name = Common.getNewName('ABCDEFJKLMNOPQRSTUVW')
|
if(name == null) name = Common.getNewName('ABCDEFJKLMNOPQRSTUVW')
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
if(typeof x == 'number' || typeof x == 'string') x = new MathLib.Expression(x.toString())
|
if(typeof x == 'number' || typeof x == 'string') x = new Expression(x.toString())
|
||||||
this.x = x
|
this.x = x
|
||||||
if(typeof y == 'number' || typeof y == 'string') y = new MathLib.Expression(y.toString())
|
if(typeof y == 'number' || typeof y == 'string') y = new Expression(y.toString())
|
||||||
this.y = y
|
this.y = y
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
this.pointStyle = pointStyle
|
this.pointStyle = pointStyle
|
||||||
|
@ -56,28 +54,22 @@ class Point extends Common.DrawableObject {
|
||||||
return `${Latex.variable(this.name)} = \\left(${this.x.latexMarkup}, ${this.y.latexMarkup}\\right)`
|
return `${Latex.variable(this.name)} = \\left(${this.x.latexMarkup}, ${this.y.latexMarkup}\\right)`
|
||||||
}
|
}
|
||||||
|
|
||||||
export() {
|
draw(canvas) {
|
||||||
return [this.name, this.visible, this.color.toString(), this.labelContent, this.x.toEditableString(), this.y.toEditableString(), this.labelPosition, this.pointStyle]
|
let [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())]
|
||||||
}
|
let pointSize = 8+(canvas.linewidth*2)
|
||||||
|
|
||||||
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) {
|
switch(this.pointStyle) {
|
||||||
case '●':
|
case '●':
|
||||||
ctx.beginPath();
|
canvas.disc(canvasX, canvasY, pointSize/2)
|
||||||
ctx.ellipse(canvasX-pointSize/2, canvasY-pointSize/2, pointSize, pointSize)
|
|
||||||
ctx.fill();
|
|
||||||
break;
|
break;
|
||||||
case '✕':
|
case '✕':
|
||||||
canvas.drawLine(ctx, canvasX-pointSize/2, canvasY-pointSize/2, canvasX+pointSize/2, canvasY+pointSize/2)
|
canvas.drawLine(canvasX-pointSize/2, canvasY-pointSize/2, canvasX+pointSize/2, canvasY+pointSize/2)
|
||||||
canvas.drawLine(ctx, canvasX-pointSize/2, canvasY+pointSize/2, canvasX+pointSize/2, canvasY-pointSize/2)
|
canvas.drawLine(canvasX-pointSize/2, canvasY+pointSize/2, canvasX+pointSize/2, canvasY-pointSize/2)
|
||||||
break;
|
break;
|
||||||
case '+':
|
case '+':
|
||||||
ctx.fillRect(canvasX-pointSize/2, canvasY-1, pointSize, 2)
|
canvas.fillRect(canvasX-pointSize/2, canvasY-1, pointSize, 2)
|
||||||
ctx.fillRect(canvasX-1, canvasY-pointSize/2, 2, pointSize)
|
canvas.fillRect(canvasX-1, canvasY-pointSize/2, 2, pointSize)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvasX, canvasY)
|
this.drawLabel(canvas, this.labelPosition, canvasX, canvasY)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,45 +16,44 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import { API as Common, ExecutableObject } from "common.mjs"
|
||||||
|
import * as P from "../parameters.mjs"
|
||||||
.import "common.js" as Common
|
import Latex from "../math/latex.mjs"
|
||||||
.import "../parameters.js" as P
|
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
|
||||||
class RepartitionFunction extends Common.ExecutableObject {
|
export default class RepartitionFunction extends ExecutableObject {
|
||||||
static type(){return 'Repartition'}
|
static type(){return 'Repartition'}
|
||||||
static displayType(){return qsTr('Repartition')}
|
static displayType(){return qsTr('Repartition')}
|
||||||
static displayTypeMultiple(){return qsTr('Repartition functions')}
|
static displayTypeMultiple(){return qsTr('Repartition functions')}
|
||||||
static properties() {return {
|
static properties() {return {
|
||||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
|
||||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
|
||||||
'comment1': QT_TRANSLATE_NOOP(
|
'comment1': QT_TRANSLATE_NOOP(
|
||||||
'comment',
|
'comment',
|
||||||
'Note: Specify the probability for each value.'
|
'Note: Specify the probability for each value.'
|
||||||
),
|
),
|
||||||
[QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d\.,]+$/, 'P({name_} = ', ') = '),
|
[QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'double', /^-?[\d.,]+$/, /^-?[\d.,]+$/, 'P({name_} = ', ') = '),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||||
}}
|
}}
|
||||||
|
static import(name, visible, color, labelContent, ...args) {
|
||||||
|
console.log(args, args.length)
|
||||||
|
if(args.length === 5) {
|
||||||
|
// Two legacy values no longer included.
|
||||||
|
args.shift()
|
||||||
|
args.shift()
|
||||||
|
}
|
||||||
|
return super.import(name, visible, color, labelContent, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
beginIncluded = true, drawLineEnds = true, probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) {
|
probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) {
|
||||||
if(name == null) name = Common.getNewName('XYZUVW', "F_")
|
if(name == null) name = Common.getNewName('XYZUVW', "F_")
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
this.beginIncluded = beginIncluded
|
|
||||||
this.drawLineEnds = drawLineEnds
|
|
||||||
this.probabilities = probabilities
|
this.probabilities = probabilities
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
this.labelX = labelX
|
this.labelX = labelX
|
||||||
this.update()
|
this.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
export() {
|
|
||||||
return [this.name, this.visible, this.color.toString(), this.labelContent,
|
|
||||||
this.beginIncluded, this.drawLineEnds, this.probabilities, this.labelPosition, this.labelX]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
let keys = Object.keys(this.probabilities).sort((a,b) => a-b);
|
let keys = Object.keys(this.probabilities).sort((a,b) => a-b);
|
||||||
let varname = this.name.substring(this.name.indexOf("_")+1)
|
let varname = this.name.substring(this.name.indexOf("_")+1)
|
||||||
|
@ -69,7 +68,7 @@ class RepartitionFunction extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x = 1) {
|
execute(x = 1) {
|
||||||
var ret = 0;
|
let ret = 0;
|
||||||
Object.keys(this.probabilities).sort((a,b) => a-b).forEach(idx => {
|
Object.keys(this.probabilities).sort((a,b) => a-b).forEach(idx => {
|
||||||
if(x >= idx) ret += parseFloat(this.probabilities[idx].replace(/,/g, '.'))
|
if(x >= idx) ret += parseFloat(this.probabilities[idx].replace(/,/g, '.'))
|
||||||
})
|
})
|
||||||
|
@ -77,6 +76,7 @@ class RepartitionFunction extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
canExecute(x = 1) {return true}
|
canExecute(x = 1) {return true}
|
||||||
|
|
||||||
// Simplify returns the simplified string of the expression.
|
// Simplify returns the simplified string of the expression.
|
||||||
simplify(x = 1) {
|
simplify(x = 1) {
|
||||||
return this.execute(x)
|
return this.execute(x)
|
||||||
|
@ -93,61 +93,51 @@ class RepartitionFunction extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
var currentY = 0;
|
let currentY = 0;
|
||||||
var keys = Object.keys(this.probabilities).map(idx => parseInt(idx)).sort((a,b) => a-b)
|
let keys = Object.keys(this.probabilities).map(idx => parseInt(idx)).sort((a,b) => a-b)
|
||||||
if(canvas.isVisible(keys[0],this.probabilities[keys[0]].replace(/,/g, '.'))) {
|
if(canvas.isVisible(keys[0],this.probabilities[keys[0]].replace(/,/g, '.'))) {
|
||||||
canvas.drawLine(ctx,
|
canvas.drawLine(0, canvas.y2px(0), canvas.x2px(keys[0]), canvas.y2px(0))
|
||||||
0,
|
|
||||||
canvas.y2px(0),
|
|
||||||
canvas.x2px(keys[0]),
|
|
||||||
canvas.y2px(0)
|
|
||||||
)
|
|
||||||
if(canvas.isVisible(keys[0],0)) {
|
if(canvas.isVisible(keys[0],0)) {
|
||||||
ctx.beginPath();
|
canvas.arc(canvas.x2px(keys[0])+4,canvas.y2px(0), 4, Math.PI / 2, 3 * Math.PI / 2);
|
||||||
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++) {
|
for(let i = 0; i < keys.length-1; i++) {
|
||||||
var idx = keys[i];
|
let idx = keys[i];
|
||||||
currentY += parseFloat(this.probabilities[idx].replace(/,/g, '.'));
|
currentY += parseFloat(this.probabilities[idx].replace(/,/g, '.'));
|
||||||
if(canvas.isVisible(idx,currentY) || canvas.isVisible(keys[i+1],currentY)) {
|
if(canvas.isVisible(idx,currentY) || canvas.isVisible(keys[i+1],currentY)) {
|
||||||
canvas.drawLine(ctx,
|
canvas.drawLine(
|
||||||
Math.max(0,canvas.x2px(idx)),
|
Math.max(0,canvas.x2px(idx)),
|
||||||
canvas.y2px(currentY),
|
canvas.y2px(currentY),
|
||||||
Math.min(canvas.canvasSize.width,canvas.x2px(keys[i+1])),
|
Math.min(canvas.width,canvas.x2px(keys[i+1])),
|
||||||
canvas.y2px(currentY)
|
canvas.y2px(currentY)
|
||||||
)
|
)
|
||||||
if(canvas.isVisible(idx,currentY)) {
|
if(canvas.isVisible(idx,currentY)) {
|
||||||
ctx.beginPath();
|
canvas.disc(canvas.x2px(idx), canvas.y2px(currentY), 4)
|
||||||
ctx.arc(canvas.x2px(idx),canvas.y2px(currentY), 4, 0, 2 * Math.PI);
|
|
||||||
ctx.fill();
|
|
||||||
}
|
}
|
||||||
if(canvas.isVisible(keys[i+1],currentY)) {
|
if(canvas.isVisible(keys[i+1],currentY)) {
|
||||||
ctx.beginPath();
|
canvas.arc(canvas.x2px(keys[i+1])+4,canvas.y2px(currentY), 4, Math.PI / 2, 3 * Math.PI / 2);
|
||||||
ctx.arc(canvas.x2px(keys[i+1])+4,canvas.y2px(currentY), 4, Math.PI / 2, 3 * Math.PI / 2);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(canvas.isVisible(keys[keys.length-1],currentY+parseFloat(this.probabilities[keys[keys.length-1]]))) {
|
if(canvas.isVisible(keys[keys.length-1],currentY+parseFloat(this.probabilities[keys[keys.length-1]]))) {
|
||||||
canvas.drawLine(ctx,
|
canvas.drawLine(
|
||||||
Math.max(0,canvas.x2px(keys[keys.length-1])),
|
Math.max(0,canvas.x2px(keys[keys.length-1])),
|
||||||
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))),
|
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))),
|
||||||
canvas.canvasSize.width,
|
canvas.width,
|
||||||
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.')))
|
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.')))
|
||||||
)
|
)
|
||||||
ctx.beginPath();
|
canvas.disc(
|
||||||
ctx.arc(
|
|
||||||
canvas.x2px(keys[keys.length-1]),
|
canvas.x2px(keys[keys.length-1]),
|
||||||
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))),
|
canvas.y2px(
|
||||||
4, 0, 2 * Math.PI);
|
currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))
|
||||||
ctx.fill();
|
),
|
||||||
|
4
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,16 +16,15 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import { Sequence as MathSequence, Domain } from "../mathlib.mjs"
|
||||||
|
import * as P from "../parameters.mjs"
|
||||||
|
import Latex from "../math/latex.mjs"
|
||||||
|
|
||||||
.import "common.js" as Common
|
import { API as Common, ExecutableObject } from "common.mjs"
|
||||||
.import "function.js" as F
|
import Function from "function.mjs"
|
||||||
.import "../mathlib.js" as MathLib
|
|
||||||
.import "../parameters.js" as P
|
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
|
||||||
class Sequence extends Common.ExecutableObject {
|
export default class Sequence extends ExecutableObject {
|
||||||
static type(){return 'Sequence'}
|
static type(){return 'Sequence'}
|
||||||
static displayType(){return qsTr('Sequence')}
|
static displayType(){return qsTr('Sequence')}
|
||||||
static displayTypeMultiple(){return qsTr('Sequences')}
|
static displayTypeMultiple(){return qsTr('Sequences')}
|
||||||
|
@ -56,21 +55,17 @@ class Sequence extends Common.ExecutableObject {
|
||||||
this.labelX = labelX
|
this.labelX = labelX
|
||||||
this.update()
|
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() {
|
update() {
|
||||||
|
console.log('Updating sequence', this.sequence)
|
||||||
super.update()
|
super.update()
|
||||||
if(
|
if(
|
||||||
this.sequence == null || this.baseValues != this.sequence.baseValues ||
|
this.sequence == null || this.baseValues !== this.sequence.baseValues ||
|
||||||
this.sequence.name != this.name ||
|
this.sequence.name !== this.name ||
|
||||||
this.sequence.expr != Object.values(this.defaultExpression)[0] ||
|
this.sequence.expr !== Object.values(this.defaultExpression)[0] ||
|
||||||
this.sequence.valuePlus != Object.keys(this.defaultExpression)[0]
|
this.sequence.valuePlus !== Object.keys(this.defaultExpression)[0]
|
||||||
)
|
)
|
||||||
this.sequence = new MathLib.Sequence(
|
this.sequence = new MathSequence(
|
||||||
this.name, this.baseValues,
|
this.name, this.baseValues,
|
||||||
Object.keys(this.defaultExpression)[0],
|
Object.keys(this.defaultExpression)[0],
|
||||||
Object.values(this.defaultExpression)[0]
|
Object.values(this.defaultExpression)[0]
|
||||||
|
@ -86,15 +81,15 @@ class Sequence extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x = 1) {
|
execute(x = 1) {
|
||||||
if(x % 1 == 0)
|
if(x % 1 === 0)
|
||||||
return this.sequence.execute(x)
|
return this.sequence.execute(x)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
canExecute(x = 1) {return x%1 == 0}
|
canExecute(x = 1) {return x%1 === 0}
|
||||||
|
|
||||||
// Simplify returns the simplified string of the expression.
|
// Simplify returns the simplified string of the expression.
|
||||||
simplify(x = 1) {
|
simplify(x = 1) {
|
||||||
if(x % 1 == 0)
|
if(x % 1 === 0)
|
||||||
return this.sequence.simplify(x)
|
return this.sequence.simplify(x)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -121,11 +116,11 @@ class Sequence extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
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, this.sequence, canvas.logscalex ? Domain.NE : Domain.N, Domain.R, this.drawPoints, this.drawDashedLines)
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,17 +16,16 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import { parseDomain, Expression, Domain } from "../mathlib.mjs"
|
||||||
|
import * as P from "../parameters.mjs"
|
||||||
|
import Objects from "../objects.mjs"
|
||||||
|
import Latex from "../math/latex.mjs"
|
||||||
|
|
||||||
.import "common.js" as Common
|
import { ExecutableObject } from "common.mjs"
|
||||||
.import "function.js" as F
|
import Function from "function.mjs"
|
||||||
.import "../objects.js" as Objects
|
|
||||||
.import "../mathlib.js" as MathLib
|
|
||||||
.import "../parameters.js" as P
|
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
|
||||||
class SommeGainsBode extends Common.ExecutableObject {
|
export default class SommeGainsBode extends ExecutableObject {
|
||||||
static type(){return 'Somme gains Bode'}
|
static type(){return 'Somme gains Bode'}
|
||||||
static displayType(){return qsTr('Bode Magnitudes Sum')}
|
static displayType(){return qsTr('Bode Magnitudes Sum')}
|
||||||
static displayTypeMultiple(){return qsTr('Bode Magnitudes Sum')}
|
static displayTypeMultiple(){return qsTr('Bode Magnitudes Sum')}
|
||||||
|
@ -45,10 +44,6 @@ class SommeGainsBode extends Common.ExecutableObject {
|
||||||
this.recalculateCache()
|
this.recalculateCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
export() {
|
|
||||||
return [this.name, this.visible, this.color.toString(), this.labelContent, this.labelPosition, this.labelX]
|
|
||||||
}
|
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
return `${this.name} = ${Objects.getObjectsName('Gain Bode').join(' + ')}`
|
return `${this.name} = ${Objects.getObjectsName('Gain Bode').join(' + ')}`
|
||||||
}
|
}
|
||||||
|
@ -58,7 +53,7 @@ class SommeGainsBode extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x = 0) {
|
execute(x = 0) {
|
||||||
for(var [dbfn, inDrawDom] of this.cachedParts) {
|
for(let [dbfn, inDrawDom] of this.cachedParts) {
|
||||||
if(inDrawDom.includes(x)) {
|
if(inDrawDom.includes(x)) {
|
||||||
return dbfn.execute(x)
|
return dbfn.execute(x)
|
||||||
}
|
}
|
||||||
|
@ -71,7 +66,7 @@ class SommeGainsBode extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
simplify(x = 1) {
|
simplify(x = 1) {
|
||||||
for(var [dbfn, inDrawDom] of this.cachedParts) {
|
for(let [dbfn, inDrawDom] of this.cachedParts) {
|
||||||
if(inDrawDom.includes(x)) {
|
if(inDrawDom.includes(x)) {
|
||||||
return dbfn.simplify(x)
|
return dbfn.simplify(x)
|
||||||
}
|
}
|
||||||
|
@ -82,33 +77,33 @@ class SommeGainsBode extends Common.ExecutableObject {
|
||||||
recalculateCache() {
|
recalculateCache() {
|
||||||
this.cachedParts = []
|
this.cachedParts = []
|
||||||
// Calculating this is fairly resource expansive so it's cached.
|
// Calculating this is fairly resource expansive so it's cached.
|
||||||
if(Objects.currentObjects['Gain Bode'] != undefined) {
|
if(Objects.currentObjects['Gain Bode'] !== undefined) {
|
||||||
console.log('Recalculating cache gain')
|
console.log('Recalculating cache gain')
|
||||||
// Minimum to draw (can be expended if needed, just not infinite or it'll cause issues.
|
// Minimum to draw (can be expended if needed, just not infinite or it'll cause issues.
|
||||||
var drawMin = 0.001
|
let drawMin = 0.001
|
||||||
|
|
||||||
var baseY = 0
|
let baseY = 0
|
||||||
var om0xGains = {1000000000: 0} // To draw the last part
|
let om0xGains = {1000000000: 0} // To draw the last part
|
||||||
var om0xPass = {1000000000: 'high'} // To draw the last part
|
let om0xPass = {1000000000: 'high'} // To draw the last part
|
||||||
Objects.currentObjects['Gain Bode'].forEach(function(gainObj) { // Sorting by their om_0 position.
|
for(/** @type {GainBode} */ let gainObj of Objects.currentObjects['Gain Bode']) { // Sorting by their om_0 position.
|
||||||
var om0x = gainObj.om_0.x.execute()
|
let om0x = gainObj.om_0.x.execute()
|
||||||
if(om0xGains[om0x] == undefined) {
|
if(om0xGains[om0x] === undefined) {
|
||||||
om0xGains[om0x] = gainObj.gain.execute()
|
om0xGains[om0x] = gainObj.gain.execute()
|
||||||
om0xPass[om0x] = gainObj.pass == 'high'
|
om0xPass[om0x] = gainObj.pass === 'high'
|
||||||
} else {
|
} else {
|
||||||
om0xGains[om0x+0.001] = gainObj.gain.execute()
|
om0xGains[om0x+0.001] = gainObj.gain.execute()
|
||||||
om0xPass[om0x+0.001] = gainObj.pass == 'high'
|
om0xPass[om0x+0.001] = gainObj.pass === 'high'
|
||||||
}
|
}
|
||||||
baseY += gainObj.execute(drawMin)
|
baseY += gainObj.execute(drawMin)
|
||||||
})
|
}
|
||||||
// Sorting the om_0x
|
// Sorting the om_0x
|
||||||
var om0xList = Object.keys(om0xGains).map(x => parseFloat(x)) // THEY WERE CONVERTED TO STRINGS...
|
let om0xList = Object.keys(om0xGains).map(x => parseFloat(x)) // THEY WERE CONVERTED TO STRINGS...
|
||||||
om0xList.sort((a,b) => a - b)
|
om0xList.sort((a,b) => a - b)
|
||||||
// Adding the total gains.
|
// Adding the total gains.
|
||||||
var gainsBeforeP = []
|
let gainsBeforeP = []
|
||||||
var gainsAfterP = []
|
let gainsAfterP = []
|
||||||
var gainTotal = 0
|
let gainTotal = 0
|
||||||
for(var om0x of om0xList){
|
for(let om0x of om0xList){
|
||||||
if(om0xPass[om0x]) { // High-pass
|
if(om0xPass[om0x]) { // High-pass
|
||||||
gainsBeforeP.push(om0xGains[om0x])
|
gainsBeforeP.push(om0xGains[om0x])
|
||||||
gainsAfterP.push(0)
|
gainsAfterP.push(0)
|
||||||
|
@ -119,10 +114,10 @@ class SommeGainsBode extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Calculating parts
|
// Calculating parts
|
||||||
var previousPallier = drawMin
|
let previousPallier = drawMin
|
||||||
for(var pallier = 0; pallier < om0xList.length; pallier++) {
|
for(let pallier = 0; pallier < om0xList.length; pallier++) {
|
||||||
var dbfn = new MathLib.Expression(`${gainTotal}*(ln(x)-ln(${previousPallier}))/ln(10)+${baseY}`)
|
let dbfn = new Expression(`${gainTotal}*(ln(x)-ln(${previousPallier}))/ln(10)+${baseY}`)
|
||||||
var inDrawDom = MathLib.parseDomain(`]${previousPallier};${om0xList[pallier]}]`)
|
let inDrawDom = parseDomain(`]${previousPallier};${om0xList[pallier]}]`)
|
||||||
this.cachedParts.push([dbfn, inDrawDom])
|
this.cachedParts.push([dbfn, inDrawDom])
|
||||||
previousPallier = om0xList[pallier]
|
previousPallier = om0xList[pallier]
|
||||||
baseY = dbfn.execute(om0xList[pallier])
|
baseY = dbfn.execute(om0xList[pallier])
|
||||||
|
@ -131,13 +126,13 @@ class SommeGainsBode extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
if(this.cachedParts.length > 0) {
|
if(this.cachedParts.length > 0) {
|
||||||
for(var [dbfn, inDrawDom] of this.cachedParts) {
|
for(let [dbfn, inDrawDom] of this.cachedParts) {
|
||||||
F.Function.drawFunction(canvas, ctx, dbfn, inDrawDom, MathLib.Domain.R)
|
Function.drawFunction(canvas, dbfn, inDrawDom, Domain.R)
|
||||||
if(inDrawDom.includes(this.labelX)) {
|
if(inDrawDom.includes(this.labelX)) {
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,16 +16,14 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import { executeExpression, Expression } from "../mathlib.mjs"
|
||||||
|
import * as P from "../parameters.mjs"
|
||||||
|
import Objects from "../objects.mjs"
|
||||||
|
import Latex from "../math/latex.mjs"
|
||||||
|
|
||||||
.import "common.js" as Common
|
import { ExecutableObject } from "common.mjs"
|
||||||
.import "../objects.js" as Objects
|
|
||||||
.import "../mathlib.js" as MathLib
|
|
||||||
.import "../parameters.js" as P
|
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
export default class SommePhasesBode extends ExecutableObject {
|
||||||
class SommePhasesBode extends Common.ExecutableObject {
|
|
||||||
static type(){return 'Somme phases Bode'}
|
static type(){return 'Somme phases Bode'}
|
||||||
static displayType(){return qsTr('Bode Phases Sum')}
|
static displayType(){return qsTr('Bode Phases Sum')}
|
||||||
static displayTypeMultiple(){return qsTr('Bode Phases Sum')}
|
static displayTypeMultiple(){return qsTr('Bode Phases Sum')}
|
||||||
|
@ -44,10 +42,6 @@ class SommePhasesBode extends Common.ExecutableObject {
|
||||||
this.recalculateCache()
|
this.recalculateCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
export() {
|
|
||||||
return [this.name, this.visible, this.color.toString(), this.labelContent, this.labelPosition, this.labelX]
|
|
||||||
}
|
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
return `${this.name} = ${Objects.getObjectsName('Phase Bode').join(' + ')}`
|
return `${this.name} = ${Objects.getObjectsName('Phase Bode').join(' + ')}`
|
||||||
}
|
}
|
||||||
|
@ -57,18 +51,18 @@ class SommePhasesBode extends Common.ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x=1) {
|
execute(x=1) {
|
||||||
if(typeof x == 'string') x = MathLib.executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
for(var i = 0; i < this.om0xList.length-1; i++) {
|
for(let i = 0; i < this.om0xList.length-1; i++) {
|
||||||
if(x >= this.om0xList[i] && x < this.om0xList[i+1]) return this.phasesList[i]
|
if(x >= this.om0xList[i] && x < this.om0xList[i+1]) return this.phasesList[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
simplify(x = 1) {
|
simplify(x = 1) {
|
||||||
var xval = x
|
let xval = x
|
||||||
if(typeof x == 'string') xval = MathLib.executeExpression(x)
|
if(typeof x == 'string') xval = executeExpression(x)
|
||||||
for(var i = 0; i < this.om0xList.length-1; i++) {
|
for(let i = 0; i < this.om0xList.length-1; i++) {
|
||||||
if(xval >= this.om0xList[i] && xval < this.om0xList[i+1]) {
|
if(xval >= this.om0xList[i] && xval < this.om0xList[i+1]) {
|
||||||
return (new MathLib.Expression(this.phasesExprList[i])).simplify()
|
return (new Expression(this.phasesExprList[i])).simplify()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return '0'
|
return '0'
|
||||||
|
@ -80,20 +74,20 @@ class SommePhasesBode extends Common.ExecutableObject {
|
||||||
|
|
||||||
recalculateCache() {
|
recalculateCache() {
|
||||||
// Minimum to draw (can be expended if needed, just not infinite or it'll cause issues.
|
// Minimum to draw (can be expended if needed, just not infinite or it'll cause issues.
|
||||||
var drawMin = 0.001
|
let drawMin = 0.001
|
||||||
var drawMax = 100000
|
let drawMax = 100000
|
||||||
this.om0xList = [drawMin, drawMax]
|
this.om0xList = [drawMin, drawMax]
|
||||||
this.phasesList = [0]
|
this.phasesList = [0]
|
||||||
this.phasesExprList = ['0']
|
this.phasesExprList = ['0']
|
||||||
var phasesDict = {}
|
let phasesDict = {}
|
||||||
var phasesExprDict = {}
|
let phasesExprDict = {}
|
||||||
phasesDict[drawMax] = 0
|
phasesDict[drawMax] = 0
|
||||||
|
|
||||||
if(Objects.currentObjects['Phase Bode'] != undefined) {
|
if(Objects.currentObjects['Phase Bode'] !== undefined) {
|
||||||
console.log('Recalculating cache phase')
|
console.log('Recalculating cache phase')
|
||||||
for(var obj of Objects.currentObjects['Phase Bode']) {
|
for(/** @type {PhaseBode} */ let obj of Objects.currentObjects['Phase Bode']) {
|
||||||
this.om0xList.push(obj.om_0.x.execute())
|
this.om0xList.push(obj.om_0.x.execute())
|
||||||
if(phasesDict[obj.om_0.x.execute()] == undefined) {
|
if(phasesDict[obj.om_0.x.execute()] === undefined) {
|
||||||
phasesDict[obj.om_0.x.execute()] = obj.phase.execute()
|
phasesDict[obj.om_0.x.execute()] = obj.phase.execute()
|
||||||
phasesExprDict[obj.om_0.x.execute()] = obj.phase.toEditableString()
|
phasesExprDict[obj.om_0.x.execute()] = obj.phase.toEditableString()
|
||||||
} else {
|
} else {
|
||||||
|
@ -104,26 +98,26 @@ class SommePhasesBode extends Common.ExecutableObject {
|
||||||
this.phasesExprList[0] += '+' + obj.om_0.y.toEditableString()
|
this.phasesExprList[0] += '+' + obj.om_0.y.toEditableString()
|
||||||
}
|
}
|
||||||
this.om0xList.sort((a,b) => a - b)
|
this.om0xList.sort((a,b) => a - b)
|
||||||
var totalAdded = this.phasesList[0]
|
let totalAdded = this.phasesList[0]
|
||||||
for(var i = 1; i < this.om0xList.length; i++) {
|
for(let i = 1; i < this.om0xList.length; i++) {
|
||||||
this.phasesList[i] = this.phasesList[i-1] + phasesDict[this.om0xList[i]]
|
this.phasesList[i] = this.phasesList[i-1] + phasesDict[this.om0xList[i]]
|
||||||
this.phasesExprList[i] = this.phasesExprList[i-1] + '+' + phasesDict[this.om0xList[i]]
|
this.phasesExprList[i] = this.phasesExprList[i-1] + '+' + phasesDict[this.om0xList[i]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
for(var i = 0; i < this.om0xList.length-1; i++) {
|
for(let i = 0; i < this.om0xList.length-1; i++) {
|
||||||
var om0xBegin = canvas.x2px(this.om0xList[i])
|
let om0xBegin = canvas.x2px(this.om0xList[i])
|
||||||
var om0xEnd = canvas.x2px(this.om0xList[i+1])
|
let om0xEnd = canvas.x2px(this.om0xList[i+1])
|
||||||
var baseY = canvas.y2px(this.phasesList[i])
|
let baseY = canvas.y2px(this.phasesList[i])
|
||||||
var nextY = canvas.y2px(this.phasesList[i+1])
|
let nextY = canvas.y2px(this.phasesList[i+1])
|
||||||
canvas.drawLine(ctx, om0xBegin, baseY, om0xEnd, baseY)
|
canvas.drawLine(om0xBegin, baseY, om0xEnd, baseY)
|
||||||
canvas.drawLine(ctx, om0xEnd, baseY, om0xEnd, nextY)
|
canvas.drawLine(om0xEnd, baseY, om0xEnd, nextY)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,16 +16,14 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import { Expression } from "../mathlib.mjs"
|
||||||
|
import * as P from "../parameters.mjs"
|
||||||
|
import Latex from "../math/latex.mjs"
|
||||||
|
|
||||||
.import "common.js" as Common
|
import { API as Common, DrawableObject } from "common.mjs"
|
||||||
.import "../mathlib.js" as MathLib
|
|
||||||
.import "../parameters.js" as P
|
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
|
||||||
|
export default class Text extends DrawableObject {
|
||||||
class Text extends Common.DrawableObject {
|
|
||||||
static type(){return 'Text'}
|
static type(){return 'Text'}
|
||||||
static displayType(){return qsTr('Text')}
|
static displayType(){return qsTr('Text')}
|
||||||
static displayTypeMultiple(){return qsTr('Texts')}
|
static displayTypeMultiple(){return qsTr('Texts')}
|
||||||
|
@ -34,7 +32,7 @@ class Text extends Common.DrawableObject {
|
||||||
[QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(),
|
[QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(),
|
||||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning,
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning,
|
||||||
[QT_TRANSLATE_NOOP('prop','text')]: 'string',
|
[QT_TRANSLATE_NOOP('prop','text')]: 'string',
|
||||||
'comment1': QT_TRANSLATE_NOOP(
|
'comment1': QT_TRANSLATE_NOOP(
|
||||||
'comment',
|
'comment',
|
||||||
'If you have latex enabled, you can use use latex markup in between $$ to create equations.'
|
'If you have latex enabled, you can use use latex markup in between $$ to create equations.'
|
||||||
),
|
),
|
||||||
|
@ -45,9 +43,9 @@ class Text extends Common.DrawableObject {
|
||||||
x = 1, y = 0, labelPosition = 'center', text = 'New text', disableLatex = false) {
|
x = 1, y = 0, labelPosition = 'center', text = 'New text', disableLatex = false) {
|
||||||
if(name == null) name = Common.getNewName('t')
|
if(name == null) name = Common.getNewName('t')
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
if(typeof x == 'number' || typeof x == 'string') x = new MathLib.Expression(x.toString())
|
if(typeof x == 'number' || typeof x == 'string') x = new Expression(x.toString())
|
||||||
this.x = x
|
this.x = x
|
||||||
if(typeof y == 'number' || typeof y == 'string') y = new MathLib.Expression(y.toString())
|
if(typeof y == 'number' || typeof y == 'string') y = new Expression(y.toString())
|
||||||
this.y = y
|
this.y = y
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
this.text = text
|
this.text = text
|
||||||
|
@ -80,10 +78,6 @@ class Text extends Common.DrawableObject {
|
||||||
return `${Latex.variable(this.name)} = "\\textsf{${this.latexMarkupText()}}"`
|
return `${Latex.variable(this.name)} = "\\textsf{${this.latexMarkupText()}}"`
|
||||||
}
|
}
|
||||||
|
|
||||||
export() {
|
|
||||||
return [this.name, this.visible, this.color.toString(), this.labelContent, this.x.toEditableString(), this.y.toEditableString(), this.labelPosition, this.text, this.disableLatex]
|
|
||||||
}
|
|
||||||
|
|
||||||
getLabel() {
|
getLabel() {
|
||||||
return this.text
|
return this.text
|
||||||
}
|
}
|
||||||
|
@ -92,9 +86,9 @@ class Text extends Common.DrawableObject {
|
||||||
return `\\textsf{${this.latexMarkupText()}}`
|
return `\\textsf{${this.latexMarkupText()}}`
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
let yOffset = this.disableLatex ? canvas.textsize-4 : 0
|
let yOffset = this.disableLatex ? canvas.textsize-4 : 0
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())+yOffset, this.disableLatex)
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())+yOffset, this.disableLatex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2023 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,23 +16,21 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.pragma library
|
import { Expression } from "../mathlib.mjs"
|
||||||
|
import * as P from "../parameters.mjs"
|
||||||
|
import Latex from "../math/latex.mjs"
|
||||||
|
import Objects from "../objects.mjs"
|
||||||
|
|
||||||
.import "common.js" as Common
|
import { API as Common, DrawableObject } from "common.mjs"
|
||||||
.import "../objects.js" as Objects
|
|
||||||
.import "../mathlib.js" as MathLib
|
|
||||||
.import "../parameters.js" as P
|
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
|
||||||
|
export default class XCursor extends DrawableObject {
|
||||||
class XCursor extends Common.DrawableObject {
|
|
||||||
static type(){return 'X Cursor'}
|
static type(){return 'X Cursor'}
|
||||||
static displayType(){return qsTr('X Cursor')}
|
static displayType(){return qsTr('X Cursor')}
|
||||||
static displayTypeMultiple(){return qsTr('X Cursors')}
|
static displayTypeMultiple(){return qsTr('X Cursors')}
|
||||||
static properties() {return {
|
static properties() {return {
|
||||||
[QT_TRANSLATE_NOOP('prop','x')]: 'Expression',
|
[QT_TRANSLATE_NOOP('prop','x')]: new P.Expression(),
|
||||||
[QT_TRANSLATE_NOOP('prop','targetElement')]: new P.ObjectType('ExecutableObject'),
|
[QT_TRANSLATE_NOOP('prop','targetElement')]: new P.ObjectType('ExecutableObject', true),
|
||||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
[QT_TRANSLATE_NOOP('prop','approximate')]: 'boolean',
|
[QT_TRANSLATE_NOOP('prop','approximate')]: 'boolean',
|
||||||
[QT_TRANSLATE_NOOP('prop','rounding')]: 'number',
|
[QT_TRANSLATE_NOOP('prop','rounding')]: 'number',
|
||||||
|
@ -51,7 +49,7 @@ class XCursor extends Common.DrawableObject {
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
this.approximate = approximate
|
this.approximate = approximate
|
||||||
this.rounding = rounding
|
this.rounding = rounding
|
||||||
if(typeof x == 'number' || typeof x == 'string') x = new MathLib.Expression(x.toString())
|
if(typeof x == 'number' || typeof x == 'string') x = new Expression(x.toString())
|
||||||
this.x = x
|
this.x = x
|
||||||
this.targetElement = targetElement
|
this.targetElement = targetElement
|
||||||
if(typeof targetElement == "string") {
|
if(typeof targetElement == "string") {
|
||||||
|
@ -62,12 +60,6 @@ class XCursor extends Common.DrawableObject {
|
||||||
this.targetValuePosition = targetValuePosition
|
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() {
|
getReadableString() {
|
||||||
if(this.targetElement == null) return `${this.name} = ${this.x.toString()}`
|
if(this.targetElement == null) return `${this.name} = ${this.x.toString()}`
|
||||||
return `${this.name} = ${this.x.toString()}\n${this.getTargetValueLabel()}`
|
return `${this.name} = ${this.x.toString()}\n${this.getTargetValueLabel()}`
|
||||||
|
@ -93,8 +85,8 @@ class XCursor extends Common.DrawableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
getTargetValueLatexLabel() {
|
getTargetValueLatexLabel() {
|
||||||
var t = this.targetElement
|
let t = this.targetElement
|
||||||
var approx = ''
|
let approx = ''
|
||||||
if(this.approximate) {
|
if(this.approximate) {
|
||||||
approx = t.execute(this.x.execute())
|
approx = t.execute(this.x.execute())
|
||||||
approx = approx.toPrecision(this.rounding + Math.round(approx).toString().length)
|
approx = approx.toPrecision(this.rounding + Math.round(approx).toString().length)
|
||||||
|
@ -144,39 +136,35 @@ class XCursor extends Common.DrawableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
let xpos = canvas.x2px(this.x.execute())
|
let xpos = canvas.x2px(this.x.execute())
|
||||||
switch(this.displayStyle) {
|
switch(this.displayStyle) {
|
||||||
case '— — — — — — —':
|
case '— — — — — — —':
|
||||||
var dashPxSize = 10
|
canvas.drawDashedLine(xpos, 0, xpos, canvas.height, 20)
|
||||||
for(var i = 0; i < canvas.canvasSize.height; i += dashPxSize*2)
|
|
||||||
canvas.drawLine(ctx, xpos, i, xpos, i+dashPxSize)
|
|
||||||
break;
|
break;
|
||||||
case '⸺⸺⸺⸺⸺⸺':
|
case '⸺⸺⸺⸺⸺⸺':
|
||||||
canvas.drawXLine(ctx, this.x.execute())
|
canvas.drawXLine(this.x.execute())
|
||||||
break;
|
break;
|
||||||
case '• • • • • • • • • •':
|
case '• • • • • • • • • •':
|
||||||
var pointDistancePx = 10
|
let pointDistancePx = 10
|
||||||
var pointSize = 2
|
let pointSize = 2
|
||||||
ctx.beginPath();
|
for(let i = 0; i < canvas.height; i += pointDistancePx)
|
||||||
for(var i = 0; i < canvas.canvasSize.height; i += pointDistancePx)
|
canvas.disc(xpos, i, pointSize)
|
||||||
ctx.ellipse(xpos-pointSize/2, i-pointSize/2, pointSize, pointSize)
|
|
||||||
ctx.fill();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drawing label at the top of the canvas.
|
// Drawing label at the top of the canvas.
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, xpos, 0, false, null, null,
|
this.drawLabel(canvas, this.labelPosition, xpos, 0, false, null, null,
|
||||||
(x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, 5, ltxImg.width, ltxImg.height),
|
(x,y,ltxImg) => canvas.drawVisibleImage(ltxImg.source, x, 5, ltxImg.width, ltxImg.height),
|
||||||
(x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, textSize.height+5))
|
(x,y,text,textSize) => canvas.drawVisibleText(text, x, textSize.height+5))
|
||||||
|
|
||||||
// Drawing label at the position of the target element.
|
// Drawing label at the position of the target element.
|
||||||
if(this.targetValuePosition == 'Next to target' && this.targetElement != null) {
|
if(this.targetValuePosition === 'Next to target' && this.targetElement != null) {
|
||||||
let ypos = canvas.y2px(this.targetElement.execute(this.x.execute()))
|
let ypos = canvas.y2px(this.targetElement.execute(this.x.execute()))
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, xpos, ypos, false,
|
this.drawLabel(canvas, this.labelPosition, xpos, ypos, false,
|
||||||
this.getTargetValueLatexLabel.bind(this), this.getTargetValueLabel.bind(this),
|
this.getTargetValueLatexLabel.bind(this), this.getTargetValueLabel.bind(this),
|
||||||
(x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, y, ltxImg.width, ltxImg.height),
|
(x,y,ltxImg) => canvas.drawVisibleImage(ltxImg.source, x, y, ltxImg.width, ltxImg.height),
|
||||||
(x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, y+textSize.height))
|
(x,y,text,textSize) => canvas.drawVisibleText(text, x, y+textSize.height))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|