diff --git a/.gitignore b/.gitignore index e2308a5..1a8dac4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,46 +1,33 @@ -# Building build/ dist/ deb_dist/ -assets/linux/flatpak/AppDir -assets/linux/flatpak/repo -assets/linux/flatpak/build-dir -assets/linux/flatpak/.flatpak-builder +linux/flatpak/AppDir +linux/flatpak/repo +linux/flatpak/build-dir +linux/flatpak/.flatpak-builder *.snap *.spec *.zip -*.tar.gz -*.spec -*.egg-info/ - -# Runtime data **/**.qmlc **/**.jsc **/**.pyc **/**.qm -**/**.log *.jsc *.qmlc -*.log -**/*.dxvk-cache .DS_Store **/.DS_Store **/__pycache__/ - -# IDE Data .ropeproject .vscode -*.kdev4 -.kdev4 +build docs/html .directory +*.kdev4 *.lpf *.lgg - -# Tests -common/coverage/ -**/.coverage - -# npm -common/node_modules -runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/index.mjs* +*.spec +.kdev4 +AccountFree.pro +AccountFree.pro.user +*.egg-info/ +*.tar.gz diff --git a/.gitmodules b/.gitmodules index 042c634..8c591d4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ -[submodule "runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/MixedMenu"] - path = runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/MixedMenu +[submodule "LogarithmPlotter/qml/eu/ad5001/MixedMenu"] + path = LogarithmPlotter/qml/eu/ad5001/MixedMenu url = https://git.ad5001.eu/Ad5001/MixedMenu +[submodule "linux/flatpak"] + path = linux/flatpak + url = https://github.com/Ad5001/eu.ad5001.LogarithmPlotter diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a756a3..d7e37d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,212 +1,5 @@ # Changelog -## v0.6.0 (?? Apr 2025) - -**New** - - * Revamped the update greet screan. - * Settings now have their dedicated configuration screen. - * Default settings for graphs can now be configured. - * LaTeX rendering now support both concurent and (experimental) threaded rendering, making graph render up to 20x times faster. - * Added a loading screen for the threaded LaTeX renderer. - -**Changes** - - * Settings menu removed, use `Edit > Preferences` to access LogarithmPlotter's settings. - * Logarithmic graduation limit upped from 10^20 to 10^309 (and conversively). - * Error messages should now all be properly translated. - * LaTeX renders are now persistantly cached. Closing and reopening files will be instantaneous. - * Special character choosing popup is now keyboard navigable and no longer covers the input. - * Domains are now rendered with LaTeX. - -**Fixed bugs** - - * Fixed sequence rendering method. - * Picker's 'snap to grid' option caused issues when picking. - * Outzooming with mousescroll no longer prevents you from being able to zoom back in. - * Several bugs related to X Cursors, namely certain function causing LogarithmPlotter to crash, and rounding errors. - * When using derivatives, the value passed as parameter is now properly rendered instead of a placeholder `x`. - * LaTeX renderer now uses native square root function look instead of `sqrt` function. - * LaTeX renderer now properly renders pi as π. - * Improved the stability of complex function drawing. - * (Linux) Certain links in 'About' were broken. - -**Translations** - - * New translation in Tamil (thanks @TamilNeram!) - * Completed Spanish translation (thanks @gallegonovato and @IngrownMink4!) - * We're still looking for Norwegian speakers who could help complete the [translation](https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/nb_NO/). - * If your language is not yet supported by LogarithmPlotter, feel free to add it on [Weblate](https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/)! - -**Internal changes** - - * The near entirety of the logic was refractored from QML JS to modern ECMAScript (added Babel and rollup as dependancies). - * PySide6 and ES codebases were completely separated (in preparation of a web-based renderer). - * Added Python unit tests (complete). - * Added ES unit tests (WIP). - * Internal tooling for translations was overalled to allow lupdate to parse newer ES syntax. - * Flushed a great many deals of hacks and bugs this way. - * Type safety is now properly enforced at every step of IO. - * Several new test files were added in the `ci` folder. - * Updated to PySide6 v6.9.0 - -## 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) - -**Changes** - - * Fully ported to PySide6 (Qt6). - * Greet screen settings are now scrollable. - * Changelog is now freezed to current version. - -**New** - - * Customizable color schemes for expressions. - * New, rewamped and improved picked location overlay settings: - * It's now possible to disable picking x or y when setting a location. - * Properties which are related to positioning (X, Y, Label's X position) can now be set using the picker. - * Visual redesign that enhances readability of settings. - * There is now a button to hide picker settings. - -**Fixed bugs** - - * Cursors in expression are now easier to see. - * Symbols in LaTeX rendered Texts cause the LaTeX renderer to crash. - * Underscores in distribution names are automatically removed if the name is modified. - * Autocomplete categories now properly respect theme colors. - * Functions in expressions (like indexOf, map...) now properly send errors when the arguments are of the wrong type or count. - * Executable Objects called (like functions, bode magnitures, phases...) now send an error if provided with no arguments. - * Function calls with no argument no longer make LogarithmPlotter crash under certain circumstances. - * Thank you dialog's lists are no longer draggable. - -**Internal changes** - - * A lot of inner changes led by porting to Qt6, fixing a lot of bugs at the same time. - * Disabled auto detect of visual theme if the `QT_QUICK_CONTROLS_STYLE` environment variable is set. - * (macOS, Windows, Flatpak) Drastically reducing installer sizes (more than halved). - * (Launchpad/Ubuntu) Using custom built packages of PySide6, meaning smaller installation and distro dependency. - -## v0.3.0 (28 Oct 2022) - -**New** - - * New completely revamped expression editor: - * Automatic closing of parentheses and brackets (can be disabled in settings). - * Syntax highlighting (can be disabled in the settings). - * Autocompletion is now available (for functions, variables and constants, object names and properties) (can be disabled in the settings). - * Object properties can now be used in expressions (e.g. if you have a point named A, you can use A.x to access its x value). - * Executable objects can be now be used in expressions (e.g. if you have a function named 'f', it's accessible using `f()`). - * LaTeX-rendered formulas are now used in the Objects and History tabs when LaTeX rendering is enabled. - * Errors in formulas are now reported in message boxes. - -**Changes** - - * The Object Editor dialog has been completely reworked internally, resulting in notable performance improvements. - * Vast improvements to the objects system: names can no longer be shared amongst different objects. - * Disabled access to custom variable and function definition in expressions (can cause issues and vulnerabilities) - * When using the set position cursor, the position change is now saved a single history action. - * Distribution are now prefixed with an 'F_' to prevent confusion with X Cursors. - -**Added translations** - - * Autocompletion categories (English, French, German, Hungarian). - * Expression editor settings (English, French, German, Hungarian). - * Expression syntax errors (English, French, German, Hungarian). - * On top of the above: - * Hungarian: v0.2.0 added text (thanks @ovari!) - * Spanish: Menu bars (thanks @Sergio Varela) - * You can contribute to translation on [Weblate](https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/#repository). - -**Fixed bugs** - - * Fixing Texts not being properly recognized as texts when saving. - * Text's 'Disable LaTeX' property is now properly saved. - * X Cursors LaTeX rendering made the app crash. - * Attempting to insert special character no longer automatically saves the expression you're editing. - * Proper HDPI support for icons and buttons (note: HDPI is not available for the rendered canvas yet). - * Support for non-latin characters in variables (e.g. greek letters, subtext, suptext) - * Silent error when misentering variable names in the expression editor causing internal issues. - * Fixing some utils function simplifying parentheses when they shouldn't have. - * (flatpak and KDE SDK) Fixing the sometimes invisible buttons on the objects tab on startup. - * (macos) Application string version does not match LogarithmPlotter's version. - * (debian) (Normally) Fixing deb building. - -**Internal changes** - - * Object dependencies are now registered on both the dependant object, and the object it's depending on. - * Objects now have a proper per-name registry. - * Object Editor Dialog has been reworked to use loaders insteads. - * Reworked the file loading system to be able to load dependencies properly. - - -## v0.2.0 (22 Apr 2022) - -**New** - - * (EXPERIMENTAL) LogarithmPlotter now has an optional LaTeX integration. - * It requires a LaTeX installation, including `latexmk` and `dvipng` available in the PATH. - * NOTE: LaTeX support is disabled by default and is only for working for the rendering on the graph. - * NOTE: The objects and history tab still use the legacy text based expression rendering. - * Thanks and contributions dialog, showing included libraries and translations, their license and author(s). - * LaTeX rendering can be disabled for texts, even if LaTeX is enabled. - -**Changes** - - * History re/undos only redraw the graph every 4 change at most in order to speed up the process when re/undoing a lot of changes. - * Gradients are no longer hidden when filtered out in the history tab. - -**Added translations** - - * LaTeX options and error messages - * Thanks and contribution dialog - * New option for text. - * Fixed translation of "repartition" which should be "distribution" in certain remaining strings. - -**Fixed bugs** - - * (macos) #1 - Opening files don't work on compiled versions of LogarithmPlotter on MacOS - * (snapcraft) Fixed bug preventing from launching LogarithmPlotter. This fix has been backported to v0.1.8. - * (snapcraft) Files are now properly opened. - * (snapcraft) Added changelog support. - -**Internal changes** - - * Moved python modules to "util" directory for more clarity. - * Moved flatpak metainfo to eu.ad5001.LogarithmPlotter repository. - * Componented the Mathlib library in order to have a more readable source. - * Added documentation for most internal JavaScript modules. - * Merge label drawing methods due to it's complexity. - * (flatpak) Updated SDK version to v5.15-21.08. - ## v0.1.8 (19 Feb 2022) **New** diff --git a/runtime-pyside6/LogarithmPlotter/__init__.py b/LogarithmPlotter/__init__.py similarity index 88% rename from runtime-pyside6/LogarithmPlotter/__init__.py rename to LogarithmPlotter/__init__.py index 08914d3..69c5368 100644 --- a/runtime-pyside6/LogarithmPlotter/__init__.py +++ b/LogarithmPlotter/__init__.py @@ -1,6 +1,6 @@ """ * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -17,19 +17,19 @@ """ from shutil import which -__VERSION__ = "0.6.0" +__VERSION__ = "0.2.0" is_release = False + # Check if development version, if so get the date of the latest git patch # and append it to the version string. if not is_release and which('git') is not None: from os.path import realpath, join, dirname, exists from subprocess import check_output from datetime import datetime - # Command to check date of latest git commit cmd = ['git', 'log', '--format=%ci', '-n 1'] - cwd = realpath(join(dirname(__file__), '..', '..', '..')) # Root LogarithmPlotter directory. + cwd = realpath(join(dirname(__file__), '..')) # Root AccountFree directory. if exists(join(cwd, '.git')): date_str = check_output(cmd, cwd=cwd).decode('utf-8').split(' ')[0] try: @@ -39,3 +39,6 @@ if not is_release and which('git') is not None: # Date cannot be parsed, not git root? pass +if __name__ == "__main__": + from .logarithmplotter import run + run() diff --git a/runtime-pyside6/tests/globals.py b/LogarithmPlotter/__main__.py similarity index 86% rename from runtime-pyside6/tests/globals.py rename to LogarithmPlotter/__main__.py index 67a1356..e679d61 100644 --- a/runtime-pyside6/tests/globals.py +++ b/LogarithmPlotter/__main__.py @@ -1,20 +1,20 @@ """ * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 - * + * Copyright (C) 2022 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 . """ -from LogarithmPlotter.logarithmplotter import create_qapp - -app = create_qapp() \ No newline at end of file +if __name__ == "__main__": + from .logarithmplotter import run + run() diff --git a/LogarithmPlotter/i18n/lp_de.ts b/LogarithmPlotter/i18n/lp_de.ts new file mode 100644 index 0000000..27f5bca --- /dev/null +++ b/LogarithmPlotter/i18n/lp_de.ts @@ -0,0 +1,1127 @@ + + + + + About + + + + About LogarithmPlotter + Über LogarithmPlotter + + + + LogarithmPlotter v%1 + LogarithmPlotter v%1 + + + + 2D plotter software to make BODE plots, sequences and repartition functions. + 2D-Grafiksoftware zur Erstellung von Bode-Diagramms, Folgen und Verteilungsfunktionen. + + + + Report a bug + Bug melden + + + + Official website + Offizielle Website + + + + AppMenuBar + + + &File + &Datei + + + + &Load... + &Laden… + + + + &Save + &Speichern + + + + Save &As... + Speichern &Unter… + + + + &Quit + &Ausfahrt + + + + &Edit + &Bearbeiten + + + + &Undo + &Lösen + + + + &Redo + &Wiederherstellen + + + + &Copy plot + Grafik &Kopieren + + + + &Create + &Erstellen + + + + &Settings + &Einstellungen + + + + Check for updates on startup + Beim Starten auf Updates prüfen + + + + Reset redo stack automaticly + Wiederherstellen-Stapel automatisch zurücksetzen + + + + Enable LaTeX rendering + + + + + &Help + &Hilfe + + + + &Source code + &Quellcode + + + + &Report a bug + Fehler &Melden + + + + &User manual + &Benutzerhandbuch + + + + &Changelog + &Changelog + + + + &Help translating! + &Hilfe beim Übersetzen! + + + + &About + &Übrigens + + + + Save unsaved changes? + Änderungen speichern? + + + + This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? + Diese Grafik enthält ungespeicherte Änderungen. Dadurch gehen alle ungespeicherten Daten verloren. Fortfahren? + + + + Changelog + + + Fetching changelog... + Changelog abrufen… + + + + Done + Schließen + + + + EditorDialog + + + Edit properties of %1 %2 + Eigenschaften von %1 %2 bearbeiten + + + + Name + Name + + + + Label content + Etikett + + + + null + leer + + + + name + Name + + + + name + value + Name + Wert + + + + + + Create new %1 + + Neues %1objekt erstellen + + + + FileDialog + + + Export Logarithm Plot file + Logarithmusgrafik exportieren + + + + Import Logarithm Plot file + Logarithmusgrafik importieren + + + + GreetScreen + + + Welcome to LogarithmPlotter + Willkommen bei LogarithmPlotter + + + + Version %1 + Version %1 + + + + Take a few seconds to configure LogarithmPlotter. +These settings can be changed at any time from the "Settings" menu. + Nehmen Sie sich ein paar Sekunden Zeit, um LogarithmPlotter zu konfigurieren. +Diese Einstellungen können jederzeit über das Menü "Einstellungen" geändert werden. + + + + Check for updates on startup (requires online connectivity) + Beim Start nach Updates suchen (Online-Verbindung erforderlich) + + + + Reset redo stack when a new action is added to history + Redo-Stapel zurücksetzen, wenn eine neue Aktion zur Historie hinzugefügt wird + + + + Enable LaTeX rendering + + + + + User manual + Benutzerhandbuch + + + + Changelog + Changelog + + + + Done + Schließen + + + + HistoryBrowser + + + Filter... + Filtern… + + + + Redo > + Wiederherstellen > + + + + > Now + > Aktueller Stand + + + + < Undo + < Rückgängig + + + + ListSetting + + + + Add Entry + + Neuer Eintrag + + + + LogarithmPlotter + + + Objects + Objekte + + + + Settings + Einstellungen + + + + History + Verlauf + + + + Saved plot to '%1'. + Gespeicherte Grafik auf '%1'. + + + + Loading file '%1'. + Laden der Datei '%1'. + + + + Unknown object type: %1. + Unbekannter Objekttyp: %1. + + + + Invalid file provided. + Ungültige Datei angegeben. + + + + Could not save file: + Die Datei konnte nicht gespeichert werden: + + + + Loaded file '%1'. + Geladene Datei '%1'. + + + + Copied plot screenshot to clipboard! + Grafik in die Zwischenablage kopiert! + + + + &Update + &Aktualisieren + + + + &Update LogarithmPlotter + LogarithmPlotter &aktualisieren + + + + ObjectCreationGrid + + + + Create new: + + Neu erstellen: + + + + ObjectLists + + + Hide all %1 + Alle %1 ausblenden + + + + Show all %1 + Alle %1 anzeigen + + + + Hide %1 %2 + Ausblenden %1 %2 + + + + Show %1 %2 + Anzeigen %1 %2 + + + + Set %1 %2 position + Position von %1 %2 einstellen + + + + Delete %1 %2 + %1 %2 löschen + + + + Pick new color for %1 %2 + Neue Farbe für %1 %2 auswählen + + + + PickLocationOverlay + + + Pointer precision: + Genauigkeit des Zeigers: + + + + Snap to grid + Am Gitter einrasten + + + + Settings + + + X Zoom + Zoom auf X + + + + Y Zoom + Zoom auf Y + + + + Min X + Minimum X + + + + Max Y + Maximum Y + + + + Max X + Maximum X + + + + Min Y + Minimum Y + + + + X Axis Step + X-Achsen-Schritt + + + + Y Axis Step + Y-Achsen-Schritt + + + + Line width + Linienbreite + + + + Text size (px) + Textgröße (px) + + + + X Label + Etikett der X-Achse + + + + Y Label + Etikett der Y-Achse + + + + X Log scale + Logarithmische Skala in X + + + + Show X graduation + X-Teilung anzeigen + + + + Show Y graduation + Y-Teilung anzeigen + + + + Copy to clipboard + Kopieren in die Zwischenablage + + + + Save plot + Grafik speichern + + + + Save plot as + Grafik speichern unter + + + + Load plot + Grafik laden + + + + changelog + + + Could not fetch changelog: Server error {}. + Changelog konnte nicht geholt werden: Server-Fehler {}. + + + + Could not fetch update: {}. + Changelog konnte nicht geholt werden: {}. + + + + color + + + + %1 %2's color changed from %3 to %4. + %1 %2 wurde von %3 bis %4 umgefärbt. + + + + comment + + + Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} + Beispiel: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ-*), ]0;1[, {3;4;5} + + + + The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) + Die folgenden Parameter werden verwendet, wenn der Definitionsbereich eine nicht kontinuierliche Menge ist. (Beispiel: ℕ, ℤ, Mengen wie {0;3}...) + + + + Note: Specify the probability for each value. + Hinweis: Geben Sie die Wahrscheinlichkeit für jeden Wert an. + + + + Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... + Hinweis: Verwenden Sie %1[n], um sich auf %1ₙ zu beziehen, %1[n+1] für %1ₙ₊₁… + + + + If you have latex enabled, you can use use latex markup in between $$ to create equations. + + + + + control + + + + + + %1: + %1: + + + + create + + + + New %1 %2 created. + Neu %1 %2 erstellt. + + + + delete + + + + %1 %2 deleted. + %1 %2 gelöscht. + + + + editproperty + + + %1 of %2 %3 changed from "%4" to "%5". + %1 von %2 %3 wurde von "%4" auf "%5" geändert. + + + + %1 of %2 changed from %3 to %4. + %1 von %2 wurde von %3 auf %4 geändert. + + + + function + + + Function + Funktion + + + + Functions + Funktionen + + + + gainbode + + + Bode Magnitude + Bode-Magnitude + + + + Bode Magnitudes + Bode-Magnituden + + + + + low-pass + Tiefpass + + + + + high-pass + Hochpass + + + + historylib + + New %1 %2 created. + Neu %1 %2 erstellt. + + + %1 %2 deleted. + %1 %2 gelöscht. + + + %1 of %2 %3 changed from "%4" to "%5". + %1 von %2 %3 wurde von "%4" auf "%5" geändert. + + + %1 %2 shown. + %1 %2 angezeigt. + + + %1 %2 hidden. + %1 %2 ausgeblendet. + + + Name of %1 %2 changed to %3. + Der Name von %1 %2 wurde in %3 geändert. + + + + latex + + + No Latex installation found. +If you already have a latex distribution installed, make sure it's installed on your path. +Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. + + + + + DVIPNG was not found. Make sure you include it from your Latex distribution. + + + + + An exception occured within the creation of the latex formula. +Process '{}' ended with a non-zero return code {}: + +{} +Please make sure your latex installation is correct and report a bug if so. + + + + + An exception occured within the creation of the latex formula. +Process '{}' took too long to finish: +{} +Please make sure your latex installation is correct and report a bug if so. + + + + + name + + + + %1 %2 renamed to %3. + %1 %2 umbenannt in %3. + + + + parameters + + + above + ↑ Über + + + + below + ↓ Unter + + + + + left + ← Links + + + + + right + → Rechts + + + + above-left + ↖ Oben links + + + + above-right + ↗ Oben rechts + + + + below-left + ↙ Unten links + + + + below-right + ↘ Unten rechts + + + + center + >|< Zentrum + + + + top + ↑ Über + + + + bottom + ↓ Unter + + + + top-left + ↖ Oben links + + + + top-right + ↗ Oben rechts + + + + bottom-left + ↙ Unten links + + + + bottom-right + ↘ Unten rechts + + + + application + Anwendung + + + + function + Funktion + + + + high + Hoch + + + + low + Tief + + + + Next to target + Neben dem Ziel + + + + With label + Mit Etikett + + + + Hidden + Versteckt + + + + phasebode + + + Bode Phase + Bode-Phase + + + + Bode Phases + Bode-Phasen + + + + point + + + Point + Punkt + + + + Points + Punkte + + + + prop + + + expression + Ausdruck + + + + definitionDomain + Definitionsbereich + + + + destinationDomain + Reichweite + + + + + + + + + + + + + labelPosition + Position des Etiketts + + + + displayMode + Anzeigemodus + + + + + + + + + + labelX + X-Position des Etiketts + + + + + drawPoints + Unentschiedene Punkte + + + + + drawDashedLines + Gestrichelte Linien anzeigen + + + + + om_0 + ω₀ + + + + pass + Pass + + + + gain + Größenordnung + + + + omGraduation + Teilung auf ω zeigen + + + + phase + Phase + + + + unit + Einheit + + + + + + x + X + + + + + y + Y + + + + pointStyle + Punkt-Stil + + + + probabilities + Wahrscheinlichkeiten + + + + text + Inhalt + + + + disableLatex + + + + + targetElement + Zielobjekt + + + + approximate + Ungefähren Wert anzeigen + + + + rounding + Rundung + + + + displayStyle + Stil + + + + targetValuePosition + Wertposition des Ziels + + + + defaultExpression + Standardausdruck + + + + baseValues + Initialisierungswerte + + + color + Farbe + + + + repartition + + + Repartition + Verteilungsfunktion + + + + Repartition functions + Verteilungsfunktionen + + + + sequence + + + Sequence + Folge + + + + Sequences + Folgen + + + + sommegainsbode + + + + Bode Magnitudes Sum + Bode-Magnituden Summe + + + + sommephasesbode + + + + Bode Phases Sum + Bode-Phasen Summe + + + + text + + + Text + Text + + + + Texts + Texte + + + + update + + + An update for LogarithPlotter (v{}) is available. + Ein Aktualisierung für LogarithmPlotter (v{}) ist verfügbar. + + + + No update available. + Keine Aktualisierung verfügbar. + + + + Could not fetch update information: Server error {}. + Es konnten keine Aktualisierungsinformationen abgerufen werden: Server-Fehler {}. + + + + Could not fetch update information: {}. + Es konnten keine Aktualisierungsinformationen abgerufen werden:{}. + + + + visibility + + + + %1 %2 shown. + %1 %2 angezeigt. + + + + + %1 %2 hidden. + %1 %2 ausgeblendet. + + + + xcursor + + + X Cursor + X Zeiger + + + + X Cursors + X Zeiger + + + diff --git a/LogarithmPlotter/i18n/lp_en.ts b/LogarithmPlotter/i18n/lp_en.ts new file mode 100644 index 0000000..6d6083f --- /dev/null +++ b/LogarithmPlotter/i18n/lp_en.ts @@ -0,0 +1,1136 @@ + + + + + About + + + + About LogarithmPlotter + About LogarithmPlotter + + + + LogarithmPlotter v%1 + LogarithmPlotter v%1 + + + + 2D plotter software to make BODE plots, sequences and repartition functions. + 2D plotter software to make Bode plots, sequences and distribution functions. + + + + Report a bug + Report a bug + + + + Official website + Official website + + + + AppMenuBar + + + &File + &File + + + + &Load... + &Open… + + + + &Save + &Save + + + + Save &As... + Save &As… + + + + &Quit + &Quit + + + + &Edit + &Edit + + + + &Undo + &Undo + + + + &Redo + &Redo + + + + &Copy plot + &Copy plot + + + + &Create + &Create + + + + &Settings + &Settings + + + + Check for updates on startup + Check for updates on startup + + + + Reset redo stack automaticly + Reset redo stack automatically + + + + Enable LaTeX rendering + Enable LaTeX rendering + + + + &Help + &Help + + + + &Source code + &Source code + + + + &Report a bug + &Report a bug + + + + &User manual + &User manual + + + + &Changelog + &Changelog + + + + &Help translating! + &Help translating! + + + + &About + &About + + + + Save unsaved changes? + Save unsaved changes? + + + + This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? + This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? + + + + Changelog + + + Fetching changelog... + Fetching changelog… + + + + Done + Done + + + + EditorDialog + + + Edit properties of %1 %2 + Edit properties of %1 %2 + + + + Name + Name + + + + Label content + Label content + + + + null + null + + + + name + name + + + + name + value + name + value + + + + + + Create new %1 + + Create new %1 + + + + FileDialog + + + Export Logarithm Plot file + Export Logarithm Plot file + + + + Import Logarithm Plot file + Import Logarithm Plot file + + + + GreetScreen + + + Welcome to LogarithmPlotter + Welcome to LogarithmPlotter + + + + Version %1 + Version %1 + + + + Take a few seconds to configure LogarithmPlotter. +These settings can be changed at any time from the "Settings" menu. + Take a few seconds to configure LogarithmPlotter. +These settings can be changed at any time from the "Settings" menu. + + + + Check for updates on startup (requires online connectivity) + Check for updates on startup (requires online connectivity) + + + + Reset redo stack when a new action is added to history + Reset redo stack when a new action is added to history + + + + Enable LaTeX rendering + Enable LaTeX rendering + + + + User manual + User manual + + + + Changelog + Changelog + + + + Done + Done + + + + HistoryBrowser + + + Filter... + Filter… + + + + Redo > + Redo > + + + + > Now + > Now + + + + < Undo + < Undo + + + + ListSetting + + + + Add Entry + + Add Entry + + + + LogarithmPlotter + + + Objects + Objects + + + + Settings + Settings + + + + History + History + + + + Saved plot to '%1'. + Saved plot to '%1'. + + + + Loading file '%1'. + Loading file '%1'. + + + + Unknown object type: %1. + Unknown object type: %1. + + + + Invalid file provided. + Invalid file provided. + + + + Could not save file: + Could not save file: + + + + Loaded file '%1'. + Loaded file '%1'. + + + + Copied plot screenshot to clipboard! + Copied plot screenshot to clipboard! + + + + &Update + &Update + + + + &Update LogarithmPlotter + &Update LogarithmPlotter + + + + ObjectCreationGrid + + + + Create new: + + Create new: + + + + ObjectLists + + + Hide all %1 + Hide all %1 + + + + Show all %1 + Show all %1 + + + + Hide %1 %2 + Hide %1 %2 + + + + Show %1 %2 + Show %1 %2 + + + + Set %1 %2 position + Set %1 %2 position + + + + Delete %1 %2 + Delete %1 %2 + + + + Pick new color for %1 %2 + Pick new color for %1 %2 + + + + PickLocationOverlay + + + Pointer precision: + Pointer precision: + + + + Snap to grid + Snap to grid + + + + Settings + + + X Zoom + X Zoom + + + + Y Zoom + Y Zoom + + + + Min X + Min X + + + + Max Y + Max Y + + + + Max X + Max X + + + + Min Y + Min Y + + + + X Axis Step + X Axis Step + + + + Y Axis Step + Y Axis Step + + + + Line width + Line width + + + + Text size (px) + Text size (px) + + + + X Label + X Label + + + + Y Label + Y Label + + + + X Log scale + X Log scale + + + + Show X graduation + Show X graduation + + + + Show Y graduation + Show Y graduation + + + + Copy to clipboard + Copy to clipboard + + + + Save plot + Save plot + + + + Save plot as + Save plot as + + + + Load plot + Open plot + + + + changelog + + + Could not fetch changelog: Server error {}. + Could not fetch changelog: Server error {}. + + + + Could not fetch update: {}. + Could not fetch changelog: {}. + + + + color + + + + %1 %2's color changed from %3 to %4. + %1 %2's color changed from %3 to %4. + + + + comment + + + Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} + Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} + + + + The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) + The following parameters are used when the domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}…) + + + + Note: Specify the probability for each value. + Note: Specify the probability for each value. + + + + Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... + Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁… + + + + 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. + + + + control + + + + + + %1: + %1: + + + + create + + + + New %1 %2 created. + New %1 %2 created. + + + + delete + + + + %1 %2 deleted. + %1 %2 deleted. + + + + editproperty + + + %1 of %2 %3 changed from "%4" to "%5". + %1 of %2 %3 changed from "%4" to "%5". + + + + %1 of %2 changed from %3 to %4. + %1 of %2 changed from %3 to %4. + + + + function + + + Function + Function + + + + Functions + Functions + + + + gainbode + + + Bode Magnitude + Bode Magnitude + + + + Bode Magnitudes + Bode Magnitudes + + + + + low-pass + low-pass + + + + + high-pass + high-pass + + + + historylib + + New %1 %2 created. + New %1 %2 created. + + + %1 %2 deleted. + %1 %2 deleted. + + + %1 of %2 %3 changed from "%4" to "%5". + %1 of %2 %3 changed from "%4" to "%5". + + + %1 %2 shown. + %1 %2 shown. + + + %1 %2 hidden. + %1 %2 hidden. + + + Name of %1 %2 changed to %3. + Name of %1 %2 changed to %3. + + + + latex + + + No Latex installation found. +If you already have a latex distribution installed, make sure it's installed on your path. +Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. + No Latex installation found. +If you already have a latex distribution installed, make sure it's installed on your path. +Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. + + + + DVIPNG was not found. Make sure you include it from your Latex distribution. + DVIPNG was not found. Make sure you include it from your Latex distribution. + + + + An exception occured within the creation of the latex formula. +Process '{}' ended with a non-zero return code {}: + +{} +Please make sure your latex installation is correct and report a bug if so. + An exception occurred within the creation of the latex formula. +Process '{}' ended with a non-zero return code {}: + +{} +Please make sure your latex installation is correct and report a bug if so. + + + + An exception occured within the creation of the latex formula. +Process '{}' took too long to finish: +{} +Please make sure your latex installation is correct and report a bug if so. + An exception occurred within the creation of the latex formula. +Process '{}' took too long to finish: +{} +Please make sure your latex installation is correct and report a bug if so. + + + + name + + + + %1 %2 renamed to %3. + %1 %2 renamed to %3. + + + + parameters + + + above + ↑ Above + + + + below + ↓ Below + + + + + left + ← Left + + + + + right + → Right + + + + above-left + ↖ Above left + + + + above-right + ↗ Above right + + + + below-left + ↙ Below left + + + + below-right + ↘ Below right + + + + center + >|< Center + + + + top + ↑ Top + + + + bottom + ↓ Bottom + + + + top-left + ↖ Top left + + + + top-right + ↗ Top right + + + + bottom-left + ↙ Bottom left + + + + bottom-right + ↘ Bottom right + + + + application + Application + + + + function + Function + + + + high + High + + + + low + Low + + + + Next to target + Next to target + + + + With label + With label + + + + Hidden + Hidden + + + + phasebode + + + Bode Phase + Bode Phase + + + + Bode Phases + Bode Phases + + + + point + + + Point + Point + + + + Points + Points + + + + prop + + + expression + Expression + + + + definitionDomain + Domain + + + + destinationDomain + Range + + + + + + + + + + + + + labelPosition + Label position + + + + displayMode + Display mode + + + + + + + + + + labelX + Label's X position + + + + + drawPoints + Show points + + + + + drawDashedLines + Show dashed lines + + + + + om_0 + ω₀ + + + + pass + Pass + + + + gain + Magnitude gain + + + + omGraduation + Show graduation on ω₀ + + + + phase + Phase + + + + unit + Unit to use + + + + + + x + X + + + + + y + Y + + + + pointStyle + Point style + + + + probabilities + Probabilities list + + + + text + Content + + + + disableLatex + Disable latex rendering for this text + + + + targetElement + Object to target + + + + approximate + Show approximate value + + + + rounding + Rounding + + + + displayStyle + Display style + + + + targetValuePosition + Target's value position + + + + defaultExpression + Default expression + + + + baseValues + Initialisation values + + + color + Color + + + + repartition + + + Repartition + Distribution + + + + Repartition functions + Distribution functions + + + + sequence + + + Sequence + Sequence + + + + Sequences + Sequences + + + + sommegainsbode + + + + Bode Magnitudes Sum + Bode Magnitudes Sum + + + + sommephasesbode + + + + Bode Phases Sum + Bode Phases Sum + + + + text + + + Text + Text + + + + Texts + Texts + + + + update + + + An update for LogarithPlotter (v{}) is available. + An update for LogarithmPlotter (v{}) is available. + + + + No update available. + No update available. + + + + Could not fetch update information: Server error {}. + Could not fetch update information: Server error {}. + + + + Could not fetch update information: {}. + Could not fetch update information: {}. + + + + visibility + + + + %1 %2 shown. + %1 %2 shown. + + + + + %1 %2 hidden. + %1 %2 hidden. + + + + xcursor + + + X Cursor + X Cursor + + + + X Cursors + X Cursors + + + diff --git a/LogarithmPlotter/i18n/lp_es.ts b/LogarithmPlotter/i18n/lp_es.ts new file mode 100644 index 0000000..d17ef46 --- /dev/null +++ b/LogarithmPlotter/i18n/lp_es.ts @@ -0,0 +1,1095 @@ + + + + + About + + + + About LogarithmPlotter + Sobre LogarithmPlotter + + + + LogarithmPlotter v%1 + LogarithmPlotter v%1 + + + + 2D plotter software to make BODE plots, sequences and repartition functions. + Software de trazado 2D para diagramas de Bode, secuencias y funciones de distribución. + + + + Report a bug + Informar de un error + + + + Official website + Sitio web oficial + + + + AppMenuBar + + + &File + &Archivo + + + + &Load... + &Abrir… + + + + &Save + &Guardar + + + + Save &As... + Guardar &como… + + + + &Quit + &Salida + + + + &Edit + &Editar + + + + &Undo + &Cancelar + + + + &Redo + &Reiniciar + + + + &Copy plot + &Copiar el gráfico + + + + &Create + &Crear + + + + &Settings + &Ajustes + + + + Check for updates on startup + + + + + Reset redo stack automaticly + + + + + Enable LaTeX rendering + + + + + &Help + + + + + &Source code + + + + + &Report a bug + + + + + &User manual + + + + + &Changelog + + + + + &Help translating! + + + + + &About + + + + + Save unsaved changes? + + + + + This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? + + + + + Changelog + + + Fetching changelog... + + + + + Done + + + + + EditorDialog + + + Edit properties of %1 %2 + + + + + Name + + + + + Label content + + + + + null + + + + + name + + + + + name + value + + + + + + + Create new %1 + + + + + FileDialog + + + Export Logarithm Plot file + + + + + Import Logarithm Plot file + + + + + GreetScreen + + + Welcome to LogarithmPlotter + + + + + Version %1 + + + + + Take a few seconds to configure LogarithmPlotter. +These settings can be changed at any time from the "Settings" menu. + + + + + Check for updates on startup (requires online connectivity) + + + + + Reset redo stack when a new action is added to history + + + + + Enable LaTeX rendering + + + + + User manual + + + + + Changelog + + + + + Done + + + + + HistoryBrowser + + + Filter... + + + + + Redo > + + + + + > Now + + + + + < Undo + + + + + ListSetting + + + + Add Entry + + + + + LogarithmPlotter + + + Objects + + + + + Settings + + + + + History + + + + + Saved plot to '%1'. + + + + + Loading file '%1'. + + + + + Unknown object type: %1. + + + + + Invalid file provided. + + + + + Could not save file: + + + + + Loaded file '%1'. + + + + + Copied plot screenshot to clipboard! + + + + + &Update + + + + + &Update LogarithmPlotter + + + + + ObjectCreationGrid + + + + Create new: + + + + + ObjectLists + + + Hide all %1 + + + + + Show all %1 + + + + + Hide %1 %2 + + + + + Show %1 %2 + + + + + Set %1 %2 position + + + + + Delete %1 %2 + + + + + Pick new color for %1 %2 + + + + + PickLocationOverlay + + + Pointer precision: + + + + + Snap to grid + + + + + Settings + + + X Zoom + + + + + Y Zoom + + + + + Min X + + + + + Max Y + + + + + Max X + + + + + Min Y + + + + + X Axis Step + + + + + Y Axis Step + + + + + Line width + + + + + Text size (px) + + + + + X Label + + + + + Y Label + + + + + X Log scale + + + + + Show X graduation + + + + + Show Y graduation + + + + + Copy to clipboard + + + + + Save plot + + + + + Save plot as + + + + + Load plot + + + + + changelog + + + Could not fetch changelog: Server error {}. + + + + + Could not fetch update: {}. + + + + + color + + + + %1 %2's color changed from %3 to %4. + + + + + comment + + + Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} + + + + + The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) + + + + + Note: Specify the probability for each value. + + + + + Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... + + + + + If you have latex enabled, you can use use latex markup in between $$ to create equations. + + + + + control + + + + + + %1: + + + + + create + + + + New %1 %2 created. + + + + + delete + + + + %1 %2 deleted. + + + + + editproperty + + + %1 of %2 %3 changed from "%4" to "%5". + + + + + %1 of %2 changed from %3 to %4. + + + + + function + + + Function + + + + + Functions + + + + + gainbode + + + Bode Magnitude + + + + + Bode Magnitudes + + + + + + low-pass + + + + + + high-pass + + + + + latex + + + No Latex installation found. +If you already have a latex distribution installed, make sure it's installed on your path. +Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. + + + + + DVIPNG was not found. Make sure you include it from your Latex distribution. + + + + + An exception occured within the creation of the latex formula. +Process '{}' ended with a non-zero return code {}: + +{} +Please make sure your latex installation is correct and report a bug if so. + + + + + An exception occured within the creation of the latex formula. +Process '{}' took too long to finish: +{} +Please make sure your latex installation is correct and report a bug if so. + + + + + name + + + + %1 %2 renamed to %3. + + + + + parameters + + + above + + + + + below + + + + + + left + + + + + + right + + + + + above-left + + + + + above-right + + + + + below-left + + + + + below-right + + + + + center + + + + + top + + + + + bottom + + + + + top-left + + + + + top-right + + + + + bottom-left + + + + + bottom-right + + + + + application + + + + + function + + + + + high + + + + + low + + + + + Next to target + + + + + With label + + + + + Hidden + + + + + phasebode + + + Bode Phase + + + + + Bode Phases + + + + + point + + + Point + + + + + Points + + + + + prop + + + expression + + + + + definitionDomain + + + + + destinationDomain + + + + + + + + + + + + + + labelPosition + + + + + displayMode + + + + + + + + + + + labelX + + + + + + drawPoints + + + + + + drawDashedLines + + + + + + om_0 + + + + + pass + + + + + gain + + + + + omGraduation + + + + + phase + + + + + unit + + + + + + + x + + + + + + y + + + + + pointStyle + + + + + probabilities + + + + + text + + + + + disableLatex + + + + + targetElement + + + + + approximate + + + + + rounding + + + + + displayStyle + + + + + targetValuePosition + + + + + defaultExpression + + + + + baseValues + + + + + repartition + + + Repartition + + + + + Repartition functions + + + + + sequence + + + Sequence + + + + + Sequences + + + + + sommegainsbode + + + + Bode Magnitudes Sum + + + + + sommephasesbode + + + + Bode Phases Sum + + + + + text + + + Text + + + + + Texts + + + + + update + + + An update for LogarithPlotter (v{}) is available. + + + + + No update available. + + + + + Could not fetch update information: Server error {}. + + + + + Could not fetch update information: {}. + + + + + visibility + + + + %1 %2 shown. + + + + + + %1 %2 hidden. + + + + + xcursor + + + X Cursor + + + + + X Cursors + + + + diff --git a/LogarithmPlotter/i18n/lp_fr.ts b/LogarithmPlotter/i18n/lp_fr.ts new file mode 100644 index 0000000..32830c4 --- /dev/null +++ b/LogarithmPlotter/i18n/lp_fr.ts @@ -0,0 +1,1145 @@ + + + + + About + + + + About LogarithmPlotter + À propos de LogarithmPlotter + + + + LogarithmPlotter v%1 + LogarithmPlotter v%1 + + + + 2D plotter software to make BODE plots, sequences and repartition functions. + Logiciel de traçage 2D pour les diagrammes de Bode, les suites et les fonctions de répartition. + + + + Report a bug + Rapport de bug + + + + Official website + Site officiel + + + + AppMenuBar + + + &File + &Fichier + + + + &Load... + &Ouvrir… + + + + &Save + &Sauvegarder + + + + Save &As... + Sauvegarde &Sous… + + + + &Quit + &Quitter + + + + &Edit + &Édition + + + + &Undo + &Annuler + + + + &Redo + &Rétablir + + + + &Copy plot + &Copier le graphe + + + + &Create + &Créer + + + + &Settings + &Paramètres + + + + Check for updates on startup + Vérifier la présence de mise à jour au démarrage + + + + Reset redo stack automaticly + Légèrement long, et pas forcément très compréhensible. + Réinitialiser la pile d'action "Rétablir" automatiquement + + + + Enable LaTeX rendering + + + + + &Help + &Aide + + + + &Source code + &Code source + + + + &Report a bug + &Rapport de bug + + + + &User manual + Manuel d'&utilisation + + + + &Changelog + &Historique des modifications + + + + &Help translating! + &Aider à la traduction ! + + + + &About + &À propos + + + + Save unsaved changes? + Sauvegarder les modifications ? + + + + This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? + Ce graphe contient des modifications non sauvegardées. En faisant cela, toutes les données non sauvegardées seront perdues. Continuer ? + + + + Changelog + + + Fetching changelog... + Récupération de l'historique des modifications… + + + + Done + Fermer + + + + EditorDialog + + + Edit properties of %1 %2 + Changer les propriétés de %1 %2 + + + + Name + Nom + + + + Label content + Étiquette + + + + null + vide + + + + name + nom + + + + name + value + nom + valeur + + + + + + Create new %1 + Traduction non litéralle pour éviter les problèmes de genre. + + Créer un nouvel objet %1 + + + + FileDialog + + + Export Logarithm Plot file + Exporter le graphe Logarithmique + + + + Import Logarithm Plot file + Importer un graphe Logarithmique + + + + GreetScreen + + + Welcome to LogarithmPlotter + Bienvenue sur LogarithmPlotter + + + + Version %1 + Version %1 + + + + Take a few seconds to configure LogarithmPlotter. +These settings can be changed at any time from the "Settings" menu. + Prenez quelques secondes pour configurer LogarithmPlotter. +Ces paramètres peuvent être modifiés à tout moment à partir du menu "Paramètres". + + + + Enable LaTeX rendering + + + + + User manual + Manuel d'utilisation + + + + Changelog + Historique des modifications + + + + Done + Fermer + + + Take a few seconds to configure LogarithmPlotter. +These settings can always be changed at any time from the "Settings" menu. + Take a few seconds to configure LogarithmPlotter. +These settings can always be changed at any time from the "Settings" menu. + + + + Check for updates on startup (requires online connectivity) + Vérifier les mises à jour au démarrage (nécessite d'être connecté à internet) + + + + Reset redo stack when a new action is added to history + Réinitialiser la pile d'action "Rétablir" lorsqu'une nouvelle action est ajoutée à l'historique + + + + HistoryBrowser + + + Filter... + Filtrer… + + + + Redo > + Rétablir > + + + + > Now + > État actuel + + + + < Undo + < Annuler + + + + ListSetting + + + + Add Entry + + Nouvelle entrée + + + + LogarithmPlotter + + + Objects + Objets + + + + Settings + Paramètres + + + + History + Historique + + + + Saved plot to '%1'. + Graphe sauvegardé dans '%1'. + + + + Loading file '%1'. + Chargement du fichier '%1'. + + + + Unknown object type: %1. + Type d'objet inconnu : %1. + + + + Invalid file provided. + Fichier fourni invalide. + + + + Could not save file: + Impossible de sauvegarder le fichier : + + + + Loaded file '%1'. + Fichier '%1' chargé. + + + + Copied plot screenshot to clipboard! + Image du graphe copiée dans le presse-papiers ! + + + + &Update + &Mise à jour + + + + &Update LogarithmPlotter + &Mettre à jour LogarithmPlotter + + + + ObjectCreationGrid + + + + Create new: + + Créer : + + + + ObjectLists + + + Hide all %1 + Cacher tous les %1 + + + + Show all %1 + Montrer tous les %1 + + + + Hide %1 %2 + Cacher l'objet %1 %2 + + + + Show %1 %2 + Montrer l'objet %1 %2 + + + + Set %1 %2 position + Définir la position de l'objet %1 %2 + + + + Delete %1 %2 + Supprimer l'objet %1 %2 + + + + Pick new color for %1 %2 + Choisissez une nouvelle couleur pour %1 %2 + + + + PickLocationOverlay + + + Pointer precision: + Précision du pointeur : + + + + Snap to grid + Placement sur la grille + + + + Settings + + + X Zoom + Zoom en X + + + + Y Zoom + Zoom en Y + + + + Min X + Min X + + + + Max Y + Max Y + + + + Max X + Max X + + + + Min Y + Min Y + + + + X Axis Step + Pas de l'axe X + + + + Y Axis Step + Pas de l'axe Y + + + + Line width + Taille des lignes + + + + Text size (px) + Taille du texte (px) + + + + X Label + Label de l'axe X + + + + Y Label + Label de l'axe Y + + + + X Log scale + Échelle logarithmique en X + + + + Show X graduation + Montrer la graduation de l'axe X + + + + Show Y graduation + Montrer la graduation de l'axe Y + + + + Copy to clipboard + Copier vers le presse-papiers + + + + Save plot + Sauvegarder le graphe + + + + Save plot as + Sauvegarder le graphe sous + + + + Load plot + Ouvrir un graphe + + + + changelog + + + Could not fetch changelog: Server error {}. + Impossible de récupérer l'historique des modifications : Erreur de serveur {}. + + + + Could not fetch update: {}. + Impossible de récupérer l'historique des modifications : {}. + + + + color + + + + %1 %2's color changed from %3 to %4. + %1 %2 a été re colorisé du %3 au %4. + + + + comment + + + Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} + Par exemple : R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} + + + + The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) + Les paramètres suivants sont utilisés lorsque le domaine de définition est un ensemble non-continu. (Ex : ℕ, ℤ, des ensembles comme {0;3}…) + + + + Note: Specify the probability for each value. + Note : Spécifiez la probabilité pour chaque valeur. + + + + Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... + Note : Utilisez %1[n] pour faire référence à %1ₙ, %1[n+1] pour %1ₙ₊₁... + Note : Utilisez %1[n] pour faire référence à %1ₙ, %1[n+1] pour %1ₙ₊₁… + + + + If you have latex enabled, you can use use latex markup in between $$ to create equations. + + + + + control + + + + + + %1: + %1 : + + + + create + + + + New %1 %2 created. + Nouvel objet %1 %2 créé. + + + + delete + + + + %1 %2 deleted. + %1 %2 supprimé(e). + + + + editproperty + + + %1 of %2 %3 changed from "%4" to "%5". + %1 de %2 %3 modifiée de "%4" à "%5". + + + + %1 of %2 changed from %3 to %4. + %1 de %2 modifiée de %3 à %4. + + + + function + + + Function + Fonction + + + + Functions + Fonctions + + + + gainbode + + + Bode Magnitude + Gain de Bode + + + + Bode Magnitudes + Gains de Bode + + + + + low-pass + passe-bas + + + + + high-pass + passe-haut + + + + historylib + + New %1 %2 created. + Nouvel objet %1 %2 créé. + + + %1 %2 deleted. + %1 %2 supprimé(e). + + + %1 of %2 %3 changed from "%4" to "%5". + %1 de %2 %3 modifiée de "%4" à "%5". + + + %1 %2 shown. + %1 %2 affiché(e). + + + %1 %2 hidden. + %1 %2 cachée(e). + + + Name of %1 %2 changed to %3. + Le nom de %1 %2 a été changé en %3. + + + + latex + + + No Latex installation found. +If you already have a latex distribution installed, make sure it's installed on your path. +Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. + Aucune installation de Latex trouvée. +Si vous avez déjà installé une distribution Latex, assurez-vous qu'elle est installée sur votre PATH. +Sinon, vous pouvez télécharger une distribution Latex comme TeX Live à l'adresse https://tug.org/texlive/. + + + + DVIPNG was not found. Make sure you include it from your Latex distribution. + DVIPNG n'a pas été trouvé. Assurez-vous de l'inclure dans votre distribution Latex. + + + + An exception occured within the creation of the latex formula. +Process '{}' ended with a non-zero return code {}: + +{} +Please make sure your latex installation is correct and report a bug if so. + Une exception s'est produite lors de la création de la formule latex. +Le processus '{}' s'est terminé par un code de retour non nul {} : + +{} +Vérifiez que votre installation de latex est correcte et signalez un bogue si c'est le cas. + + + + An exception occured within the creation of the latex formula. +Process '{}' took too long to finish: +{} +Please make sure your latex installation is correct and report a bug if so. + Une exception s'est produite lors de la création de la formule latex. +Le processus '{}' a mis trop de temps à se terminer : +{} +Vérifiez que votre installation de latex est correcte et signalez un bogue si c'est le cas. + + + + name + + + + %1 %2 renamed to %3. + %1 %2 renommé(e) en %3. + + + + parameters + + + above + ↑ Au dessus + + + + below + ↓ En dessous + + + + + left + ← À gauche + + + + + right + → À droite + + + + above-left + ↖ Au dessus à gauche + + + + above-right + ↗ Au dessus à droite + + + + below-left + ↙ En dessous à gauche + + + + below-right + ↘ En dessous à droite + + + + center + >|< Centré + + + + top + ↑ Au dessus + + + + bottom + ↓ En dessous + + + + top-left + ↖ Au dessus à gauche + + + + top-right + ↗ Au dessus à droite + + + + bottom-left + ↙ En dessous à gauche + + + + bottom-right + ↘ En dessous à droite + + + + application + Application + + + + function + Fonction + + + + high + Haut + + + + low + Bas + + + + Next to target + A côté de la cible + + + + With label + Avec l'étiquette + + + + Hidden + Caché + + + + phasebode + + + Bode Phase + Phase de Bode + + + + Bode Phases + Phases de Bode + + + + point + + + Point + Point + + + + Points + Points + + + + prop + + + expression + Expression + + + + definitionDomain + Domaine de définition + + + + destinationDomain + Portée + + + + + + + + + + + + + labelPosition + Position de l'étiquette + + + + displayMode + Mode d'affichage + + + + + + + + + + labelX + Position en X de l'étiquette + + + + + drawPoints + Afficher les points + + + + + drawDashedLines + Afficher les pointillés + + + + + om_0 + ω₀ + + + + pass + Passe + + + + gain + Gain + + + + omGraduation + Afficher la graduation sur ω₀ + + + + phase + Phase + + + + unit + Unité de la phase + + + + + + x + X + + + + + y + Y + + + + pointStyle + Style du point + + + + probabilities + Liste de probabilités + + + + text + Contenu + + + + disableLatex + Désactiver le rendu latex pour ce texte + + + + targetElement + Objet à cibler + + + + approximate + Afficher la valeur approximative + + + + rounding + Arrondi + + + + displayStyle + Style d'affichage + + + + targetValuePosition + Position de la valeur de la cible + + + + defaultExpression + Expression + + + + baseValues + Valeurs d'initialisation + + + color + Couleur + + + + repartition + + + Repartition + Répartition + + + + Repartition functions + Fonctions de répartition + + + + sequence + + + Sequence + Suite + + + + Sequences + Suites + + + + sommegainsbode + + + + Bode Magnitudes Sum + Sommes des gains de Bode + + + + sommephasesbode + + + + Bode Phases Sum + Somme des phases de Bode + + + + text + + + Text + Texte + + + + Texts + Textes + + + + update + + + An update for LogarithPlotter (v{}) is available. + Une mise à jour de LogarithmPlotter (v{}) est disponible. + + + + No update available. + À jour. + + + + Could not fetch update information: Server error {}. + Impossible de récupérer les informations de mise à jour. Erreur du serveur {}. + + + + Could not fetch update information: {}. + Impossible de récupérer les informations de mise à jour. {}. + + + + visibility + + + + %1 %2 shown. + %1 %2 affiché(e). + + + + + %1 %2 hidden. + %1 %2 cachée(e). + + + + xcursor + + + X Cursor + Curseur X + + + + X Cursors + Curseurs X + + + diff --git a/LogarithmPlotter/i18n/lp_hu.ts b/LogarithmPlotter/i18n/lp_hu.ts new file mode 100644 index 0000000..91ba500 --- /dev/null +++ b/LogarithmPlotter/i18n/lp_hu.ts @@ -0,0 +1,1119 @@ + + + + + About + + + + About LogarithmPlotter + LogarithmPlotter névjegye + + + + LogarithmPlotter v%1 + LogarithmPlotter %1 verzió + + + + 2D plotter software to make BODE plots, sequences and repartition functions. + Síkbeli ábrázolásszoftver Bode-ábrák, sorozatok és eloszlási funkciók készítéséhez. + + + + Report a bug + Hiba bejelentése + + + + Official website + + + + + AppMenuBar + + + &File + &Fájl + + + + &Load... + &Betöltés… + + + + &Save + &Mentés + + + + Save &As... + Me&ntés másként… + + + + &Quit + &Kilépés + + + + &Edit + S&zerkesztés + + + + &Undo + &Visszavonás + + + + &Redo + &Ismétlés + + + + &Copy plot + Ábra má&solása + + + + &Create + &Létrehozás + + + + &Settings + &Beállítások + + + + Check for updates on startup + Frissítések keresése indításkor + + + + Reset redo stack automaticly + Ismétlési verem önműködő visszaállítása + + + + Enable LaTeX rendering + + + + + &Help + &Súgó + + + + &Source code + &Forráskód + + + + &Report a bug + &Hiba bejelentése + + + + &User manual + + + + + &Changelog + &Változásnapló + + + + &Help translating! + &Segítség a fordításban! + + + + &About + &Névjegy + + + + Save unsaved changes? + Menti a változtatásokat? + + + + This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? + Ez az ábra nem mentett változtatásokat tartalmaz. Ezzel az összes nem mentett adat elveszik. Folytatja? + + + + Changelog + + + Fetching changelog... + Változásnapló lekérése… + + + + Done + Kész + + + + EditorDialog + + + Edit properties of %1 %2 + %1 %2 tulajdonságainak szerkesztése + + + + Name + Név + + + + Label content + Címke tartalom + + + + null + üres + + + + name + név + + + + name + value + név + érték + + + + + + Create new %1 + + Új %1 létrehozása + + + + FileDialog + + + Export Logarithm Plot file + Logaritmus-ábra-fájl exportálása + + + + Import Logarithm Plot file + Logaritmus-ábra-fájl importálása + + + + GreetScreen + + + Welcome to LogarithmPlotter + Isten hozott a LogarithmPlotter! + + + + Version %1 + %1 verzió + + + + Take a few seconds to configure LogarithmPlotter. +These settings can be changed at any time from the "Settings" menu. + Szánjon néhány másodpercet a LogarithmPlotter beállításához. +Ezek a beállítások bármikor módosíthatók a „Beállítások” menüben. + + + + Check for updates on startup (requires online connectivity) + Frissítések keresése indításkor (online kapcsolat szükséges) + + + + Reset redo stack when a new action is added to history + Ismétlési verem alaphelyzet visszaállítása, ha új műveletet adnak az előzményekhez + + + + Enable LaTeX rendering + + + + + User manual + + + + + Changelog + Változásnapló + + + + Done + Kész + + + + HistoryBrowser + + + Filter... + + + + + Redo > + Ismétlés > + + + + > Now + > Most + + + + < Undo + < Visszavonás + + + + ListSetting + + + + Add Entry + + Bejegyzés hozzáadása + + + + LogarithmPlotter + + + Objects + Tárgyak + + + + Settings + Beállítások + + + + History + Előzmények + + + + Saved plot to '%1'. + Ábra mentve ide: „%1”. + + + + Loading file '%1'. + A(z) „%1” fájl betöltése folyamatban van. + + + + Unknown object type: %1. + Ismeretlen objektumtípus: %1. + + + + Invalid file provided. + A megadott fájl érvénytelen. + + + + Could not save file: + A fájl mentése nem sikerült: + + + + Loaded file '%1'. + A(z) „%1” fájl betöltve. + + + + Copied plot screenshot to clipboard! + Ábra képernyőkép vágólapra másolva! + + + + &Update + &Frissítés + + + + &Update LogarithmPlotter + A LogarithmPlotter &frissítése + + + + ObjectCreationGrid + + + + Create new: + + Új létrehozása: + + + + ObjectLists + + + Hide all %1 + Összes %1 elrejtése + + + + Show all %1 + Összes %1 megjelenítése + + + + Hide %1 %2 + %1 %2 elrejtése + + + + Show %1 %2 + %1 %2 megjelenítése + + + + Set %1 %2 position + %1 %2 helye beállítása + + + + Delete %1 %2 + %1 %2 törlése + + + + Pick new color for %1 %2 + Válasszon új színt a következőhöz: %1 %2 + + + + PickLocationOverlay + + + Pointer precision: + Mutató pontossága: + + + + Snap to grid + Rácshoz illesztés + + + + Settings + + + X Zoom + X-nagyítás + + + + Y Zoom + Y-nagyítás + + + + Min X + Legkisebb X + + + + Max Y + Legnagyobb Y + + + + Max X + Legnagyobb X + + + + Min Y + Legkisebb Y + + + + X Axis Step + X tengely lépésköze + + + + Y Axis Step + Y tengely lépésköze + + + + Line width + Vonalvastagság + + + + Text size (px) + Szövegméret (képpont) + + + + X Label + X címke + + + + Y Label + Y címke + + + + X Log scale + X tengely logaritmikus skálával + + + + Show X graduation + X érettségi megjelenítése + + + + Show Y graduation + Y érettségi megjelenítése + + + + Copy to clipboard + Másolás a vágólapra + + + + Save plot + Ábra mentése + + + + Save plot as + Ábra mentése másként + + + + Load plot + Ábra betöltése + + + + changelog + + + Could not fetch changelog: Server error {}. + Nem sikerült lekérni a változásnaplót: Kiszolgálóhiba: {}. + + + + Could not fetch update: {}. + Nem sikerült lekérni a változásnaplót: {}. + + + + color + + + + %1 %2's color changed from %3 to %4. + + + + + comment + + + Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} + Példák: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} + + + + The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) + A következő paraméterek használatosak, ha a tartomány nem folytonos halmaz. (Példák: ℕ, ℤ, olyan halmazok, mint a {0;3}…) + + + + Note: Specify the probability for each value. + Megjegyzés: Adja meg az egyes értékek valószínűségét. + + + + Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... + Megjegyzés: A(z) %1[n] használatával hivatkozhat erre: %1ₙ, a(z) %1[n+1] használatával hivatkozhat erre: %1ₙ₊₁, … + + + + If you have latex enabled, you can use use latex markup in between $$ to create equations. + + + + + control + + + + + + %1: + %1: + + + + create + + + + New %1 %2 created. + Új %1 %2 létrehozva. + + + + delete + + + + %1 %2 deleted. + %1 %2 törölve. + + + + editproperty + + + %1 of %2 %3 changed from "%4" to "%5". + %1/%2 %3 megváltozott. Régi érték: %4, új érték: %5. + + + + %1 of %2 changed from %3 to %4. + + + + + function + + + Function + Függvény + + + + Functions + Függvények + + + + gainbode + + + Bode Magnitude + Bode-nagyságrend + + + + Bode Magnitudes + Bode-nagyságrendek + + + + + low-pass + aluláteresztő + + + + + high-pass + felüláteresztő + + + + historylib + + New %1 %2 created. + Új %1 %2 létrehozva. + + + %1 %2 deleted. + %1 %2 törölve. + + + %1 of %2 %3 changed from "%4" to "%5". + %1/%2 %3 megváltozott. Régi érték: %4, új érték: %5. + + + %1 %2 shown. + %1 %2 megjelenítve. + + + %1 %2 hidden. + %1 %2 rejtve. + + + + latex + + + No Latex installation found. +If you already have a latex distribution installed, make sure it's installed on your path. +Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. + + + + + DVIPNG was not found. Make sure you include it from your Latex distribution. + + + + + An exception occured within the creation of the latex formula. +Process '{}' ended with a non-zero return code {}: + +{} +Please make sure your latex installation is correct and report a bug if so. + + + + + An exception occured within the creation of the latex formula. +Process '{}' took too long to finish: +{} +Please make sure your latex installation is correct and report a bug if so. + + + + + name + + + + %1 %2 renamed to %3. + + + + + parameters + + + above + ↑ Felett + + + + below + ↓ Alatt + + + + + left + ← Balra + + + + + right + → Jobbra + + + + above-left + ↖ Felett, balra + + + + above-right + ↗ Felett, jobbra + + + + below-left + ↙ Alatt, balra + + + + below-right + ↘ Alatt, jobbra + + + + center + >|< Középre + + + + top + ↑ Felső + + + + bottom + ↓ Alsó + + + + top-left + ↖ Bal felső + + + + top-right + ↗ Jobb felső + + + + bottom-left + ↙ Bal alsó + + + + bottom-right + ↘ Jobb alsó + + + + application + Alkalmazás + + + + function + Függvény + + + + high + Magas + + + + low + Alul + + + + Next to target + Cél mellé + + + + With label + Címkével + + + + Hidden + Rejtett + + + + phasebode + + + Bode Phase + Bode-fázis + + + + Bode Phases + Bode-fázisok + + + + point + + + Point + Pont + + + + Points + Pontok + + + + prop + + + expression + Kifejezés + + + + definitionDomain + Abszcissza tartomány + + + + destinationDomain + Ordináta tartomány + + + + + + + + + + + + + labelPosition + Címke helyzete + + + + displayMode + Megjelenítési mód + + + + + + + + + + labelX + Címke X helyzete + + + + + drawPoints + Pontok megjelenítése + + + + + drawDashedLines + Szaggatott vonalak megjelenítése + + + + + om_0 + ω₀ + + + + pass + Áteresztő + + + + gain + Nagyságrend nyeresége + + + + omGraduation + ω₀ érettségi megjelenítése + + + + phase + Fázis + + + + unit + Egység használata + + + + + + x + X + + + + + y + Y + + + + pointStyle + Pontstílus + + + + probabilities + Valószínűségek listája + + + + text + Tartalom + + + + disableLatex + + + + + targetElement + Tárgycél + + + + approximate + Hozzávetőleges érték megjelenítése + + + + rounding + Kerekítés + + + + displayStyle + Megjelenítési stílus + + + + targetValuePosition + Cél értékpozíciója + + + + defaultExpression + Alapértelmezett kifejezés + + + + baseValues + Kezdeményezési értékek + + + + repartition + + + Repartition + Elosztás + + + + Repartition functions + Elosztási függvények + + + + sequence + + + Sequence + Sorozat + + + + Sequences + Sorozatok + + + + sommegainsbode + + + + Bode Magnitudes Sum + Bode-nagyságrendek összege + + + + sommephasesbode + + + + Bode Phases Sum + Bode-fázisok összege + + + + text + + + Text + Szöveg + + + + Texts + Szövegek + + + + update + + + An update for LogarithPlotter (v{}) is available. + Elérhető a Logaritmus-ábrázoló ({} verzió) frissítése. + + + + No update available. + Nincs telepíthető frissítés. + + + + Could not fetch update information: Server error {}. + Nem sikerült lekérni a frissítési adatokat: Kiszolgálóhiba: {}. + + + + Could not fetch update information: {}. + Nem sikerült lekérni a frissítési adatokat: {}. + + + + visibility + + + + %1 %2 shown. + %1 %2 megjelenítve. + + + + + %1 %2 hidden. + %1 %2 rejtve. + + + + xcursor + + + X Cursor + X kurzor + + + + X Cursors + X kurzorok + + + diff --git a/LogarithmPlotter/i18n/lp_nb_NO.ts b/LogarithmPlotter/i18n/lp_nb_NO.ts new file mode 100644 index 0000000..85ade3d --- /dev/null +++ b/LogarithmPlotter/i18n/lp_nb_NO.ts @@ -0,0 +1,1119 @@ + + + + + About + + + + About LogarithmPlotter + Om + + + + LogarithmPlotter v%1 + LogarithmPlotter v%1 + + + + 2D plotter software to make BODE plots, sequences and repartition functions. + 2D-plotterprogramvare laget for opprettelse av Bode-diagram, sekvenser, og distribusjonsfunksjoner. + + + + Report a bug + Rapporter en feil + + + + Official website + + + + + AppMenuBar + + + &File + &Fil + + + + &Load... + &Last inn … + + + + &Save + &Lagre + + + + Save &As... + Lagre &som … + + + + &Quit + &Avslutt + + + + &Edit + &Rediger + + + + &Undo + &Angre + + + + &Redo + &Gjenta + + + + &Copy plot + &Kopier plott + + + + &Create + &Opprett + + + + &Settings + &Innstillinger + + + + Check for updates on startup + Se etter nye versjoner ved programstart + + + + Reset redo stack automaticly + Tilbakestill angrehistorikk automatisk + + + + Enable LaTeX rendering + + + + + &Help + &Hjelp + + + + &Source code + + + + + &Report a bug + + + + + &User manual + + + + + &Changelog + + + + + &Help translating! + + + + + &About + &Om + + + + Save unsaved changes? + + + + + This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? + + + + + Changelog + + + Fetching changelog... + + + + + Done + + + + + EditorDialog + + + Edit properties of %1 %2 + Rediger egenskaper for %1 %2 + + + + Name + Navn + + + + Label content + Etikett-innhold + + + + null + NULL + + + + name + navn + + + + name + value + navn + veri + + + + + + Create new %1 + + Opprett nytt %1 + + + + FileDialog + + + Export Logarithm Plot file + Eksporter logaritmeplott-fil + + + + Import Logarithm Plot file + Importer logaritmeplott-fil + + + + GreetScreen + + + Welcome to LogarithmPlotter + Velkommen til LogarithmPlotter + + + + Version %1 + Versjon %1 + + + + Take a few seconds to configure LogarithmPlotter. +These settings can be changed at any time from the "Settings" menu. + Sett opp LogarithmPlotter. +Disse innstillingene kan endres når som helst fra «Innstillinger»-menyen. + + + + Check for updates on startup (requires online connectivity) + Se etter nye versjoner ved programstart. (Krever tilkobling til Internett.) + + + + Reset redo stack when a new action is added to history + Tilbakesitll angrehistorikk når en ny handling legges til + + + + Enable LaTeX rendering + + + + + User manual + + + + + Changelog + + + + + Done + + + + + HistoryBrowser + + + Filter... + + + + + Redo > + Angre > + + + + > Now + > Nå + + + + < Undo + < Angre + + + + ListSetting + + + + Add Entry + + + + + LogarithmPlotter + + + Objects + Objekter + + + + Settings + Innstillinger + + + + History + Historikk + + + + Saved plot to '%1'. + Lagret plott i «%1». + + + + Loading file '%1'. + Laster inn «%1»-fil. + + + + Unknown object type: %1. + Ukjent objekttype: %1. + + + + Invalid file provided. + Ugyldig fil angitt. + + + + Could not save file: + Kunne ikke lagre fil: + + + + Loaded file '%1'. + Lastet inn filen «%1». + + + + Copied plot screenshot to clipboard! + Kopierte plott-skjermavbildning til utklippstavlen! + + + + &Update + &Oppdater + + + + &Update LogarithmPlotter + &Installer ny versjon av LogartimePlotter + + + + ObjectCreationGrid + + + + Create new: + + Opprett ny: + + + + ObjectLists + + + Hide all %1 + Skjul alle %1 + + + + Show all %1 + Vis alle %1 + + + + Hide %1 %2 + Skjul %1 %2 + + + + Show %1 %2 + Vis %1 %2 + + + + Set %1 %2 position + Sett %1 %2 posisjon + + + + Delete %1 %2 + Slett %1 %2 + + + + Pick new color for %1 %2 + Velg ny farge for %1 %2 + + + + PickLocationOverlay + + + Pointer precision: + Peker-presisjon: + + + + Snap to grid + Fest til rutenett + + + + Settings + + + X Zoom + X-forstørrelse + + + + Y Zoom + Y-forstørrelse + + + + Min X + Min. X + + + + Max Y + Maks. Y + + + + Max X + Maks. X + + + + Min Y + Min. Y + + + + X Axis Step + X-aksesteg + + + + Y Axis Step + Y-aksesteg + + + + Line width + Linjebredde + + + + Text size (px) + Tekststørrelse (piksler) + + + + X Label + Navn på X-akse + + + + Y Label + Navn på Y-akse + + + + X Log scale + Logaritmisk skala i x + + + + Show X graduation + Vis X-inndeling + + + + Show Y graduation + Vis Y-inndeling + + + + Copy to clipboard + Kopier til utklippstavle + + + + Save plot + Lagre plott + + + + Save plot as + Lagre plott som + + + + Load plot + Last inn plott + + + + changelog + + + Could not fetch changelog: Server error {}. + + + + + Could not fetch update: {}. + + + + + color + + + + %1 %2's color changed from %3 to %4. + + + + + comment + + + Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} + + + + + The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) + + + + + Note: Specify the probability for each value. + + + + + Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... + + + + + If you have latex enabled, you can use use latex markup in between $$ to create equations. + + + + + control + + + + + + %1: + + + + + create + + + + New %1 %2 created. + Ny %1 %2 opprettet. + + + + delete + + + + %1 %2 deleted. + %1 %2 slettet. + + + + editproperty + + + %1 of %2 %3 changed from "%4" to "%5". + %1 av %2 %3 endret fra «%4» til «%5». + + + + %1 of %2 changed from %3 to %4. + + + + + function + + + Function + Funksjon + + + + Functions + Funksjoner + + + + gainbode + + + Bode Magnitude + Bode-magnitude + + + + Bode Magnitudes + Bode-magnituder + + + + + low-pass + lavpass + + + + + high-pass + høypass + + + + historylib + + New %1 %2 created. + Ny %1 %2 opprettet. + + + %1 %2 deleted. + %1 %2 slettet. + + + %1 of %2 %3 changed from "%4" to "%5". + %1 av %2 %3 endret fra «%4» til «%5». + + + %1 %2 shown. + %1 %2 vist. + + + %1 %2 hidden. + %1 %2 skjult. + + + + latex + + + No Latex installation found. +If you already have a latex distribution installed, make sure it's installed on your path. +Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. + + + + + DVIPNG was not found. Make sure you include it from your Latex distribution. + + + + + An exception occured within the creation of the latex formula. +Process '{}' ended with a non-zero return code {}: + +{} +Please make sure your latex installation is correct and report a bug if so. + + + + + An exception occured within the creation of the latex formula. +Process '{}' took too long to finish: +{} +Please make sure your latex installation is correct and report a bug if so. + + + + + name + + + + %1 %2 renamed to %3. + + + + + parameters + + + above + + + + + below + + + + + + left + + + + + + right + + + + + above-left + + + + + above-right + + + + + below-left + + + + + below-right + + + + + center + + + + + top + + + + + bottom + + + + + top-left + + + + + top-right + + + + + bottom-left + + + + + bottom-right + + + + + application + + + + + function + + + + + high + + + + + low + + + + + Next to target + + + + + With label + + + + + Hidden + + + + + phasebode + + + Bode Phase + Bode-fase + + + + Bode Phases + Bode-faser + + + + point + + + Point + Punkt + + + + Points + Punkter + + + + prop + + + expression + + + + + definitionDomain + + + + + destinationDomain + + + + + + + + + + + + + + labelPosition + + + + + displayMode + + + + + + + + + + + labelX + + + + + + drawPoints + + + + + + drawDashedLines + + + + + + om_0 + + + + + pass + + + + + gain + + + + + omGraduation + + + + + phase + + + + + unit + + + + + + + x + + + + + + y + + + + + pointStyle + + + + + probabilities + + + + + text + + + + + disableLatex + + + + + targetElement + + + + + approximate + + + + + rounding + + + + + displayStyle + + + + + targetValuePosition + + + + + defaultExpression + + + + + baseValues + + + + + repartition + + + Repartition + Distribusjon + + + + Repartition functions + Distribusjonsfunksjoner + + + + sequence + + + Sequence + Følge + + + + Sequences + Følger + + + + sommegainsbode + + + + Bode Magnitudes Sum + Bode-magnitudesum + + + + sommephasesbode + + + + Bode Phases Sum + Bode-fasesum + + + + text + + + Text + Tekst + + + + Texts + Tekster + + + + update + + + An update for LogarithPlotter (v{}) is available. + En ny versjon av LogartimePlotter (v{}) er tilgjengelig + + + + No update available. + Ingen nye versjoner. + + + + Could not fetch update information: Server error {}. + Fant ikke ut om det er noen nye versjoner. Tjenerfeil {}. + + + + Could not fetch update information: {}. + Kunne ikke hente info om hvorvidt det er nye versjoner: {}. + + + + visibility + + + + %1 %2 shown. + %1 %2 vist. + + + + + %1 %2 hidden. + %1 %2 skjult. + + + + xcursor + + + X Cursor + X-peker + + + + X Cursors + X-pekere + + + diff --git a/LogarithmPlotter/i18n/lp_template.ts b/LogarithmPlotter/i18n/lp_template.ts new file mode 100644 index 0000000..aacd4e5 --- /dev/null +++ b/LogarithmPlotter/i18n/lp_template.ts @@ -0,0 +1,1095 @@ + + + + + About + + + + About LogarithmPlotter + + + + + LogarithmPlotter v%1 + + + + + 2D plotter software to make BODE plots, sequences and repartition functions. + + + + + Report a bug + + + + + Official website + + + + + AppMenuBar + + + &File + + + + + &Load... + + + + + &Save + + + + + Save &As... + + + + + &Quit + + + + + &Edit + + + + + &Undo + + + + + &Redo + + + + + &Copy plot + + + + + &Create + + + + + &Settings + + + + + Check for updates on startup + + + + + Reset redo stack automaticly + + + + + Enable LaTeX rendering + + + + + &Help + + + + + &Source code + + + + + &Report a bug + + + + + &User manual + + + + + &Changelog + + + + + &Help translating! + + + + + &About + + + + + Save unsaved changes? + + + + + This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? + + + + + Changelog + + + Fetching changelog... + + + + + Done + + + + + EditorDialog + + + Edit properties of %1 %2 + + + + + Name + + + + + Label content + + + + + null + + + + + name + + + + + name + value + + + + + + + Create new %1 + + + + + FileDialog + + + Export Logarithm Plot file + + + + + Import Logarithm Plot file + + + + + GreetScreen + + + Welcome to LogarithmPlotter + + + + + Version %1 + + + + + Take a few seconds to configure LogarithmPlotter. +These settings can be changed at any time from the "Settings" menu. + + + + + Check for updates on startup (requires online connectivity) + + + + + Reset redo stack when a new action is added to history + + + + + Enable LaTeX rendering + + + + + User manual + + + + + Changelog + + + + + Done + + + + + HistoryBrowser + + + Filter... + + + + + Redo > + + + + + > Now + + + + + < Undo + + + + + ListSetting + + + + Add Entry + + + + + LogarithmPlotter + + + Objects + + + + + Settings + + + + + History + + + + + Saved plot to '%1'. + + + + + Loading file '%1'. + + + + + Unknown object type: %1. + + + + + Invalid file provided. + + + + + Could not save file: + + + + + Loaded file '%1'. + + + + + Copied plot screenshot to clipboard! + + + + + &Update + + + + + &Update LogarithmPlotter + + + + + ObjectCreationGrid + + + + Create new: + + + + + ObjectLists + + + Hide all %1 + + + + + Show all %1 + + + + + Hide %1 %2 + + + + + Show %1 %2 + + + + + Set %1 %2 position + + + + + Delete %1 %2 + + + + + Pick new color for %1 %2 + + + + + PickLocationOverlay + + + Pointer precision: + + + + + Snap to grid + + + + + Settings + + + X Zoom + + + + + Y Zoom + + + + + Min X + + + + + Max Y + + + + + Max X + + + + + Min Y + + + + + X Axis Step + + + + + Y Axis Step + + + + + Line width + + + + + Text size (px) + + + + + X Label + + + + + Y Label + + + + + X Log scale + + + + + Show X graduation + + + + + Show Y graduation + + + + + Copy to clipboard + + + + + Save plot + + + + + Save plot as + + + + + Load plot + + + + + changelog + + + Could not fetch changelog: Server error {}. + + + + + Could not fetch update: {}. + + + + + color + + + + %1 %2's color changed from %3 to %4. + + + + + comment + + + Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} + + + + + The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) + + + + + Note: Specify the probability for each value. + + + + + Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... + + + + + If you have latex enabled, you can use use latex markup in between $$ to create equations. + + + + + control + + + + + + %1: + + + + + create + + + + New %1 %2 created. + + + + + delete + + + + %1 %2 deleted. + + + + + editproperty + + + %1 of %2 %3 changed from "%4" to "%5". + + + + + %1 of %2 changed from %3 to %4. + + + + + function + + + Function + + + + + Functions + + + + + gainbode + + + Bode Magnitude + + + + + Bode Magnitudes + + + + + + low-pass + + + + + + high-pass + + + + + latex + + + No Latex installation found. +If you already have a latex distribution installed, make sure it's installed on your path. +Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. + + + + + DVIPNG was not found. Make sure you include it from your Latex distribution. + + + + + An exception occured within the creation of the latex formula. +Process '{}' ended with a non-zero return code {}: + +{} +Please make sure your latex installation is correct and report a bug if so. + + + + + An exception occured within the creation of the latex formula. +Process '{}' took too long to finish: +{} +Please make sure your latex installation is correct and report a bug if so. + + + + + name + + + + %1 %2 renamed to %3. + + + + + parameters + + + above + + + + + below + + + + + + left + + + + + + right + + + + + above-left + + + + + above-right + + + + + below-left + + + + + below-right + + + + + center + + + + + top + + + + + bottom + + + + + top-left + + + + + top-right + + + + + bottom-left + + + + + bottom-right + + + + + application + + + + + function + + + + + high + + + + + low + + + + + Next to target + + + + + With label + + + + + Hidden + + + + + phasebode + + + Bode Phase + + + + + Bode Phases + + + + + point + + + Point + + + + + Points + + + + + prop + + + expression + + + + + definitionDomain + + + + + destinationDomain + + + + + + + + + + + + + + labelPosition + + + + + displayMode + + + + + + + + + + + labelX + + + + + + drawPoints + + + + + + drawDashedLines + + + + + + om_0 + + + + + pass + + + + + gain + + + + + omGraduation + + + + + phase + + + + + unit + + + + + + + x + + + + + + y + + + + + pointStyle + + + + + probabilities + + + + + text + + + + + disableLatex + + + + + targetElement + + + + + approximate + + + + + rounding + + + + + displayStyle + + + + + targetValuePosition + + + + + defaultExpression + + + + + baseValues + + + + + repartition + + + Repartition + + + + + Repartition functions + + + + + sequence + + + Sequence + + + + + Sequences + + + + + sommegainsbode + + + + Bode Magnitudes Sum + + + + + sommephasesbode + + + + Bode Phases Sum + + + + + text + + + Text + + + + + Texts + + + + + update + + + An update for LogarithPlotter (v{}) is available. + + + + + No update available. + + + + + Could not fetch update information: Server error {}. + + + + + Could not fetch update information: {}. + + + + + visibility + + + + %1 %2 shown. + + + + + + %1 %2 hidden. + + + + + xcursor + + + X Cursor + + + + + X Cursors + + + + diff --git a/LogarithmPlotter/i18n/release.sh b/LogarithmPlotter/i18n/release.sh new file mode 100755 index 0000000..93475b5 --- /dev/null +++ b/LogarithmPlotter/i18n/release.sh @@ -0,0 +1,2 @@ +#!/bin/bash +lrelease *.ts diff --git a/LogarithmPlotter/i18n/update.sh b/LogarithmPlotter/i18n/update.sh new file mode 100755 index 0000000..aa48415 --- /dev/null +++ b/LogarithmPlotter/i18n/update.sh @@ -0,0 +1,2 @@ +#!/bin/bash +lupdate -extensions js,qs,qml,py -recursive .. -ts lp_*.ts diff --git a/LogarithmPlotter/logarithmplotter.py b/LogarithmPlotter/logarithmplotter.py new file mode 100644 index 0000000..70073ef --- /dev/null +++ b/LogarithmPlotter/logarithmplotter.py @@ -0,0 +1,152 @@ +""" + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . +""" + +from time import time + +from PySide2.QtWidgets import QApplication +from PySide2.QtQml import QQmlApplicationEngine +from PySide2.QtCore import Qt, QTranslator, QLocale +from PySide2.QtGui import QIcon + +from tempfile import TemporaryDirectory +from os import getcwd, chdir, environ, path, remove, close +from platform import release as os_release +from sys import platform, argv, version as sys_version, exit +from sys import path as sys_path + +start_time = time() + +# Create the temporary directory for saving copied screenshots and latex files +tempdir = TemporaryDirectory() +tmpfile = path.join(tempdir.name, 'graph.png') +pwd = getcwd() + +chdir(path.dirname(path.realpath(__file__))) + +if path.realpath(path.join(getcwd(), "..")) not in sys_path: + sys_path.append(path.realpath(path.join(getcwd(), ".."))) + + +from LogarithmPlotter import __VERSION__ +from LogarithmPlotter.util import config, native +from LogarithmPlotter.util.update import check_for_updates +from LogarithmPlotter.util.helper import Helper +from LogarithmPlotter.util.latex import Latex + +config.init() + +def get_linux_theme(): + des = { + "KDE": "org.kde.desktop", + "gnome": "default", + "lxqt": "fusion", + "mate": "fusion", + } + if "XDG_SESSION_DESKTOP" in environ: + return des[environ["XDG_SESSION_DESKTOP"]] if environ["XDG_SESSION_DESKTOP"] in des else "fusion" + else: + # Android + return "Material" + +def run(): + + environ["QT_QUICK_CONTROLS_STYLE"] = { + "linux": get_linux_theme(), + "freebsd": get_linux_theme(), + "win32": "universal" if os_release == "10" else "fusion", + "cygwin": "fusion", + "darwin": "default" + }[platform] + + dep_time = time() + print("Loaded dependencies in " + str((dep_time - start_time)*1000) + "ms.") + + icon_fallbacks = QIcon.fallbackSearchPaths(); + base_icon_path = path.join(getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "icons") + icon_fallbacks.append(path.realpath(path.join(base_icon_path, "common"))) + icon_fallbacks.append(path.realpath(path.join(base_icon_path, "objects"))) + icon_fallbacks.append(path.realpath(path.join(base_icon_path, "history"))) + icon_fallbacks.append(path.realpath(path.join(base_icon_path, "settings"))) + icon_fallbacks.append(path.realpath(path.join(base_icon_path, "settings", "custom"))) + QIcon.setFallbackSearchPaths(icon_fallbacks); + + QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) + app = QApplication(argv) + app.setApplicationName("LogarithmPlotter") + app.setOrganizationName("Ad5001") + app.styleHints().setShowShortcutsInContextMenus(True) + app.setWindowIcon(QIcon(path.realpath(path.join(getcwd(), "logarithmplotter.svg")))) + + # Installing translators + translator = QTranslator() + # Check if lang is forced. + forcedlang = [p for p in argv if p[:7]=="--lang="] + locale = QLocale(forcedlang[0][7:]) if len(forcedlang) > 0 else QLocale() + if (translator.load(locale, "lp", "_", path.realpath(path.join(getcwd(), "i18n")))): + app.installTranslator(translator); + + # Installing macOS file handler. + macOSFileOpenHandler = None + if platform == "darwin": + macOSFileOpenHandler = native.MacOSFileOpenHandler() + app.installEventFilter(macOSFileOpenHandler) + + engine = QQmlApplicationEngine() + global tmpfile + helper = Helper(pwd, tmpfile) + latex = Latex(tempdir) + engine.rootContext().setContextProperty("Helper", helper) + engine.rootContext().setContextProperty("Latex", latex) + engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv) + 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. + + engine.addImportPath(path.realpath(path.join(getcwd(), "qml"))) + engine.load(path.realpath(path.join(getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "LogarithmPlotter.qml"))) + + + if not engine.rootObjects(): + print("No root object", path.realpath(path.join(getcwd(), "qml"))) + print(path.realpath(path.join(getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "LogarithmPlotter.qml"))) + exit(-1) + + # Open the current diagram + chdir(pwd) + if len(argv) > 0 and path.exists(argv[-1]) and argv[-1].split('.')[-1] in ['lpf']: + engine.rootObjects()[0].loadDiagram(argv[-1]) + chdir(path.dirname(path.realpath(__file__))) + + if platform == "darwin": + macOSFileOpenHandler.init_graphics(engine.rootObjects()[0]) + + latex.check_latex_install() + + # Check for updates + if config.getSetting("check_for_updates"): + check_for_updates(__VERSION__, engine.rootObjects()[0]) + + exit_code = app.exec_() + + tempdir.cleanup() + config.save() + exit(exit_code) + +if __name__ == "__main__": + run() + diff --git a/LogarithmPlotter/logarithmplotter.svg b/LogarithmPlotter/logarithmplotter.svg new file mode 100644 index 0000000..69f819d --- /dev/null +++ b/LogarithmPlotter/logarithmplotter.svg @@ -0,0 +1,9 @@ + + LogarithmPlotter Icon v1.0 + + + + + + + diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml similarity index 67% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml index d271cb6..7e5ef9c 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,11 +16,13 @@ * along with this program. If not, see . */ -import QtQuick -import Qt.labs.platform as Native +import QtQuick 2.12 +import QtQuick.Dialogs 1.3 //import QtQuick.Controls 2.15 import eu.ad5001.MixedMenu 1.1 -import eu.ad5001.LogarithmPlotter.Common +import "js/objects.js" as Objects +import "js/historylib.js" as HistoryLib +import "js/math/latex.js" as Latex /*! @@ -41,7 +43,6 @@ MenuBar { shortcut: StandardKey.Open onTriggered: settings.load() icon.name: 'document-open' - icon.color: sysPalette.windowText } Action { @@ -49,14 +50,13 @@ MenuBar { shortcut: StandardKey.Save onTriggered: settings.save() icon.name: 'document-save' - icon.color: sysPalette.windowText } Action { text: qsTr("Save &As...") shortcut: StandardKey.SaveAs onTriggered: settings.saveAs() - icon.color: sysPalette.windowText - icon.name: 'document-save-as' + icon.name: 'document-save-as' + } MenuSeparator { } Action { @@ -70,7 +70,6 @@ MenuBar { } icon.name: 'application-exit' - icon.color: sysPalette.windowText } } @@ -79,31 +78,25 @@ MenuBar { Action { text: qsTr("&Undo") shortcut: StandardKey.Undo - onTriggered: Modules.History.undo() + onTriggered: history.undo() icon.name: 'edit-undo' icon.color: enabled ? sysPalette.windowText : sysPaletteIn.windowText + enabled: history.undoCount > 0 } Action { text: qsTr("&Redo") shortcut: StandardKey.Redo - onTriggered: Modules.History.redo() + onTriggered: history.redo() icon.name: 'edit-redo' icon.color: enabled ? sysPalette.windowText : sysPaletteIn.windowText + enabled: history.redoCount > 0 } + MenuSeparator { } Action { text: qsTr("&Copy plot") shortcut: StandardKey.Copy onTriggered: root.copyDiagramToClipboard() icon.name: 'edit-copy' - icon.color: sysPalette.windowText - } - MenuSeparator { } - Action { - text: qsTr("&Preferences") - shortcut: StandardKey.Copy - onTriggered: preferences.open() - icon.name: 'settings' - icon.color: sysPalette.windowText } } @@ -111,83 +104,106 @@ MenuBar { title: qsTr("&Create") // Services repeater Repeater { - model: Object.keys(Modules.Objects.types) + model: Object.keys(Objects.types) MenuItem { - text: Modules.Objects.types[modelData].displayType() - visible: Modules.Objects.types[modelData].createable() + text: Objects.types[modelData].displayType() + visible: Objects.types[modelData].createable() height: visible ? implicitHeight : 0 icon.name: modelData icon.source: './icons/objects/' + modelData + '.svg' icon.color: sysPalette.buttonText onTriggered: { - var newObj = Modules.Objects.createNewRegisteredObject(modelData) - Modules.History.addToHistory(new JS.HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export())) + var newObj = Objects.createNewRegisteredObject(modelData) + history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export())) objectLists.update() } } } } + Menu { + 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' + } + + 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' + } + + Action { + id: enableLatexSetting + text: qsTr("Enable LaTeX rendering") + checkable: true + checked: Helper.getSettingBool("enable_latex") + onTriggered: { + Helper.setSettingBool("enable_latex", checked) + Latex.enabled = checked + drawCanvas.requestPaint() + } + icon.name: 'Expression' + + Component.onCompleted: Latex.enabled = checked + } + } + Menu { title: qsTr("&Help") Action { text: qsTr("&Source code") icon.name: 'software-sources' - icon.color: sysPalette.windowText onTriggered: Qt.openUrlExternally("https://git.ad5001.eu/Ad5001/LogarithmPlotter") } Action { text: qsTr("&Report a bug") icon.name: 'tools-report-bug' - icon.color: sysPalette.windowText onTriggered: Qt.openUrlExternally("https://git.ad5001.eu/Ad5001/LogarithmPlotter/issues") } Action { text: qsTr("&User manual") icon.name: 'documentation' - icon.color: sysPalette.windowText onTriggered: Qt.openUrlExternally("https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/_Sidebar") } Action { text: qsTr("&Changelog") icon.name: 'state-information' - icon.color: sysPalette.windowText onTriggered: changelog.open() } Action { text: qsTr("&Help translating!") - icon.name: 'translate' - icon.color: sysPalette.windowText + icon.name: 'translator' onTriggered: Qt.openUrlExternally("https://hosted.weblate.org/engage/logarithmplotter/") } MenuSeparator { } - Action { - text: qsTr("&Thanks") - icon.name: 'help-about' - icon.color: sysPalette.windowText - onTriggered: thanksTo.open() - } Action { text: qsTr("&About") shortcut: StandardKey.HelpContents - icon.name: 'help-about' - icon.color: sysPalette.windowText + icon.name: 'about' onTriggered: about.open() } } - Native.MessageDialog { + MessageDialog { id: saveUnsavedChangesDialog title: qsTr("Save unsaved changes?") + icon: StandardIcon.Question text: qsTr("This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue?") - buttons: Native.MessageDialog.Save | Native.MessageDialog.Discard | Native.MessageDialog.Cancel - - onSaveClicked: settings.save() - onDiscardClicked: Qt.quit() + standardButtons: StandardButton.Yes | StandardButton.No + onYes: Qt.quit() } - function openSaveUnsavedChangesDialog() { - saveUnsavedChangesDialog.open() + function showSaveUnsavedChangesDialog() { + saveUnsavedChangesDialog.visible = true } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/History.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/History.qml new file mode 100644 index 0000000..e6caf9d --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/History.qml @@ -0,0 +1,219 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +import QtQuick 2.12 +import QtQml 2.12 +import "../js/objects.js" as Objects +import "../js/historylib.js" as HistoryLib +import "../js/history/common.js" as HistoryCommon + +/*! + \qmltype History + \inqmlmodule eu.ad5001.LogarithmPlotter.History + \brief QObject holding persistantly for undo & redo stacks. + + \sa HistoryBrowser, historylib +*/ +Item { + // Using a QtObject is necessary in order to have proper property propagation in QML + id: historyObj + + /*! + \qmlproperty int History::undoCount + Count of undo actions. + */ + property int undoCount: 0 + /*! + \qmlproperty int History::redoCount + Count of redo actions. + */ + property int redoCount: 0 + /*! + \qmlproperty var History::undoStack + Stack of undo actions. + */ + property var undoStack: [] + /*! + \qmlproperty var History::redoStack + Stack of redo actions. + */ + property var redoStack: [] + /*! + \qmlproperty bool History::saved + true when no modification was done to the current working file, false otherwise. + */ + property bool saved: true + + + /*! + \qmlmethod void History::clear() + Clears both undo and redo stacks completly. + */ + function clear() { + undoCount = 0 + redoCount = 0 + undoStack = [] + redoStack = [] + } + + + /*! + \qmlmethod var History::serialize() + Serializes history into JSON-able content. + */ + function serialize() { + let undoSt = [], redoSt = []; + for(let i = 0; i < undoCount; i++) + undoSt.push([ + undoStack[i].type(), + undoStack[i].export() + ]); + for(let i = 0; i < redoCount; i++) + redoSt.push([ + redoStack[i].type(), + redoStack[i].export() + ]); + return [undoSt, redoSt] + } + + /*! + \qmlmethod void History::unserialize(var undoSt, var redoSt) + Unserializes both \c undoSt stack and \c redoSt stack from serialized content. + */ + function unserialize(undoSt, redoSt) { + clear(); + for(let i = 0; i < undoSt.length; i++) + undoStack.push(new HistoryLib.Actions[undoSt[i][0]](...undoSt[i][1])) + for(let i = 0; i < redoSt.length; i++) + redoStack.push(new HistoryLib.Actions[redoSt[i][0]](...redoSt[i][1])) + undoCount = undoSt.length; + redoCount = redoSt.length; + objectLists.update() + } + + /*! + \qmlmethod void History::addToHistory(var action) + Adds an instance of historylib.Action to history. + */ + function addToHistory(action) { + if(action instanceof HistoryLib.Action) { + console.log("Added new entry to history: " + action.getReadableString()) + undoStack.push(action) + undoCount++; + if(Helper.getSettingBool("reset_redo_stack")) { + redoStack = [] + redoCount = 0 + } + saved = false + } + } + + /*! + \qmlmethod void History::undo(bool updateObjectList = true) + Undoes the historylib.Action at the top of the undo stack and pushes it to the top of the redo stack. + By default, will update the graph and the object list. This behavior can be disabled by setting the \c updateObjectList to false. + */ + function undo(updateObjectList = true) { + if(undoStack.length > 0) { + var action = undoStack.pop() + action.undo() + if(updateObjectList) + objectLists.update() + redoStack.push(action) + undoCount--; + redoCount++; + saved = false + } + } + + /*! + \qmlmethod void History::redo(bool updateObjectList = true) + Redoes the historylib.Action at the top of the redo stack and pushes it to the top of the undo stack. + By default, will update the graph and the object list. This behavior can be disabled by setting the \c updateObjectList to false. + */ + function redo(updateObjectList = true) { + if(redoStack.length > 0) { + var action = redoStack.pop() + action.redo() + if(updateObjectList) + objectLists.update() + undoStack.push(action) + undoCount++; + redoCount--; + saved = false + } + } + + /*! + \qmlmethod void History::undoMultipleDefered(int toUndoCount) + Undoes several historylib.Action at the top of the undo stack and pushes them to the top of the redo stack. + It undoes them deferedly to avoid overwhelming the computer while creating a cool short accelerated summary of all changes. + */ + function undoMultipleDefered(toUndoCount) { + undoTimer.toUndoCount = toUndoCount; + undoTimer.start() + if(toUndoCount > 0) + saved = false + } + + + /*! + \qmlmethod void History::redoMultipleDefered(int toRedoCount) + Redoes several historylib.Action at the top of the redo stack and pushes them to the top of the undo stack. + It redoes them deferedly to avoid overwhelming the computer while creating a cool short accelerated summary of all changes. + */ + function redoMultipleDefered(toRedoCount) { + redoTimer.toRedoCount = toRedoCount; + redoTimer.start() + if(toRedoCount > 0) + saved = false + } + + Timer { + id: undoTimer + interval: 5; running: false; repeat: true + property int toUndoCount: 0 + onTriggered: { + if(toUndoCount > 0) { + historyObj.undo(toUndoCount % 4 == 1) // Only redraw once every 4 changes. + toUndoCount--; + } else { + running = false; + } + } + } + + Timer { + id: redoTimer + interval: 5; running: false; repeat: true + property int toRedoCount: 0 + onTriggered: { + if(toRedoCount > 0) { + historyObj.redo(toRedoCount % 4 == 1) // Only redraw once every 4 changes. + toRedoCount--; + } else { + running = false; + } + } + } + + Component.onCompleted: { + HistoryLib.history = historyObj + HistoryCommon.themeTextColor = sysPalette.windowText + } +} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/Browser.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/HistoryBrowser.qml similarity index 65% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/Browser.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/HistoryBrowser.qml index 09b6feb..c1f73a4 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/Browser.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/HistoryBrowser.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,15 +16,14 @@ * along with this program. If not, see . */ -pragma ComponentBehavior: Bound - -import QtQuick.Controls -import QtQuick +import QtQuick.Controls 2.12 +import QtQuick 2.12 import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting +import "../js/utils.js" as Utils /*! - \qmltype Browser + \qmltype HistoryBrowser \inqmlmodule eu.ad5001.LogarithmPlotter.History \brief Tab of the drawer that allows to navigate through the undo and redo history. @@ -48,27 +47,13 @@ Item { true when the system is running with a dark theme, false otherwise. */ property bool darkTheme: isDarkTheme() - - /*! - \qmlproperty int HistoryBrowser::undoCount - Number of actions in the undo stack. - */ - property int undoCount: 0 - - /*! - \qmlproperty int HistoryBrowser::redoCount - Number of actions in the redo stack. - */ - property int redoCount: 0 Setting.TextSetting { id: filterInput anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top - anchors.rightMargin: 5 placeholderText: qsTr("Filter...") - category: "all" } ScrollView { @@ -90,22 +75,19 @@ Item { id: redoColumn anchors.right: parent.right anchors.top: parent.top - width: historyBrowser.actionWidth + width: actionWidth Repeater { - model: historyBrowser.redoCount + model: history.redoCount - SingleItem { + HistoryItem { id: redoButton - width: historyBrowser.actionWidth + width: actionWidth //height: actionHeight isRedo: true + idx: index darkTheme: historyBrowser.darkTheme hidden: !(filterInput.value == "" || content.includes(filterInput.value)) - onClicked: { - redoTimer.toRedoCount = Modules.History.redoStack.length-index - redoTimer.start() - } } } } @@ -118,14 +100,14 @@ Item { transform: Rotation { origin.x: 30; origin.y: 30; angle: 270} height: 70 width: 20 - visible: historyBrowser.redoCount > 0 + visible: history.redoCount > 0 } Rectangle { id: nowRect anchors.right: parent.right anchors.top: redoColumn.bottom - width: historyBrowser.actionWidth + width: actionWidth height: 40 color: sysPalette.highlight Text { @@ -141,24 +123,20 @@ Item { id: undoColumn anchors.right: parent.right anchors.top: nowRect.bottom - width: historyBrowser.actionWidth + width: actionWidth Repeater { - model: historyBrowser.undoCount + model: history.undoCount - SingleItem { + HistoryItem { id: undoButton - width: historyBrowser.actionWidth + width: actionWidth //height: actionHeight isRedo: false + idx: index darkTheme: historyBrowser.darkTheme hidden: !(filterInput.value == "" || content.includes(filterInput.value)) - - onClicked: { - undoTimer.toUndoCount = +index+1 - undoTimer.start() - } } } } @@ -171,39 +149,7 @@ Item { transform: Rotation { origin.x: 30; origin.y: 30; angle: 270} height: 60 width: 20 - visible: historyBrowser.undoCount > 0 - } - } - } - - Timer { - id: undoTimer - interval: 5; running: false; repeat: true - property int toUndoCount: 0 - onTriggered: { - if(toUndoCount > 0) { - Modules.History.undo() - if(toUndoCount % 3 === 1) - Modules.Canvas.requestPaint() - toUndoCount--; - } else { - running = false; - } - } - } - - Timer { - id: redoTimer - interval: 5; running: false; repeat: true - property int toRedoCount: 0 - onTriggered: { - if(toRedoCount > 0) { - Modules.History.redo() - if(toRedoCount % 3 === 1) - Modules.Canvas.requestPaint() - toRedoCount--; - } else { - running = false; + visible: history.undoCount > 0 } } } @@ -216,18 +162,6 @@ Item { let hex = sysPalette.windowText.toString() // We only check the first parameter, as on all normal OSes, text color is grayscale. return parseInt(hex.substr(1,2), 16) > 128 - } - - Component.onCompleted: { - Modules.History.initialize({ - helper: Helper, - themeTextColor: sysPalette.windowText.toString(), - imageDepth: Screen.devicePixelRatio, - fontSize: 14 - }) - Modules.History.on("cleared loaded added undone redone", () => { - undoCount = Modules.History.undoStack.length - redoCount = Modules.History.redoStack.length - }) + } } diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/SingleItem.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/HistoryItem.qml similarity index 78% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/SingleItem.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/HistoryItem.qml index fac19e3..44397af 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/SingleItem.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/HistoryItem.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,13 +16,15 @@ * along with this program. If not, see . */ -import QtQuick -import QtQuick.Controls +import QtQuick.Controls 2.12 +import QtQuick 2.12 +import QtGraphicalEffects 1.15 +import "../js/utils.js" as Utils import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting /*! - \qmltype SingleItem + \qmltype HistoryItem \inqmlmodule eu.ad5001.LogarithmPlotter.History \brief Item representing an history action. @@ -40,17 +42,17 @@ Button { \qmlproperty bool HistoryItem::isRedo true if the action is in the redo stack, false othewise. */ - required property bool isRedo + property bool isRedo /*! - \qmlproperty int HistoryItem::index + \qmlproperty int HistoryItem::idx Index of the item within the HistoryBrowser list. */ - required property int index + property int idx /*! \qmlproperty bool HistoryItem::darkTheme true when the system is running with a dark theme, false otherwise. */ - required property bool darkTheme + property bool darkTheme /*! \qmlproperty bool HistoryItem::hidden true when the item is filtered out, false otherwise. @@ -60,7 +62,7 @@ Button { \qmlproperty int HistoryItem::historyAction Associated history action. */ - readonly property var historyAction: isRedo ? Modules.History.redoStack.at(index) : Modules.History.undoStack.at(-index-1) + readonly property var historyAction: isRedo ? history.redoStack[idx] : history.undoStack[history.undoCount-idx-1] /*! \qmlproperty int HistoryItem::actionHeight @@ -81,11 +83,12 @@ Button { height: hidden ? 8 : Math.max(actionHeight, label.height + 15) - Rectangle { + LinearGradient { anchors.fill: parent //opacity: hidden ? 0.6 : 1 + start: Qt.point(0, 0) + end: Qt.point(parent.width, 0) gradient: Gradient { - orientation: Gradient.Horizontal GradientStop { position: 0.1; color: "transparent" } GradientStop { position: 1.5; color: clr } } @@ -113,23 +116,10 @@ Button { anchors.verticalCenter: parent.verticalCenter visible: !hidden font.pixelSize: 14 - text: "" + text: historyAction.getHTMLString().replace(/\$\{tag_color\}/g, clr) textFormat: Text.RichText clip: true wrapMode: Text.WordWrap - - Component.onCompleted: function() { - // Render HTML, might be string, but could also be a promise - const html = historyAction.getHTMLString() - if(typeof html === "string") { - label.text = html.replace(/\$\{tag_color\}/g, clr) - } else { - // Promise! We need to way to wait for it to be completed. - html.then(rendered => { - label.text = rendered.replace(/\$\{tag_color\}/g, clr) - }) - } - } } Rectangle { @@ -142,9 +132,18 @@ Button { color: sysPalette.windowText } + //text: content + ToolTip.visible: hovered ToolTip.delay: 200 ToolTip.text: content + + onClicked: { + if(isRedo) + history.redoMultipleDefered(history.redoCount-idx) + else + history.undoMultipleDefered(+idx+1) + } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/qmldir b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/qmldir new file mode 100644 index 0000000..4f011a5 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/qmldir @@ -0,0 +1,5 @@ +module eu.ad5001.LogarithmPlotter.History + +History 1.0 History.qml +HistoryBrowser 1.0 HistoryBrowser.qml +HistoryItem 1.0 HistoryItem.qml diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml new file mode 100644 index 0000000..e35950a --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml @@ -0,0 +1,475 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +import QtQuick 2.12 +import "js/objects.js" as Objects +import "js/utils.js" as Utils +import "js/mathlib.js" as MathLib + +/*! + \qmltype LogGraphCanvas + \inqmlmodule eu.ad5001.LogarithmPlotter + \brief Canvas used to display the diagram. + + Provides a customized canvas with several helper methods to be used by objects. + + \sa LogarithmPlotter, PickLocationOverlay +*/ +Canvas { + id: canvas + anchors.top: separator.bottom + anchors.left: parent.left + height: parent.height - 90 + width: parent.width + + /*! + \qmlproperty double LogGraphCanvas::xmin + Minimum x of the diagram, provided from settings. + \sa Settings + */ + property double xmin: 0 + /*! + \qmlproperty double LogGraphCanvas::ymax + Maximum y of the diagram, provided from settings. + \sa Settings + */ + property double ymax: 0 + /*! + \qmlproperty double LogGraphCanvas::xzoom + Zoom on the x axis of the diagram, provided from settings. + \sa Settings + */ + property double xzoom: 10 + /*! + \qmlproperty double LogGraphCanvas::yzoom + Zoom on the y axis of the diagram, provided from settings. + \sa Settings + */ + property double yzoom: 10 + /*! + \qmlproperty string LogGraphCanvas::xaxisstep + Step of the x axis graduation, provided from settings. + \note: Only available in non-logarithmic mode. + \sa Settings + */ + property string xaxisstep: "4" + /*! + \qmlproperty string LogGraphCanvas::yaxisstep + Step of the y axis graduation, provided from settings. + \sa Settings + */ + property string yaxisstep: "4" + /*! + \qmlproperty string LogGraphCanvas::xlabel + Label used on the x axis, provided from settings. + \sa Settings + */ + property string xlabel: "" + /*! + \qmlproperty string LogGraphCanvas::ylabel + Label used on the y axis, provided from settings. + \sa Settings + */ + property string ylabel: "" + /*! + \qmlproperty double LogGraphCanvas::linewidth + Width of lines that will be drawn into the canvas, provided from settings. + \sa Settings + */ + property double linewidth: 1 + /*! + \qmlproperty double LogGraphCanvas::textsize + Font size of the text that will be drawn into the canvas, provided from settings. + \sa Settings + */ + property double textsize: 14 + /*! + \qmlproperty bool LogGraphCanvas::logscalex + true if the canvas should be in logarithmic mode, false otherwise. + Provided from settings. + \sa Settings + */ + property bool logscalex: false + /*! + \qmlproperty bool LogGraphCanvas::showxgrad + true if the x graduation should be shown, false otherwise. + Provided from settings. + \sa Settings + */ + property bool showxgrad: false + /*! + \qmlproperty bool LogGraphCanvas::showygrad + true if the y graduation should be shown, false otherwise. + Provided from settings. + \sa Settings + */ + property bool showygrad: false + + /*! + \qmlproperty int LogGraphCanvas::maxgradx + Max power of the logarithmic scaled on the x axis in logarithmic mode. + */ + 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 + Dictionary of format {image: [callback.image data]} containing data for defered image loading. + */ + property var imageLoaders: {} + /*! + \qmlproperty var LogGraphCanvas::ctx + Cache for the 2D context so that it may be used asynchronously. + */ + property var ctx + + Component.onCompleted: imageLoaders = {} + + onPaint: function(rect) { + //console.log('Redrawing') + if(rect.width == canvas.width) { // Redraw full canvas + ctx = getContext("2d"); + 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) obj.draw(canvas, ctx) + } + } + ctx.lineWidth = 1 + } + } + + onImageLoaded: { + Object.keys(imageLoaders).forEach((key) => { + if(isImageLoaded(key)) { + // Calling callback + imageLoaders[key][0](canvas, ctx, imageLoaders[key][1]) + delete imageLoaders[key] + } + }) + } + + /*! + \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*(y==0))) + } + } 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(visible(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(visible(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) + 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. + */ + function x2px(x) { + if(logscalex) { + var logxmin = Math.log(xmin) + return (Math.log(x)-logxmin)*xzoom + } else return (x - xmin)*xzoom + } + + /*! + \qmlmethod double LogGraphCanvas::y2px(double y) + Converts an \c y coordinate to it's relative position on the canvas. + The y axis not supporting logarithmic scale, it only support linear convertion. + */ + function y2px(y) { + return (ymax-y)*yzoom + } + + /*! + \qmlmethod double LogGraphCanvas::px2x(double px) + Converts an x \c 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. + */ + function px2x(px) { + if(logscalex) { + return Math.exp(px/xzoom+Math.log(xmin)) + } else return (px/xzoom+xmin) + } + + /*! + \qmlmethod double LogGraphCanvas::px2x(double px) + Converts an x \c 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. + */ + function px2y(px) { + return -(px/yzoom-ymax) + } + + /*! + \qmlmethod bool LogGraphCanvas::visible(double x, double y) + Checks whether a plot point (\c x, \c y) is visible or not on the canvas. + */ + function visible(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) + } + } +} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml similarity index 50% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml index ddbd2bd..b2f6975 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,18 +16,17 @@ * along with this program. If not, see . */ -import QtQml -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts 1.12 +import QtQml 2.12 +import QtQuick.Controls 2.12 import eu.ad5001.MixedMenu 1.1 +import QtQuick.Layouts 1.12 +import QtQuick 2.12 +// Auto loading all objects. +import "js/objs/autoload.js" as ALObjects -// Auto loading all modules. -import eu.ad5001.LogarithmPlotter.Common - -import eu.ad5001.LogarithmPlotter.History 1.0 as History +import "js/objects.js" as Objects +import eu.ad5001.LogarithmPlotter.History 1.0 import eu.ad5001.LogarithmPlotter.ObjectLists 1.0 -import eu.ad5001.LogarithmPlotter.Overlay 1.0 as Overlay import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup /*! @@ -43,7 +42,7 @@ ApplicationWindow { width: 1000 height: 500 color: sysPalette.window - title: qsTr("untitled") + title: "LogarithmPlotter " + (settings.saveFilename != "" ? " - " + settings.saveFilename.split('/').pop() : "") + (history.saved ? "" : "*") SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } SystemPalette { id: sysPaletteIn; colorGroup: SystemPalette.Disabled } @@ -51,17 +50,15 @@ ApplicationWindow { menuBar: appMenu.trueItem AppMenuBar {id: appMenu} - - Popup.GreetScreen {} - Popup.Preferences {id: preferences} + History { id: history } + + Popup.GreetScreen {} Popup.Changelog {id: changelog} Popup.About {id: about} - Popup.ThanksTo {id: thanksTo} - Popup.Alert { id: alert anchors.bottom: parent.bottom @@ -121,16 +118,16 @@ ApplicationWindow { ObjectLists { id: objectLists - onChanged: Modules.Canvas.requestPaint() + onChanged: drawCanvas.requestPaint() } Settings { id: settings canvas: drawCanvas - onChanged: Modules.Canvas.requestPaint() + onChanged: drawCanvas.requestPaint() } - History.Browser { + HistoryBrowser { id: historyBrowser } } @@ -144,9 +141,23 @@ ApplicationWindow { width: sidebar.inPortrait ? parent.width : parent.width - sidebar.width//*sidebar.position x: sidebar.width//*sidebar.position + xmin: settings.xmin + ymax: settings.ymax + xzoom: settings.xzoom + yzoom: settings.yzoom + xlabel: settings.xlabel + ylabel: settings.ylabel + yaxisstep: settings.yaxisstep + xaxisstep: settings.xaxisstep + logscalex: settings.logscalex + linewidth: settings.linewidth + textsize: settings.textsize + showxgrad: settings.showxgrad + showygrad: settings.showygrad + property bool firstDrawDone: false - onPainted: if(!firstDrawDone) { + onPainted: if(!firstDrawDone) { firstDrawDone = true; console.info("First paint done in " + (new Date().getTime()-(StartTime*1000)) + "ms") if(TestBuild == true) { @@ -155,20 +166,125 @@ ApplicationWindow { } } - Overlay.ViewPositionChange { - id: viewPositionChanger - anchors.fill: parent - } - - Overlay.PickLocation { + PickLocationOverlay { id: positionPicker anchors.fill: parent + canvas: parent } } - - Overlay.Loading { - id: loadingOverlay - anchors.fill: parent + + /*! + \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 = {} + for(var objType in data['objects']) { + if(Object.keys(Objects.types).indexOf(objType) > -1) { + Objects.currentObjects[objType] = [] + for(var objData of data['objects'][objType]) { + var obj = new Objects.types[objType](...objData) + Objects.currentObjects[objType].push(obj) + } + } else { + error += qsTr("Unknown object type: %1.").arg(objType) + "\n"; + } + } + + // 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 { @@ -185,26 +301,10 @@ ApplicationWindow { onTriggered: Qt.quit() // Quit after paint on test build } - onClosing: function(close) { - if(!Modules.IO.saved) { + onClosing: { + if(!history.saved) { close.accepted = false - appMenu.openSaveUnsavedChangesDialog() - } - } - - /*! - \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() + appMenu.showSaveUnsavedChangesDialog() } } @@ -235,7 +335,7 @@ ApplicationWindow { Action { text: qsTr("&Update LogarithmPlotter") icon.name: 'update' - onTriggered: Qt.openUrlExternally("https://apps.ad5001.eu/logarithmplotter/") + onTriggered: Qt.openUrlExternally("https://dev.apps.ad5001.eu/logarithmplotter") } } @@ -246,27 +346,4 @@ ApplicationWindow { function showUpdateMenu() { appMenu.addMenu(updateMenu) } - - // Initializing modules - Component.onCompleted: { - Modules.IO.initialize({ root, settings, alert }) - Modules.Latex.initialize({ latex: Latex, helper: Helper }) - Modules.Settings.on("changed", (evt) => { - if(evt.property === "saveFilename") { - const fileName = evt.newValue.split('/').pop().split('\\').pop() - if(fileName !== "") - title = fileName - } - }) - Modules.IO.on("saved loaded", (evt) => { - // Refreshing sidebar - updateObjectsLists() - if(title.endsWith("*")) - title = title.substring(0, title.length-1) - }) - Modules.IO.on("modified", () => { - if(!title.endsWith("*")) - title = title+"*" - }) - } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/EditorDialog.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/EditorDialog.qml new file mode 100644 index 0000000..e4721ed --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/EditorDialog.qml @@ -0,0 +1,309 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Dialogs 1.3 as D +import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting +import "../js/objects.js" as Objects +import "../js/objs/common.js" as ObjectsCommons +import "../js/historylib.js" as HistoryLib +import "../js/utils.js" as Utils +import "../js/mathlib.js" as MathLib + +/*! + \qmltype EditorDialog + \inqmlmodule eu.ad5001.LogarithmPlotter.ObjectLists + \brief Dialog used to edit properties of objects. + + This class contains the dialog that allows to edit all properties of objects. + \todo In the future, this class should be optimized so that each property doesn't instanciate one instance of each setting type. + + \sa LogarithmPlotter, ObjectLists +*/ +D.Dialog { + id: objEditor + /*! + \qmlproperty string EditorDialog::objType + Type of object being edited by the dialog. + */ + property string objType: 'Point' + /*! + \qmlproperty int EditorDialog::objIndex + Index of the objects amongst the ones of it's type. + */ + property int objIndex: 0 + /*! + \qmlproperty var EditorDialog::obj + Instance of the object being edited. + */ + property var obj: Objects.currentObjects[objType][objIndex] + + title: "LogarithmPlotter" + width: 350 + height: 400 + + Label { + id: dlgTitle + anchors.left: parent.left + anchors.top: parent.top + verticalAlignment: TextInput.AlignVCenter + text: qsTr("Edit properties of %1 %2").arg(Objects.types[objEditor.objType].displayType()).arg(objEditor.obj.name) + font.pixelSize: 20 + color: sysPalette.windowText + } + + Column { + id: dlgProperties + anchors.top: dlgTitle.bottom + width: objEditor.width - 20 + spacing: 10 + + Setting.TextSetting { + id: nameProperty + height: 30 + label: qsTr("Name") + icon: "common/label.svg" + min: 1 + width: dlgProperties.width + value: objEditor.obj.name + onChanged: function(newValue) { + var newName = Utils.parseName(newValue) + if(newName != '' && objEditor.obj.name != newName) { + if(Objects.getObjectByName(newName) != null) { + newName = ObjectsCommons.getNewName(newName) + } + history.addToHistory(new HistoryLib.NameChanged( + objEditor.obj.name, objEditor.objType, newName + )) + Objects.currentObjects[objEditor.objType][objEditor.objIndex].name = newName + objEditor.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex] + objectListList.update() + } + } + } + + Setting.ComboBoxSetting { + id: labelContentProperty + height: 30 + width: dlgProperties.width + label: qsTr("Label content") + model: [qsTr("null"), qsTr("name"), qsTr("name + value")] + property var idModel: ["null", "name", "name + value"] + icon: "common/label.svg" + currentIndex: idModel.indexOf(objEditor.obj.labelContent) + onActivated: function(newIndex) { + if(idModel[newIndex] != objEditor.obj.labelContent) { + objEditor.obj.labelContent = idModel[newIndex] + objectListList.update() + } + } + } + + // Dynamic properties + Repeater { + id: dlgCustomProperties + + Item { + height: customPropComment.height + customPropText.height + customPropCheckBox.height + customPropCombo.height + customPropListDict.height + width: dlgProperties.width + property string label: qsTranslate('prop',modelData[0]) + property string icon: Utils.camelCase2readable(modelData[0]) + + // Item for comments + Label { + id: customPropComment + width: parent.width + height: visible ? implicitHeight : 0 + visible: modelData[0].startsWith('comment') + // Translated text with object name. + property string trText: visible ? qsTranslate('comment', modelData[1]).toString() : '' + text: (visible && trText.includes("%1") ? trText.arg(objEditor.obj.name) : trText).toString() + //color: sysPalette.windowText + wrapMode: Text.WordWrap + } + + // Setting for text & number settings as well as domains & expressions + Setting.TextSetting { + id: customPropText + height: visible ? 30 : 0 + width: parent.width + label: parent.label + icon: `settings/custom/${parent.icon}.svg` + isDouble: modelData[1] == 'number' + visible: paramTypeIn(modelData[1], ['Expression', 'Domain', 'string', 'number']) + defValue: visible ? { + 'Expression': () => Utils.simplifyExpression(objEditor.obj[modelData[0]].toEditableString()), + 'Domain': () => objEditor.obj[modelData[0]].toString(), + 'string': () => objEditor.obj[modelData[0]], + 'number': () => objEditor.obj[modelData[0]] + }[modelData[1]]() : "" + onChanged: function(newValue) { + var newValue = { + 'Expression': () => new MathLib.Expression(newValue), + 'Domain': () => MathLib.parseDomain(newValue), + 'string': () => newValue, + 'number': () => parseFloat(newValue) + }[modelData[1]]() + // Ensuring old and new values are different to prevent useless adding to history. + if(objEditor.obj[modelData[0]] != newValue) { + history.addToHistory(new HistoryLib.EditedProperty( + objEditor.obj.name, objEditor.objType, modelData[0], + objEditor.obj[modelData[0]], newValue + )) + objEditor.obj[modelData[0]] = newValue + Objects.currentObjects[objEditor.objType][objEditor.objIndex].update() + objectListList.update() + } + } + } + + // Setting for boolean + CheckBox { + id: customPropCheckBox + visible: modelData[1] == 'boolean' + height: visible ? 20 : 0 + width: parent.width + text: parent.label + //icon: visible ? `settings/custom/${parent.icon}.svg` : '' + + checked: visible ? objEditor.obj[modelData[0]] : false + onClicked: { + history.addToHistory(new HistoryLib.EditedProperty( + objEditor.obj.name, objEditor.objType, modelData[0], + objEditor.obj[modelData[0]], this.checked + )) + objEditor.obj[modelData[0]] = this.checked + Objects.currentObjects[objEditor.objType][objEditor.objIndex].update() + objectListList.update() + } + } + + // Setting when selecting data from an enum, or an object of a certain type. + Setting.ComboBoxSetting { + id: customPropCombo + width: dlgProperties.width + height: visible ? 30 : 0 + label: parent.label + icon: visible ? `settings/custom/${parent.icon}.svg` : '' + // True to select an object of type, false for enums. + property bool selectObjMode: paramTypeIn(modelData[1], ['ObjectType']) + property bool isRealObject: !selectObjMode || (modelData[1].objType != "ExecutableObject" && modelData[1].objType != "DrawableObject") + + // Base, untranslated version of the model. + property var baseModel: visible ? + (selectObjMode ? + Objects.getObjectsName(modelData[1].objType).concat( + isRealObject ? [qsTr("+ Create new %1").arg(Objects.types[modelData[1].objType].displayType())] : + []) : + modelData[1].values) + : [] + // Translated verison of the model. + model: selectObjMode ? baseModel : modelData[1].translatedValues + visible: paramTypeIn(modelData[1], ['ObjectType', 'Enum']) + currentIndex: baseModel.indexOf(selectObjMode ? objEditor.obj[modelData[0]].name : objEditor.obj[modelData[0]]) + + onActivated: function(newIndex) { + if(selectObjMode) { + // This is only done when what we're selecting are Objects. + // Setting object property. + var selectedObj = Objects.getObjectByName(baseModel[newIndex], modelData[1].objType) + if(newIndex != 0) { + // Make sure we don't set the object to null. + if(selectedObj == null) { + // Creating new object. + selectedObj = Objects.createNewRegisteredObject(modelData[1].objType) + history.addToHistory(new HistoryLib.CreateNewObject(selectedObj.name, modelData[1].objType, selectedObj.export())) + baseModel = Objects.getObjectsName(modelData[1].objType).concat( + isRealObject ? [qsTr("+ Create new %1").arg(Objects.types[modelData[1].objType].displayType())] : + []) + currentIndex = baseModel.indexOf(selectedObj.name) + } + selectedObj.requiredBy.push(Objects.currentObjects[objEditor.objType][objEditor.objIndex]) + //Objects.currentObjects[objEditor.objType][objEditor.objIndex].requiredBy = objEditor.obj[modelData[0]].filter((obj) => objEditor.obj.name != obj.name) + } + objEditor.obj.requiredBy = objEditor.obj.requiredBy.filter((obj) => objEditor.obj.name != obj.name) + history.addToHistory(new HistoryLib.EditedProperty( + objEditor.obj.name, objEditor.objType, modelData[0], + objEditor.obj[modelData[0]], selectedObj + )) + objEditor.obj[modelData[0]] = selectedObj + } else if(baseModel[newIndex] != objEditor.obj[modelData[0]]) { + // Ensuring new property is different to not add useless history entries. + history.addToHistory(new HistoryLib.EditedProperty( + objEditor.obj.name, objEditor.objType, modelData[0], + objEditor.obj[modelData[0]], baseModel[newIndex] + )) + objEditor.obj[modelData[0]] = baseModel[newIndex] + } + // Refreshing + Objects.currentObjects[objEditor.objType][objEditor.objIndex].update() + objectListList.update() + } + } + + // Setting to edit lists or dictionaries (e.g sequences & repartition function values) + Setting.ListSetting { + id: customPropListDict + width: parent.width + height: visible ? implicitHeight : 0 + + visible: paramTypeIn(modelData[1], ['List', 'Dict']) + label: parent.label + //icon: `settings/custom/${parent.icon}.svg` + dictionaryMode: paramTypeIn(modelData[1], ['Dict']) + keyType: dictionaryMode ? modelData[1].keyType : 'string' + valueType: visible ? modelData[1].valueType : 'string' + preKeyLabel: visible ? (dictionaryMode ? modelData[1].preKeyLabel : modelData[1].label).replace(/\{name\}/g, objEditor.obj.name) : '' + postKeyLabel: visible ? (dictionaryMode ? modelData[1].postKeyLabel : '').replace(/\{name\}/g, objEditor.obj.name) : '' + keyRegexp: dictionaryMode ? modelData[1].keyFormat : /^.+$/ + valueRegexp: visible ? modelData[1].format : /^.+$/ + forbidAdding: visible ? modelData[1].forbidAdding : false + + onChanged: { + var exported = exportModel() + history.addToHistory(new HistoryLib.EditedProperty( + objEditor.obj.name, objEditor.objType, modelData[0], + objEditor.obj[modelData[0]], exported + )) + //Objects.currentObjects[objEditor.objType][objEditor.objIndex][modelData[0]] = exported + objEditor.obj[modelData[0]] = exported + //Objects.currentObjects[objEditor.objType][objEditor.objIndex].update() + objEditor.obj.update() + objectListList.update() + } + + Component.onCompleted: { + if(visible) importModel(objEditor.obj[modelData[0]]) + } + } + } + } + } + + /*! + \qmlmethod void EditorDialog::show() + Shows the editor after the object to be edited is set. + */ + function show() { + dlgCustomProperties.model = [] // Reset + let objProps = Objects.types[objEditor.objType].properties() + dlgCustomProperties.model = Object.keys(objProps).map(prop => [prop, objProps[prop]]) // Converted to 2-dimentional array. + objEditor.open() + } +} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml similarity index 60% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml index 21559a9..bba2622 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,10 +16,11 @@ * along with this program. If not, see . */ -import QtQuick -import QtQuick.Controls +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import "../js/objects.js" as Objects +import "../js/historylib.js" as HistoryLib import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting -import eu.ad5001.LogarithmPlotter.Common /*! @@ -33,21 +34,6 @@ Column { id: createRow property var objectEditor 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 { id: createTitle @@ -60,12 +46,12 @@ Column { width: parent.width columns: 3 Repeater { - model: Object.keys(Modules.Objects.types) + model: Object.keys(Objects.types) Button { id: createBtn width: 96 - visible: Modules.Objects.types[modelData].createable() + visible: Objects.types[modelData].createable() height: visible ? width*0.8 : 0 // The KDE SDK is kinda buggy, so it respects neither specified color nor display propreties. //display: AbstractButton.TextUnderIcon @@ -93,7 +79,7 @@ Column { anchors.rightMargin: 4 horizontalAlignment: Text.AlignHCenter font.pixelSize: 14 - text: Modules.Objects.types[modelData].displayType() + text: Objects.types[modelData].displayType() wrapMode: Text.WordWrap clip: true } @@ -103,28 +89,13 @@ Column { ToolTip.text: label.text onClicked: { - let newObj = Modules.Objects.createNewRegisteredObject(modelData) - Modules.History.addToHistory(new JS.HistoryLib.CreateNewObject( - newObj.name, modelData, newObj.export() - )) + var newObj = Objects.createNewRegisteredObject(modelData) + history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export())) objectLists.update() - - let hasXProp = newObj.constructor.properties().hasOwnProperty('x') - let hasYProp = newObj.constructor.properties().hasOwnProperty('y') - 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) - } + objectEditor.obj = Objects.currentObjects[modelData][Objects.currentObjects[modelData].length - 1] + objectEditor.objType = modelData + objectEditor.objIndex = Objects.currentObjects[modelData].length - 1 + objectEditor.show() } } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml new file mode 100644 index 0000000..d19ddaa --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml @@ -0,0 +1,272 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +import QtQuick 2.12 +import QtQuick.Dialogs 1.3 as D +import QtQuick.Controls 2.12 +import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting +import "../js/objects.js" as Objects +import "../js/historylib.js" as HistoryLib + +/*! + \qmltype ObjectLists + \inqmlmodule eu.ad5001.LogarithmPlotter + \brief Tab of the drawer that allows the user to manage the objects. + + This item allows the user to syntheticly see all objects, while giving the user the ability + to show, hide, delete, change the location and color, as well as opening the editor dialog + for each object. + + \sa LogarithmPlotter, ObjectCreationGrid, ObjectLists +*/ +ScrollView { + id: objectListList + + signal changed() + + property var listViews: {'':''} // Needs to be initialized or will be undefined -_- + + + ScrollBar.horizontal.visible: false + ScrollBar.vertical.visible: true + + ListView { + id: objectsListView + model: Object.keys(Objects.types) + width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0) + implicitHeight: contentItem.childrenRect.height + footer.height + 10 + + delegate: ListView { + id: objTypeList + property string objType: objectsListView.model[index] + property var editingRows: [] + model: Objects.currentObjects[objType] + width: objectsListView.width + implicitHeight: contentItem.childrenRect.height + visible: model != undefined && model.length > 0 + interactive: false + + Component.onCompleted: objectListList.listViews[objType] = objTypeList // Listing in order to be refreshed + + header: Row { + width: typeHeaderText.width + typeVisibilityCheckBox.visible + height: visible ? 20 : 0 + visible: objTypeList.visible + + CheckBox { + id: typeVisibilityCheckBox + checked: Objects.currentObjects[objType] != undefined ? Objects.currentObjects[objType].every(obj => obj.visible) : true + onClicked: { + for(var obj of Objects.currentObjects[objType]) obj.visible = this.checked + for(var obj of objTypeList.editingRows) obj.objVisible = this.checked + objectListList.changed() + } + + ToolTip.visible: hovered + ToolTip.text: checked ? qsTr("Hide all %1").arg(Objects.types[objType].displayTypeMultiple()) : qsTr("Show all %1").arg(Objects.types[objType].displayTypeMultiple()) + } + + Label { + id: typeHeaderText + verticalAlignment: TextInput.AlignVCenter + text: qsTranslate("control", "%1: ").arg(Objects.types[objType].displayTypeMultiple()) + font.pixelSize: 20 + } + } + + delegate: Item { + id: controlRow + property var obj: Objects.currentObjects[objType][index] + property alias objVisible: objVisibilityCheckBox.checked + height: 40 + width: objTypeList.width + + Component.onCompleted: objTypeList.editingRows.push(controlRow) + + CheckBox { + id: objVisibilityCheckBox + checked: Objects.currentObjects[objType][index].visible + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 5 + onClicked: { + history.addToHistory(new HistoryLib.EditedVisibility( + Objects.currentObjects[objType][index].name, objType, this.checked + )) + Objects.currentObjects[objType][index].visible = this.checked + objectListList.changed() + controlRow.obj = Objects.currentObjects[objType][index] + } + + ToolTip.visible: hovered + ToolTip.text: checked ? + qsTr("Hide %1 %2").arg(Objects.types[objType].displayType()).arg(obj.name) : + qsTr("Show %1 %2").arg(Objects.types[objType].displayType()).arg(obj.name) + } + + Label { + id: objDescription + anchors.left: objVisibilityCheckBox.right + anchors.right: deleteButton.left + height: parent.height + verticalAlignment: TextInput.AlignVCenter + text: obj.getReadableString() + font.pixelSize: 14 + + MouseArea { + anchors.fill: parent + onClicked: { + objEditor.obj = Objects.currentObjects[objType][index] + objEditor.objType = objType + objEditor.objIndex = index + //objEditor.editingRow = controlRow + objEditor.show() + } + } + } + + Button { + id: pointerButton + width: parent.height - 10 + height: width + anchors.right: deleteButton.left + anchors.rightMargin: 5 + anchors.topMargin: 5 + + Setting.Icon { + id: icon + width: 18 + height: 18 + anchors.centerIn: parent + + color: sysPalette.windowText + source: '../icons/common/position.svg' + } + + property bool hasXProp: Objects.types[objType].properties().hasOwnProperty('x') + property bool hasYProp: Objects.types[objType].properties().hasOwnProperty('y') + visible: hasXProp || hasYProp + ToolTip.visible: hovered + ToolTip.text: qsTr("Set %1 %2 position").arg(Objects.types[objType].displayType()).arg(obj.name) + + onClicked: { + positionPicker.objType = objType + positionPicker.objName = obj.name + positionPicker.pickX = hasXProp + positionPicker.pickY = hasYProp + positionPicker.propertyX = 'x' + positionPicker.propertyY = 'y' + positionPicker.visible = true + + } + } + + Button { + id: deleteButton + width: parent.height - 10 + height: width + anchors.right: colorPickRect.left + anchors.rightMargin: 5 + anchors.topMargin: 5 + icon.name: 'delete' + icon.source: '../icons/common/delete.svg' + icon.color: sysPalette.buttonText + ToolTip.visible: hovered + ToolTip.text: qsTr("Delete %1 %2").arg(Objects.types[objType].displayType()).arg(obj.name) + + onClicked: { + history.addToHistory(new HistoryLib.DeleteObject( + obj.name, objType, obj.export() + )) + Objects.currentObjects[objType][index].delete() + Objects.currentObjects[objType].splice(index, 1) + objectListList.update() + } + } + + Rectangle { + id: colorPickRect + anchors.right: parent.right + anchors.rightMargin: 5 + anchors.topMargin: 5 + color: obj.color + width: parent.height - 10 + height: width + radius: Math.min(width, height) + border.width: 2 + border.color: sysPalette.windowText + + MouseArea { + anchors.fill: parent + onClicked: pickColor.open() + } + } + + D.ColorDialog { + id: pickColor + color: obj.color + title: qsTr("Pick new color for %1 %2").arg(Objects.types[objType].displayType()).arg(obj.name) + onAccepted: { + history.addToHistory(new HistoryLib.ColorChanged( + obj.name, objType, obj.color, color.toString() + )) + obj.color = color.toString() + controlRow.obj = Objects.currentObjects[objType][index] + objectListList.update() + } + } + } + } + + // Create items + footer: ObjectCreationGrid { + id: createRow + width: objectsListView.width + objectEditor: objEditor + objectLists: objectListList + } + } + + // Object editor + EditorDialog { + id: objEditor + } + + /*! + \qmlmethod void ObjectLists::update() + Updates the view of the ObjectLists. + */ + function update() { + objectListList.changed() + for(var objType in objectListList.listViews) { + objectListList.listViews[objType].model = Objects.currentObjects[objType] + } + } + + /*! + \qmlmethod void ObjectLists::paramTypeIn(var parameter, var types) + Checks if the type of the provided \c parameter is in \c types. + \note The type can be normal string types ('boolean', 'string', 'number'...) or object types (Enum, Dictionay, Object types...). If the latter, only the type of object type should be provided in \c types. E.g: if you want to check if the parameter is an enum, add "Enum" to types. + */ + function paramTypeIn(parameter, types = []) { + if(types.includes(parameter.toString())) return true + if(typeof parameter == 'object' && 'type' in parameter) + return types.includes(parameter.type) + return false + } +} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/qmldir b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/qmldir similarity index 78% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/qmldir rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/qmldir index cc26d7a..7cbf3ea 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/qmldir +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/qmldir @@ -2,4 +2,5 @@ module eu.ad5001.LogarithmPlotter.ObjectLists ObjectLists 1.0 ObjectLists.qml ObjectCreationGrid 1.0 ObjectCreationGrid.qml -ObjectRow 1.0 ObjectRow.qml +EditorDialog 1.0 EditorDialog.qml + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml new file mode 100644 index 0000000..6c4ddec --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml @@ -0,0 +1,217 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import "js/objects.js" as Objects +import "js/mathlib.js" as MathLib +import "js/historylib.js" as HistoryLib + +/*! + \qmltype PickLocationOverlay + \inqmlmodule eu.ad5001.LogarithmPlotter + \brief Overlay used to pick a new location for an object. + + Provides an overlay over the canvas that can be shown when the user clicks the "Set position" button + on a specific object. It allows the user to pick a new location on the canvas to place the object at. + This overlay allows to set the precision of the pick as well as whether the pick should be on the plot grid. + + \sa LogarithmPlotter, LogGraphCanvas +*/ +Item { + id: pickerRoot + visible: false + + /*! + \qmlproperty var PickLocationOverlay::canvas + logGraphCanvas instance. + */ + property var canvas + /*! + \qmlproperty string PickLocationOverlay::objType + Type of object whose position the user is picking. + */ + property string objType: 'Point' + /*! + \qmlproperty string PickLocationOverlay::objType + Name of the object whose position the user is picking. + */ + property string objName: 'A' + /*! + \qmlproperty bool PickLocationOverlay::pickX + true if the user should be picking a position on the x axis. + */ + property bool pickX: true + /*! + \qmlproperty bool PickLocationOverlay::pickY + true if the user should be picking a position on the y axis. + */ + property bool pickY: true + /*! + \qmlproperty string PickLocationOverlay::propertyX + Name of the object's property whose x value is being changed. + */ + property string propertyX: 'x' + /*! + \qmlproperty string PickLocationOverlay::propertyY + Name of the object's property whose y value is being changed. + */ + property string propertyY: 'y' + /*! + \qmlproperty int PickLocationOverlay::precision + Precision of the picked value (post-dot precision). + */ + property alias precision: precisionSlider.value + + Rectangle { + color: sysPalette.window + opacity: 0.35 + anchors.fill: parent + } + + MouseArea { + id: picker + anchors.fill: parent + hoverEnabled: parent.visible + cursorShape: Qt.CrossCursor + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { + if(mouse.button == Qt.LeftButton) { // Validate + if(parent.pickX) { + let newValue = picked.mouseX.toString() + newValue = { + 'Expression': () => new MathLib.Expression(newValue), + 'number': () => parseFloat(newValue) + }[Objects.types[objType].properties()[propertyX]]() + let obj = Objects.getObjectByName(objName, objType) + history.addToHistory(new HistoryLib.EditedProperty( + objName, objType, propertyX, obj[propertyX], newValue + )) + obj[propertyX] = newValue + obj.update() + objectLists.update() + } + if(parent.pickY) { + let newValue = picked.mouseY.toString() + newValue = { + 'Expression': () => new MathLib.Expression(newValue), + 'number': () => parseFloat(newValue) + }[Objects.types[objType].properties()[propertyY]]() + let obj = Objects.getObjectByName(objName, objType) + history.addToHistory(new HistoryLib.EditedProperty( + objName, objType, propertyY, obj[propertyY], newValue + )) + obj[propertyY] = newValue + obj.update() + objectLists.update() + } + } + pickerRoot.visible = false; + } + } + + Row { + height: precisionSlider.height + Text { + text: " "+ qsTr("Pointer precision:") + " " + color: 'black' + anchors.verticalCenter: parent.verticalCenter + } + + Slider { + id: precisionSlider + from: 0 + value: 2 + to: 10 + stepSize: 1 + ToolTip { + parent: precisionSlider.handle + visible: precisionSlider.pressed + text: precisionSlider.value.toFixed(0) + } + } + + CheckBox { + id: snapToGridCheckbox + text: qsTr("Snap to grid") + checked: false + } + } + + Rectangle { + id: xCursor + width: 1 + height: parent.height + color: 'black' + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: canvas.x2px(picked.mouseX) + visible: parent.pickX + } + + Rectangle { + id: yCursor + width: parent.width + height: 1 + color: 'black' + anchors.top: parent.top + anchors.left: parent.left + anchors.topMargin: canvas.y2px(picked.mouseY) + visible: parent.pickY + } + + Text { + id: picked + x: picker.mouseX - width - 5 + y: picker.mouseY - height - 5 + property double axisX: canvas.xaxisstep1 + property double mouseX: { + let xpos = canvas.px2x(picker.mouseX) + if(snapToGridCheckbox.checked) { + if(canvas.logscalex) { + // Calculate the logged power + let pow = Math.pow(10, Math.floor(Math.log10(xpos))) + return pow*Math.round(xpos/pow) + } else { + return canvas.xaxisstep1*Math.round(xpos/canvas.xaxisstep1) + } + } else { + return xpos.toFixed(parent.precision) + } + } + property double mouseY: { + let ypos = canvas.px2y(picker.mouseY) + if(snapToGridCheckbox.checked) { + return canvas.yaxisstep1*Math.round(ypos/canvas.yaxisstep1) + } else { + return ypos.toFixed(parent.precision) + } + } + color: 'black' + text: { + if(parent.pickX && parent.pickY) + return `(${mouseX}, ${mouseY})` + if(parent.pickX) + return `X = ${mouseX}` + if(parent.pickY) + return `Y = ${mouseY}` + } + } + + +} diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/About.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/About.qml new file mode 100644 index 0000000..7e1687c --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/About.qml @@ -0,0 +1,124 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +import QtQuick 2.12 +import QtQuick.Dialogs 1.3 as D +import QtQuick.Controls 2.12 + +/*! + \qmltype About + \inqmlmodule eu.ad5001.LogarithmPlotter.Popup + \brief About popup of LogarithmPlotter. + + \sa LogarithmPlotter +*/ +D.Dialog { + id: about + title: qsTr("About LogarithmPlotter") + width: 400 + height: 600 + + Image { + id: logo + source: "../icons/logarithmplotter.svg" + sourceSize.width: 64 + sourceSize.height: 64 + width: 64 + height: 64 + anchors.horizontalCenter: parent.horizontalCenter + anchors.rightMargin: width/2 + anchors.top: parent.top + anchors.topMargin: 10 + } + + Label { + id: appName + anchors.top: logo.bottom + anchors.left: parent.left + anchors.topMargin: 10 + horizontalAlignment: Text.AlignHCenter + width: parent.width + wrapMode: Text.WordWrap + font.pixelSize: 25 + text: qsTr("LogarithmPlotter v%1").arg(Helper.getVersion()) + } + + Label { + id: description + anchors.top: appName.bottom + anchors.left: parent.left + anchors.topMargin: 10 + horizontalAlignment: Text.AlignHCenter + width: parent.width + wrapMode: Text.WordWrap + font.pixelSize: 18 + text: qsTr("2D plotter software to make BODE plots, sequences and repartition functions.") + } + + Label { + id: debugInfos + anchors.top: description.bottom + anchors.left: parent.left + anchors.topMargin: 10 + horizontalAlignment: Text.AlignHCenter + width: parent.width + wrapMode: Text.WordWrap + font.pixelSize: 14 + text: Helper.getDebugInfos() + } + + Label { + id: copyrightInfos + anchors.top: debugInfos.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + width: Math.min(410, parent.width) + wrapMode: Text.WordWrap + textFormat: Text.RichText + font.pixelSize: 13 + text: "Copyright © 2022 Ad5001 <mail@ad5001.eu>
+
+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 http://www.gnu.org/licenses/." + onLinkActivated: Qt.openUrlExternally(link) + } + + Row { + anchors.top: copyrightInfos.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + spacing: 5 + + Button { + id: openIssueButton + text: qsTr('Report a bug') + icon.name: 'tools-report-bug' + onClicked: Qt.openUrlExternally('https://git.ad5001.eu/Ad5001/LogarithmPlotter') + } + + Button { + id: officialWebsiteButton + text: qsTr('Official website') + icon.name: 'web-browser' + onClicked: Qt.openUrlExternally('https://apps.ad5001.eu/logarithmplotter/') + } + } +} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Alert.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Alert.qml similarity index 98% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Alert.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Alert.qml index 98e37d4..4b54697 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Alert.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Alert.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import QtQuick +import QtQuick 2.12 /*! \qmltype Alert \inqmlmodule eu.ad5001.LogarithmPlotter.Popup diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Changelog.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Changelog.qml similarity index 66% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Changelog.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Changelog.qml index 1797b9d..0bf9247 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Changelog.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Changelog.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -import QtQuick -import QtQuick.Controls +import QtQuick 2.12 +import QtQuick.Controls 2.12 /*! \qmltype Changelog @@ -32,7 +32,7 @@ Popup { id: changelogPopup x: (parent.width-width)/2 y: Math.max(20, (parent.height-height)/2) - width: 800 + width: changelog.width+40 height: Math.min(parent.height-40, 500) modal: true focus: true @@ -44,62 +44,42 @@ Popup { */ property bool changelogNeedsFetching: true - onAboutToShow: if(changelogNeedsFetching) { - Helper.fetchChangelog().then((fetchedText) => { - changelogNeedsFetching = false - changelog.text = fetchedText - changelogView.contentItem.implicitHeight = changelog.height - }, (error) => { - const e = qsTranslate("changelog", "Could not fetch update: {}.").replace('{}', error) - console.error(e) - changelogNeedsFetching = false - changelog.text = e - changelogView.contentItem.implicitHeight = changelog.height - }) + onAboutToShow: if(changelogNeedsFetching) Helper.fetchChangelog() + + Connections { + target: Helper + function onChangelogFetched(chl) { + changelogNeedsFetching = false; + changelog.text = chl + } } ScrollView { - id: changelogView anchors.top: parent.top anchors.topMargin: 10 anchors.left: parent.left anchors.leftMargin: 10 - anchors.right: parent.right - anchors.rightMargin: 10 anchors.bottom: doneBtn.top anchors.bottomMargin: 10 clip: true - Label { id: changelog color: sysPalette.windowText - width: 760 - wrapMode: Text.WordWrap textFormat: TextEdit.MarkdownText text: qsTr("Fetching changelog...") onLinkActivated: Qt.openUrlExternally(link) + } } - Rectangle { - id: bottomSeparator - opacity: 0.3 - color: sysPalette.windowText - width: parent.width * 2 / 3 - height: 1 - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: doneBtn.top - anchors.bottomMargin: 7 - } - Button { id: doneBtn - text: qsTr("Close") + text: qsTr("Done") font.pixelSize: 18 anchors.bottom: parent.bottom - anchors.bottomMargin: 7 + anchors.bottomMargin: 10 anchors.horizontalCenter: parent.horizontalCenter onClicked: changelogPopup.close() } diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/FileDialog.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/FileDialog.qml similarity index 87% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/FileDialog.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/FileDialog.qml index 00dbdae..dc5c8c8 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/FileDialog.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/FileDialog.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import Qt.labs.platform +import QtQuick.Dialogs 1.3 as D /*! \qmltype FileDialog @@ -25,7 +25,7 @@ import Qt.labs.platform \sa LogarithmPlotter, Settings */ -FileDialog { +D.FileDialog { id: fileDialog property bool exportMode: false @@ -33,6 +33,6 @@ FileDialog { title: exportMode ? qsTr("Export Logarithm Plot file") : qsTr("Import Logarithm Plot file") nameFilters: ["Logarithm Plot File (*.lpf)", "All files (*)"] - defaultSuffix: 'lpf' - fileMode: exportMode ? FileDialog.SaveFile : FileDialog.OpenFile + folder: shortcuts.documents + selectExisting: !exportMode } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/GreetScreen.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/GreetScreen.qml new file mode 100644 index 0000000..15c1587 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/GreetScreen.qml @@ -0,0 +1,167 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import "../js/math/latex.js" as Latex + +/*! + \qmltype GreetScreen + \inqmlmodule eu.ad5001.LogarithmPlotter.Popup + \brief Overlay displayed when LogarithmPlotter is launched for the first time or when it was just updated. + + It contains several settings as well as an easy access to the changelog + + \sa LogarithmPlotter, Settings, AppMenuBar, Changelog +*/ +Popup { + id: greetingPopup + x: (parent.width-width)/2 + y: Math.max(20, (parent.height-height)/2) + width: Math.max(welcome.width+70, checkForUpdatesSetting.width, resetRedoStackSetting.width)+20 + height: Math.min(parent.height-40, 500) + modal: true + focus: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + + Item { + id: welcome + height: logo.height + width: logo.width + 10 + welcomeText.width + anchors.top: parent.top + anchors.topMargin: (parent.width-width)/2 + 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 + anchors.left: logo.right + anchors.leftMargin: 10 + //width: parent.width + wrapMode: Text.WordWrap + font.pixelSize: 32 + text: qsTr("Welcome to LogarithmPlotter") + } + } + + Label { + id: versionText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: welcome.bottom + anchors.topMargin: 10 + //width: parent.width + 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 + anchors.top: versionText.bottom + anchors.topMargin: 40 + //width: parent.width + 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.horizontalCenter: parent.horizontalCenter + anchors.top: helpText.bottom + anchors.topMargin: 10 + checked: Helper.getSettingBool("check_for_updates") + text: qsTr('Check for updates on startup (requires online connectivity)') + onClicked: { + Helper.setSettingBool("check_for_updates", checked) + //checkForUpdatesMenuSetting.checked = checked + } + } + + CheckBox { + id: resetRedoStackSetting + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: checkForUpdatesSetting.bottom + 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) + //resetRedoStackMenuSetting.checked = checked + } + } + + CheckBox { + id: enableLatexSetting + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: resetRedoStackSetting.bottom + checked: Helper.getSettingBool("enable_latex") + text: qsTr('Enable LaTeX rendering') + onClicked: { + Helper.setSettingBool("enable_latex", checked) + Latex.enabled = checked + drawCanvas.requestPaint() + } + } + + Row { + anchors.bottom: parent.bottom + anchors.bottomMargin: 10 + spacing: 10 + anchors.horizontalCenter: parent.horizontalCenter + + Button { + id: userManualBtn + text: qsTr("User manual") + font.pixelSize: 18 + onClicked: Qt.openUrlExternally("https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/_Sidebar") + } + + Button { + id: changelogBtn + text: qsTr("Changelog") + font.pixelSize: 18 + onClicked: changelog.open() + } + + Button { + id: doneBtn + text: qsTr("Done") + font.pixelSize: 18 + onClicked: greetingPopup.close() + } + } + + Component.onCompleted: if(Helper.getSetting("last_install_greet") != Helper.getVersion()) { + greetingPopup.open() + } + + onClosed: Helper.setSetting("last_install_greet", Helper.getVersion()) +} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/qmldir b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/qmldir similarity index 57% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/qmldir rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/qmldir index 3cf03fa..a37ae7c 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/qmldir +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/qmldir @@ -1,11 +1,8 @@ module eu.ad5001.LogarithmPlotter.Popup -Alert 1.0 Alert.qml About 1.0 About.qml -BaseDialog 1.0 BaseDialog.qml -Changelog 1.0 Changelog.qml +Alert 1.0 Alert.qml FileDialog 1.0 FileDialog.qml GreetScreen 1.0 GreetScreen.qml -InsertCharacter 1.0 InsertCharacter.qml -Preferences 1.0 Preferences.qml -ThanksTo 1.0 ThanksTo.qml +Changelog 1.0 Changelog.qml + diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ComboBoxSetting.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ComboBoxSetting.qml similarity index 97% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ComboBoxSetting.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ComboBoxSetting.qml index a4dc93b..bd572e3 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ComboBoxSetting.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ComboBoxSetting.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -import QtQuick -import QtQuick.Controls +import QtQuick 2.12 +import QtQuick.Controls 2.12 /*! \qmltype ComboBoxSetting @@ -114,7 +114,6 @@ Item { anchors.left: iconLabel.right anchors.leftMargin: icon == "" ? 0 : 5 height: 30 - width: Math.max(85, implicitWidth) anchors.top: parent.top verticalAlignment: TextInput.AlignVCenter text: qsTranslate("control", "%1: ").arg(control.label) diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/Icon.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/Icon.qml similarity index 81% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/Icon.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/Icon.qml index 6ba76aa..d1b7b58 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/Icon.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/Icon.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -15,9 +15,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -import QtQuick -import QtQuick.Window -import QtQuick.Controls.impl +import QtQuick 2.7 +import QtGraphicalEffects 1.0 /*! \qmltype Icon @@ -41,16 +40,19 @@ Item { \qmlproperty string Icon::source Path of the icon image source. */ - property alias sourceSize: img.sourceS + property alias sourceSize: img.sourceSize.width - ColorImage { + Image { id: img height: parent.height width: parent.width - // visible: false - property int sourceS: width*Screen.devicePixelRatio - sourceSize.width: sourceS - sourceSize.height: sourceS + //smooth: true + visible: false + sourceSize.height: sourceSize.width + } + ColorOverlay { + anchors.fill: img + source: img color: parent.color } } diff --git a/common/.mocharc.jsonc b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/LatexExpression.qml similarity index 79% rename from common/.mocharc.jsonc rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/LatexExpression.qml index 56a6b31..bc8897c 100644 --- a/common/.mocharc.jsonc +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/LatexExpression.qml @@ -1,25 +1,28 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2024 Ad5001 - * + * Copyright (C) 2022 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 . */ -{ - "recursive": true, - "require": [ - "esm", - "./test/hooks.mjs" - ] + +import QtQuick 2.12 +import QtQuick.Controls 2.12 + +Image { + id: expr + property string expression + + src: Latex.render(expression).split(',')[0] } diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ListSetting.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ListSetting.qml similarity index 96% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ListSetting.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ListSetting.qml index daad433..c71fddd 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ListSetting.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ListSetting.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -import QtQuick -import QtQuick.Controls -import QtQml.Models +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQml.Models 2.12 /*! \qmltype ListSetting @@ -140,8 +140,8 @@ Column { visible: control.dictionaryMode height: parent.height width: visible ? 50 : 0 - validator: RegularExpressionValidator { - regularExpression: control.keyRegexp + validator: RegExpValidator { + regExp: control.keyRegexp } verticalAlignment: TextInput.AlignVCenter horizontalAlignment: TextInput.AlignHCenter @@ -180,8 +180,8 @@ Column { id: valueInput height: parent.height width: parent.width - x - deleteButton.width - 5 - validator: RegularExpressionValidator { - regularExpression: control.valueRegexp + validator: RegExpValidator { + regExp: control.valueRegexp } verticalAlignment: TextInput.AlignVCenter horizontalAlignment: TextInput.AlignHCenter diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml similarity index 63% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml index bc105aa..d9239c8 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,9 +16,8 @@ * along with this program. If not, see . */ -import QtQuick.Controls -import QtQuick -import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup +import QtQuick.Controls 2.12 +import QtQuick 2.12 /*! \qmltype TextSetting @@ -37,7 +36,7 @@ Item { Emitted when the value of the text has been changed. The corresponding handler is \c onChanged. */ - signal changed(var newValue) + signal changed(string newValue) /*! \qmlproperty bool TextSetting::isInt @@ -49,12 +48,6 @@ Item { If true, the input is being parsed an double before being emitting the \a changed signal. */ 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 Minimum value for numbers that can be entered into the input. @@ -100,14 +93,14 @@ Item { id: labelItem anchors.left: iconLabel.right anchors.leftMargin: icon == "" ? 0 : 5 - anchors.top: parent.top height: parent.height - width: visible ? Math.max(85, implicitWidth) : 0 + anchors.top: parent.top verticalAlignment: TextInput.AlignVCenter //color: sysPalette.windowText text: visible ? qsTranslate("control", "%1: ").arg(control.label) : "" visible: control.label != "" } + TextField { id: input @@ -119,23 +112,17 @@ Item { verticalAlignment: TextInput.AlignVCenter horizontalAlignment: control.label == "" ? TextInput.AlignLeft : TextInput.AlignHCenter color: sysPalette.windowText - validator: RegularExpressionValidator { - regularExpression: control.isInt ? /-?[0-9]+/ : control.isDouble ? /-?[0-9]+(\.[0-9]+)?/ : /.+/ + validator: RegExpValidator { + regExp: control.isInt ? /-?[0-9]+/ : control.isDouble ? /-?[0-9]+(\.[0-9]+)?/ : /.+/ } focus: true text: control.defValue selectByMouse: true - onEditingFinished: function() { - if(insertButton.focus || insertPopup.focus) return - let value = text - if(control.isInt) { - let parsed = parseInt(value) - value = isNaN(parsed) ? control.min : Math.max(control.min,parsed) - } else if(control.isDouble) { - let parsed = parseFloat(value) - value = isNaN(parsed) ? control.min : Math.max(control.min,parsed) - } - if(value !== "" && value.toString() != defValue) { + onEditingFinished: { + var value = text + if(control.isInt) value = Math.max(control.min,parseInt(value).toString()=="NaN"?control.min:parseInt(value)) + if(control.isDouble) value = Math.max(control.min,parseFloat(value).toString()=="NaN"?control.min:parseFloat(value)) + if(value != "" && value.toString() != defValue) { control.changed(value) defValue = value.toString() } @@ -143,43 +130,61 @@ Item { } Button { - id: insertButton + text: "α" anchors.right: parent.right anchors.rightMargin: 5 anchors.verticalCenter: parent.verticalCenter width: 20 height: width visible: !isInt && !isDouble - - Icon { - id: icon - width: 12 - height: 12 - anchors.centerIn: parent - - color: sysPalette.windowText - source: '../icons/properties/expression.svg' - } - - onClicked: { - insertPopup.open() - insertPopup.setFocus() - } + onClicked: insertPopup.open() } - Popup.InsertCharacter { + Popup { id: insertPopup + x: Math.round((parent.width - width) / 2) + y: Math.round((parent.height - height) / 2) + width: 280 + height: insertGrid.insertChars.length/insertGrid.columns*(width/insertGrid.columns) + modal: true + focus: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent - x: parent.width - width - y: parent.height - - onSelected: function(c) { - input.insert(input.cursorPosition, c) - } - - onClosed: function() { - focus = false - input.focus = true + Grid { + id: insertGrid + width: parent.width + columns: 7 + + property var insertChars: [ + "α","β","γ","δ","ε","ζ","η", + "π","θ","κ","λ","μ","ξ","ρ", + "ς","σ","τ","φ","χ","ψ","ω", + "Γ","Δ","Θ","Λ","Ξ","Π","Σ", + "Φ","Ψ","Ω","ₐ","ₑ","ₒ","ₓ", + "ₕ","ₖ","ₗ","ₘ","ₙ","ₚ","ₛ", + "ₜ","¹","²","³","⁴","⁵","⁶", + "⁷","⁸","⁹","⁰","₁","₂","₃", + "₄","₅","₆","₇","₈","₉","₀" + + ] + Repeater { + model: parent.insertChars.length + + Button { + id: insertBtn + width: insertGrid.width/insertGrid.columns + height: width + text: insertGrid.insertChars[modelData] + flat: text == " " + font.pixelSize: 18 + + onClicked: { + input.insert(input.cursorPosition, insertGrid.insertChars[modelData]) + insertPopup.close() + input.focus = true + } + } + } } } } diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/qmldir b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/qmldir similarity index 63% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/qmldir rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/qmldir index bb78bbe..22ad816 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/qmldir +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/qmldir @@ -1,8 +1,7 @@ module eu.ad5001.LogarithmPlotter.Setting -AutocompletionCategory 1.0 AutocompletionCategory.qml ComboBoxSetting 1.0 ComboBoxSetting.qml -ExpressionEditor 1.0 ExpressionEditor.qml Icon 1.0 Icon.qml ListSetting 1.0 ListSetting.qml TextSetting 1.0 TextSetting.qml + diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml similarity index 59% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml index 64e9f61..6bde410 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,11 +16,11 @@ * along with this program. If not, see . */ -import QtQuick -import QtQuick.Controls +import QtQuick.Controls 2.12 +import QtQuick 2.12 import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup -import eu.ad5001.LogarithmPlotter.Common +import "js/utils.js" as Utils /*! \qmltype Settings @@ -44,83 +44,88 @@ ScrollView { Zoom on the x axis of the diagram, provided from settings. \sa Settings */ - property double xzoom: Helper.getSetting('default_graph.xzoom') + property double xzoom: 100 /*! \qmlproperty double Settings::yzoom Zoom on the y axis of the diagram, provided from settings. \sa Settings */ - property double yzoom: Helper.getSetting('default_graph.yzoom') + property double yzoom: 10 /*! \qmlproperty double Settings::xmin Minimum x of the diagram, provided from settings. \sa Settings */ - property double xmin: Helper.getSetting('default_graph.xmin') + property double xmin: 5/10 /*! \qmlproperty double Settings::ymax Maximum y of the diagram, provided from settings. \sa Settings */ - property double ymax: Helper.getSetting('default_graph.ymax') + property double ymax: 25 /*! \qmlproperty string Settings::xaxisstep Step of the x axis graduation, provided from settings. \note: Only available in non-logarithmic mode. \sa Settings */ - property string xaxisstep: Helper.getSetting('default_graph.xaxisstep') + property string xaxisstep: "4" /*! \qmlproperty string Settings::yaxisstep Step of the y axis graduation, provided from settings. \sa Settings */ - property string yaxisstep: Helper.getSetting('default_graph.yaxisstep') + property string yaxisstep: "4" /*! \qmlproperty string Settings::xlabel Label used on the x axis, provided from settings. \sa Settings */ - property string xlabel: Helper.getSetting('default_graph.xlabel') + property string xlabel: "" /*! \qmlproperty string Settings::ylabel Label used on the y axis, provided from settings. \sa Settings */ - property string ylabel: Helper.getSetting('default_graph.ylabel') + property string ylabel: "" /*! \qmlproperty double Settings::linewidth Width of lines that will be drawn into the canvas, provided from settings. \sa Settings */ - property double linewidth: Helper.getSetting('default_graph.linewidth') + property double linewidth: 1 /*! \qmlproperty double Settings::textsize Font size of the text that will be drawn into the canvas, provided from settings. \sa Settings */ - property double textsize: Helper.getSetting('default_graph.textsize') + property double textsize: 18 /*! \qmlproperty bool Settings::logscalex true if the canvas should be in logarithmic mode, false otherwise. Provided from settings. \sa Settings */ - property bool logscalex: Helper.getSetting('default_graph.logscalex') + property bool logscalex: true /*! \qmlproperty bool Settings::showxgrad true if the x graduation should be shown, false otherwise. Provided from settings. \sa Settings */ - property bool showxgrad: Helper.getSetting('default_graph.showxgrad') + property bool showxgrad: true /*! \qmlproperty bool Settings::showygrad true if the y graduation should be shown, false otherwise. Provided from settings. \sa Settings */ - property bool showygrad: Helper.getSetting('default_graph.showygrad') + property bool showygrad: true + /*! + \qmlproperty bool Settings::saveFilename + Path of the currently opened file. Empty if no file is opened. + */ + property string saveFilename: "" Column { spacing: 10 @@ -130,21 +135,16 @@ ScrollView { Popup.FileDialog { id: fdiag onAccepted: { - let filePath = fdiag.currentFile.toString().substr(7) - if(filePath.at(2) === ":") // Windows path, remove the leading slash. - filePath = filePath.substr(1) - Modules.Settings.set("saveFilename", filePath) + var filePath = fileUrl.toString().substr(7) + settings.saveFilename = filePath if(exportMode) { - Modules.IO.saveDiagram(filePath) + root.saveDiagram(filePath) } else { - Modules.IO.loadDiagram(filePath) - // Adding labels. - if(xAxisLabel.find(Modules.Settings.xlabel) === -1) - xAxisLabel.model.append({text: Modules.Settings.xlabel}) - xAxisLabel.editText = Modules.Settings.xlabel - if(yAxisLabel.find(Modules.Settings.ylabel) === -1) - yAxisLabel.model.append({text: Modules.Settings.ylabel}) - yAxisLabel.editText = Modules.Settings.ylabel + root.loadDiagram(filePath) + if(xAxisLabel.find(settings.xlabel) == -1) xAxisLabel.model.append({text: settings.xlabel}) + xAxisLabel.editText = settings.xlabel + if(yAxisLabel.find(settings.ylabel) == -1) yAxisLabel.model.append({text: settings.ylabel}) + yAxisLabel.editText = settings.ylabel } } } @@ -155,39 +155,28 @@ ScrollView { height: 30 isDouble: true label: qsTr("X Zoom") - min: 0.1 + min: 1 icon: "settings/xzoom.svg" width: settings.settingWidth - + value: settings.xzoom.toFixed(2) onChanged: function(newValue) { - Modules.Settings.set("xzoom", newValue, true) + settings.xzoom = newValue settings.changed() } - - function update(newValue) { - value = Modules.Settings.xzoom.toFixed(2) - maxX.update() - } } Setting.TextSetting { id: zoomY height: 30 isDouble: true - min: 0.1 label: qsTr("Y Zoom") icon: "settings/yzoom.svg" width: settings.settingWidth - + value: settings.yzoom.toFixed(2) onChanged: function(newValue) { - Modules.Settings.set("yzoom", newValue, true) + settings.yzoom = newValue settings.changed() } - - function update(newValue) { - value = Modules.Settings.yzoom.toFixed(2) - minY.update() - } } // Positioning the graph @@ -199,18 +188,14 @@ ScrollView { label: qsTr("Min X") icon: "settings/xmin.svg" width: settings.settingWidth - + defValue: settings.xmin onChanged: function(newValue) { - Modules.Settings.set("xmin", newValue, true) - settings.changed() - } - - function update(newValue) { - let newVal = Modules.Settings.xmin - if(newVal > 1e-5) - newVal = newVal.toDecimalPrecision(8) - value = newVal - maxX.update() + if(parseFloat(maxX.value) > newValue) { + settings.xmin = newValue + settings.changed() + } else { + alert.show("Minimum x value must be inferior to maximum.") + } } } @@ -222,16 +207,11 @@ ScrollView { label: qsTr("Max Y") icon: "settings/ymax.svg" width: settings.settingWidth - + defValue: settings.ymax onChanged: function(newValue) { - Modules.Settings.set("ymax", newValue, true) + settings.ymax = newValue settings.changed() } - - function update() { - value = Modules.Settings.ymax - minY.update() - } } Setting.TextSetting { @@ -242,24 +222,15 @@ ScrollView { label: qsTr("Max X") icon: "settings/xmax.svg" width: settings.settingWidth - + value: canvas.px2x(canvas.canvasSize.width).toFixed(2) onChanged: function(xvaluemax) { - if(xvaluemax > Modules.Settings.xmin) { - const newXZoom = Modules.Settings.xzoom * canvas.width/(Modules.Canvas.x2px(xvaluemax)) // Adjusting zoom to fit. = (end)/(px of current point) - Modules.Settings.set("xzoom", newXZoom, true) - zoomX.update() + if(xvaluemax > settings.xmin) { + settings.xzoom = settings.xzoom * canvas.canvasSize.width/(canvas.x2px(xvaluemax)) // Adjusting zoom to fit. = (end)/(px of current point) settings.changed() } else { alert.show("Maximum x value must be superior to minimum.") } } - - function update() { - let newVal = Modules.Canvas.px2x(canvas.width) - if(newVal > 1e-5) - newVal = newVal.toDecimalPrecision(8) - value = newVal - } } Setting.TextSetting { @@ -270,56 +241,42 @@ ScrollView { label: qsTr("Min Y") icon: "settings/ymin.svg" width: settings.settingWidth - + defValue: canvas.px2y(canvas.canvasSize.height).toFixed(2) onChanged: function(yvaluemin) { if(yvaluemin < settings.ymax) { - const newYZoom = Modules.Settings.yzoom * canvas.height/(Modules.Canvas.y2px(yvaluemin)) // Adjusting zoom to fit. = (end)/(px of current point) - Modules.Settings.set("yzoom", newYZoom, true) - zoomY.update() + settings.yzoom = settings.yzoom * canvas.canvasSize.height/(canvas.y2px(yvaluemin)) // Adjusting zoom to fit. = (end)/(px of current point) settings.changed() } else { alert.show("Minimum y value must be inferior to maximum.") } } - - function update() { - value = Modules.Canvas.px2y(canvas.height).toDecimalPrecision(8) - } } Setting.TextSetting { id: xAxisStep height: 30 - category: "expression" label: qsTr("X Axis Step") icon: "settings/xaxisstep.svg" width: settings.settingWidth - + defValue: settings.xaxisstep + visible: !settings.logscalex onChanged: function(newValue) { - Modules.Settings.set("xaxisstep", newValue, true) + settings.xaxisstep = newValue settings.changed() } - - function update() { - value = Modules.Settings.xaxisstep - visible = !Modules.Settings.logscalex - } } Setting.TextSetting { id: yAxisStep height: 30 - category: "expression" label: qsTr("Y Axis Step") icon: "settings/yaxisstep.svg" width: settings.settingWidth - + defValue: settings.yaxisstep onChanged: function(newValue) { - Modules.Settings.set("yaxisstep", newValue, true) + settings.yaxisstep = newValue settings.changed() } - - function update() { value = Modules.Settings.yaxisstep } } Setting.TextSetting { @@ -330,13 +287,11 @@ ScrollView { min: 1 icon: "settings/linewidth.svg" width: settings.settingWidth - + defValue: settings.linewidth onChanged: function(newValue) { - Modules.Settings.set("linewidth", newValue, true) + settings.linewidth = newValue settings.changed() } - - function update() { value = Modules.Settings.linewidth } } Setting.TextSetting { @@ -347,13 +302,11 @@ ScrollView { min: 1 icon: "settings/textsize.svg" width: settings.settingWidth - + defValue: settings.textsize onChanged: function(newValue) { - Modules.Settings.set("textsize", newValue, true) + settings.textsize = newValue settings.changed() } - - function update() { value = Modules.Settings.textsize } } Setting.ComboBoxSetting { @@ -362,31 +315,24 @@ ScrollView { width: settings.settingWidth label: qsTr('X Label') icon: "settings/xlabel.svg" - editable: true model: ListModel { ListElement { text: "" } ListElement { text: "x" } ListElement { text: "ω (rad/s)" } } - + currentIndex: find(settings.xlabel) + editable: true onAccepted: function(){ - editText = JS.Utils.parseName(editText, false) - if(find(editText) === -1) model.append({text: editText}) - currentIndex = find(editText) - Modules.Settings.set("xlabel", editText, true) + editText = Utils.parseName(editText, false) + if (find(editText) === -1) model.append({text: editText}) + settings.xlabel = editText settings.changed() } - onActivated: function(selectedId) { - Modules.Settings.set("xlabel", model.get(selectedId).text, true) + settings.xlabel = model.get(selectedId).text settings.changed() } - - function update() { - editText = Modules.Settings.xlabel - if(find(editText) === -1) model.append({text: editText}) - currentIndex = find(editText) - } + Component.onCompleted: editText = settings.xlabel } Setting.ComboBoxSetting { @@ -395,7 +341,6 @@ ScrollView { width: settings.settingWidth label: qsTr('Y Label') icon: "settings/ylabel.svg" - editable: true model: ListModel { ListElement { text: "" } ListElement { text: "y" } @@ -404,52 +349,39 @@ ScrollView { ListElement { text: "φ (deg)" } ListElement { text: "φ (rad)" } } - + currentIndex: find(settings.ylabel) + editable: true onAccepted: function(){ - editText = JS.Utils.parseName(editText, false) - if(find(editText) === -1) model.append({text: editText}) - currentIndex = find(editText) - Modules.Settings.set("ylabel", editText, true) + editText = Utils.parseName(editText, false) + if (find(editText) === -1) model.append({text: editText, yaxisstep: root.yaxisstep}) + settings.ylabel = editText settings.changed() } - onActivated: function(selectedId) { - Modules.Settings.set("ylabel", model.get(selectedId).text, true) + settings.ylabel = model.get(selectedId).text settings.changed() } - - function update() { - editText = Modules.Settings.ylabel - if(find(editText) === -1) model.append({text: editText}) - currentIndex = find(editText) - } + Component.onCompleted: editText = settings.ylabel } CheckBox { id: logScaleX + checked: settings.logscalex text: qsTr('X Log scale') onClicked: { - Modules.Settings.set("logscalex", checked, true) - if(Modules.Settings.xmin <= 0) // Reset xmin to prevent crash. - Modules.Settings.set("xmin", .5) + settings.logscalex = checked settings.changed() } - - function update() { - checked = Modules.Settings.logscalex - xAxisStep.update() - } } CheckBox { id: showXGrad + checked: settings.showxgrad text: qsTr('Show X graduation') onClicked: { - Modules.Settings.set("showxgrad", checked, true) + settings.showxgrad = checked settings.changed() } - - function update() { checked = Modules.Settings.showxgrad } } CheckBox { @@ -457,10 +389,9 @@ ScrollView { checked: settings.showygrad text: qsTr('Show Y graduation') onClicked: { - Modules.Settings.set("showygrad", checked, true) + settings.showygrad = checked settings.changed() } - function update() { checked = Modules.Settings.showygrad } } Button { @@ -506,10 +437,10 @@ ScrollView { Saves the current canvas in the opened file. If no file is currently opened, prompts to pick a save location. */ function save() { - if(Modules.Settings.saveFilename == "") { + if(settings.saveFilename == "") { saveAs() } else { - Modules.IO.saveDiagram(Modules.Settings.saveFilename) + root.saveDiagram(settings.saveFilename) } } @@ -530,30 +461,4 @@ ScrollView { fdiag.exportMode = false fdiag.open() } - - /** - * Initializing the settings - */ - Component.onCompleted: function() { - const matchedElements = new Map([ - ["xzoom", zoomX], - ["yzoom", zoomY], - ["xmin", minX], - ["ymax", maxY], - ["xaxisstep", xAxisStep], - ["yaxisstep", yAxisStep], - ["xlabel", xAxisLabel], - ["ylabel", yAxisLabel], - ["linewidth", lineWidth], - ["textsize", textSize], - ["logscalex", logScaleX], - ["showxgrad", showXGrad], - ["showygrad", showYGrad] - ]) - Modules.Settings.on("changed", (evt) => { - if(matchedElements.has(evt.property)) - matchedElements.get(evt.property).update() - }) - Modules.Settings.initialize({ helper: Helper }) - } } diff --git a/assets/icons/common/text.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/Text.svg similarity index 100% rename from assets/icons/common/text.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/Text.svg diff --git a/assets/icons/common/angle.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/angle.svg similarity index 100% rename from assets/icons/common/angle.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/angle.svg diff --git a/assets/icons/common/appearance.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/appearance.svg similarity index 100% rename from assets/icons/common/appearance.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/appearance.svg diff --git a/assets/icons/common/arrow.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/arrow.svg similarity index 100% rename from assets/icons/common/arrow.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/arrow.svg diff --git a/assets/icons/common/delete.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/delete.svg similarity index 100% rename from assets/icons/common/delete.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/delete.svg diff --git a/assets/icons/common/label.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/label.svg similarity index 100% rename from assets/icons/common/label.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/label.svg diff --git a/assets/icons/common/position.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/position.svg similarity index 100% rename from assets/icons/common/position.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/position.svg diff --git a/assets/icons/common/target.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/target.svg similarity index 100% rename from assets/icons/common/target.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/target.svg diff --git a/assets/icons/history/appearance.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/appearance.svg similarity index 100% rename from assets/icons/history/appearance.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/appearance.svg diff --git a/assets/icons/history/create.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/create.svg similarity index 100% rename from assets/icons/history/create.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/create.svg diff --git a/assets/icons/common/close.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/delete.svg similarity index 100% rename from assets/icons/common/close.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/delete.svg diff --git a/assets/icons/history/modify.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/modify.svg similarity index 100% rename from assets/icons/history/modify.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/modify.svg diff --git a/assets/icons/history/name.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/name.svg similarity index 100% rename from assets/icons/history/name.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/name.svg diff --git a/assets/icons/history/position.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/position.svg similarity index 100% rename from assets/icons/history/position.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/position.svg diff --git a/assets/icons/history/visibility.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/visibility.svg similarity index 100% rename from assets/icons/history/visibility.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/history/visibility.svg diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/logarithmplotter.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/logarithmplotter.svg new file mode 120000 index 0000000..0497bab --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/logarithmplotter.svg @@ -0,0 +1 @@ +../../../../../logarithmplotter.svg \ No newline at end of file diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Function.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Function.svg new file mode 100644 index 0000000..2dbb7cf --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Function.svg @@ -0,0 +1,110 @@ + + + + + + + + + + image/svg+xml + + + 2021 + + + Ad5001 + + + + + (C) Ad5001 2021 - Licensed under CC4.0-BY-NC-SA + + + + + + + + + + + + + + + + + + + + + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Gain Bode.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Gain Bode.svg new file mode 100644 index 0000000..2d085e7 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Gain Bode.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + ω + + + + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Phase Bode.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Phase Bode.svg new file mode 100644 index 0000000..e9a0278 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Phase Bode.svg @@ -0,0 +1,88 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + φ + + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Point.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Point.svg new file mode 100644 index 0000000..8bee87b --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Point.svg @@ -0,0 +1 @@ +A diff --git a/assets/icons/objects/Repartition.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Repartition.svg similarity index 100% rename from assets/icons/objects/Repartition.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Repartition.svg diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Sequence.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Sequence.svg new file mode 100644 index 0000000..9bbbf82 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Sequence.svg @@ -0,0 +1,103 @@ + + + + + + + + + + image/svg+xml + + + + + + + u + n + + + + + + + + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Somme gains Bode.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Somme gains Bode.svg new file mode 100644 index 0000000..4a61e54 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Somme gains Bode.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Somme gains Bode2.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Somme gains Bode2.svg new file mode 100644 index 0000000..6965e85 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Somme gains Bode2.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + ΣG + + + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Somme phases Bode.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Somme phases Bode.svg new file mode 100644 index 0000000..24aa9d0 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Somme phases Bode.svg @@ -0,0 +1,92 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + Σφ + + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Text.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Text.svg new file mode 100644 index 0000000..0872104 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Text.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/X Cursor.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/X Cursor.svg new file mode 100644 index 0000000..a7b534d --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/X Cursor.svg @@ -0,0 +1,85 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/assets/icons/properties/definitionDomain.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Definition Domain.svg similarity index 100% rename from assets/icons/properties/definitionDomain.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Definition Domain.svg diff --git a/assets/icons/properties/destinationDomain.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Destination Domain.svg similarity index 100% rename from assets/icons/properties/destinationDomain.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Destination Domain.svg diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Display Mode.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Display Mode.svg new file mode 120000 index 0000000..7a5ba45 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Display Mode.svg @@ -0,0 +1 @@ +../../common/appearance.svg \ No newline at end of file diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Display Style.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Display Style.svg new file mode 120000 index 0000000..7a5ba45 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Display Style.svg @@ -0,0 +1 @@ +../../common/appearance.svg \ No newline at end of file diff --git a/assets/icons/properties/expression.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Expression.svg similarity index 100% rename from assets/icons/properties/expression.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Expression.svg diff --git a/assets/icons/properties/gain.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Gain.svg similarity index 100% rename from assets/icons/properties/gain.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Gain.svg diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Label Position.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Label Position.svg new file mode 120000 index 0000000..43aa15f --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Label Position.svg @@ -0,0 +1 @@ +../../common/arrow.svg \ No newline at end of file diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Label X.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Label X.svg new file mode 120000 index 0000000..6b40925 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Label X.svg @@ -0,0 +1 @@ +../../common/position.svg \ No newline at end of file diff --git a/assets/icons/properties/pass.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Pass.svg similarity index 100% rename from assets/icons/properties/pass.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Pass.svg diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Phase.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Phase.svg new file mode 120000 index 0000000..21699e9 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Phase.svg @@ -0,0 +1 @@ +../../common/angle.svg \ No newline at end of file diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Point Style.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Point Style.svg new file mode 120000 index 0000000..7a5ba45 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Point Style.svg @@ -0,0 +1 @@ +../../common/appearance.svg \ No newline at end of file diff --git a/assets/icons/properties/rounding.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Rounding.svg similarity index 100% rename from assets/icons/properties/rounding.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Rounding.svg diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Target Element.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Target Element.svg new file mode 120000 index 0000000..50124dc --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Target Element.svg @@ -0,0 +1 @@ +../../common/target.svg \ No newline at end of file diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Target Value Position.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Target Value Position.svg new file mode 120000 index 0000000..6b40925 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Target Value Position.svg @@ -0,0 +1 @@ +../../common/position.svg \ No newline at end of file diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Text.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Text.svg new file mode 120000 index 0000000..69be9e1 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Text.svg @@ -0,0 +1 @@ +../../common/label.svg \ No newline at end of file diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Unit.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Unit.svg new file mode 120000 index 0000000..21699e9 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Unit.svg @@ -0,0 +1 @@ +../../common/angle.svg \ No newline at end of file diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/X.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/X.svg new file mode 120000 index 0000000..6b40925 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/X.svg @@ -0,0 +1 @@ +../../common/position.svg \ No newline at end of file diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Y.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Y.svg new file mode 120000 index 0000000..6b40925 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/Y.svg @@ -0,0 +1 @@ +../../common/position.svg \ No newline at end of file diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/ω₀.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/ω₀.svg new file mode 120000 index 0000000..21699e9 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/ω₀.svg @@ -0,0 +1 @@ +../../common/angle.svg \ No newline at end of file diff --git a/assets/icons/settings/linewidth.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/linewidth.svg similarity index 100% rename from assets/icons/settings/linewidth.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/linewidth.svg diff --git a/assets/icons/settings/textsize.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/textsize.svg similarity index 100% rename from assets/icons/settings/textsize.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/textsize.svg diff --git a/assets/icons/settings/timeline.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/timeline.svg similarity index 100% rename from assets/icons/settings/timeline.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/timeline.svg diff --git a/assets/icons/settings/update.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/update.svg similarity index 100% rename from assets/icons/settings/update.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/update.svg diff --git a/assets/icons/settings/xaxisstep.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/xaxisstep.svg similarity index 100% rename from assets/icons/settings/xaxisstep.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/xaxisstep.svg diff --git a/assets/icons/settings/xlabel.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/xlabel.svg similarity index 100% rename from assets/icons/settings/xlabel.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/xlabel.svg diff --git a/assets/icons/settings/xmax.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/xmax.svg similarity index 100% rename from assets/icons/settings/xmax.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/xmax.svg diff --git a/assets/icons/settings/xmin.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/xmin.svg similarity index 100% rename from assets/icons/settings/xmin.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/xmin.svg diff --git a/assets/icons/settings/xzoom.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/xzoom.svg similarity index 100% rename from assets/icons/settings/xzoom.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/xzoom.svg diff --git a/assets/icons/settings/yaxisstep.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/yaxisstep.svg similarity index 100% rename from assets/icons/settings/yaxisstep.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/yaxisstep.svg diff --git a/assets/icons/settings/ylabel.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/ylabel.svg similarity index 100% rename from assets/icons/settings/ylabel.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/ylabel.svg diff --git a/assets/icons/settings/ymax.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/ymax.svg similarity index 100% rename from assets/icons/settings/ymax.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/ymax.svg diff --git a/assets/icons/settings/ymin.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/ymin.svg similarity index 100% rename from assets/icons/settings/ymin.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/ymin.svg diff --git a/assets/icons/settings/yzoom.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/yzoom.svg similarity index 100% rename from assets/icons/settings/yzoom.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/yzoom.svg diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js new file mode 100644 index 0000000..9192db5 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js @@ -0,0 +1,1839 @@ +// https://silentmatt.com/javascript-expression-evaluator/ + +.pragma library + +var INUMBER = 'INUMBER'; +var IOP1 = 'IOP1'; +var IOP2 = 'IOP2'; +var IOP3 = 'IOP3'; +var IVAR = 'IVAR'; +var IVARNAME = 'IVARNAME'; +var IFUNCALL = 'IFUNCALL'; +var IFUNDEF = 'IFUNDEF'; +var IEXPR = 'IEXPR'; +var IEXPREVAL = 'IEXPREVAL'; +var IMEMBER = 'IMEMBER'; +var IENDSTATEMENT = 'IENDSTATEMENT'; +var IARRAY = 'IARRAY'; + +function Instruction(type, value) { + this.type = type; + this.value = (value !== undefined && value !== null) ? value : 0; +} + +Instruction.prototype.toString = function () { + switch (this.type) { + case INUMBER: + case IOP1: + case IOP2: + case IOP3: + case IVAR: + case IVARNAME: + case IENDSTATEMENT: + return this.value; + case IFUNCALL: + return 'CALL ' + this.value; + case IFUNDEF: + return 'DEF ' + this.value; + case IARRAY: + return 'ARRAY ' + this.value; + case IMEMBER: + return '.' + this.value; + default: + return 'Invalid Instruction'; + } +}; + +function unaryInstruction(value) { + return new Instruction(IOP1, value); +} + +function binaryInstruction(value) { + return new Instruction(IOP2, value); +} + +function ternaryInstruction(value) { + return new Instruction(IOP3, value); +} + +function simplify(tokens, unaryOps, binaryOps, ternaryOps, values) { + var nstack = []; + var newexpression = []; + var n1, n2, n3; + var f; + for (var i = 0; i < tokens.length; i++) { + var item = tokens[i]; + var type = item.type; + if (type === INUMBER || type === IVARNAME) { + if (Array.isArray(item.value)) { + nstack.push.apply(nstack, simplify(item.value.map(function (x) { + return new Instruction(INUMBER, x); + }).concat(new Instruction(IARRAY, item.value.length)), unaryOps, binaryOps, ternaryOps, values)); + } else { + nstack.push(item); + } + } else if (type === IVAR && values.hasOwnProperty(item.value)) { + item = new Instruction(INUMBER, values[item.value]); + nstack.push(item); + } else if (type === IOP2 && nstack.length > 1) { + n2 = nstack.pop(); + n1 = nstack.pop(); + f = binaryOps[item.value]; + item = new Instruction(INUMBER, f(n1.value, n2.value)); + nstack.push(item); + } else if (type === IOP3 && nstack.length > 2) { + n3 = nstack.pop(); + n2 = nstack.pop(); + n1 = nstack.pop(); + if (item.value === '?') { + nstack.push(n1.value ? n2.value : n3.value); + } else { + f = ternaryOps[item.value]; + item = new Instruction(INUMBER, f(n1.value, n2.value, n3.value)); + nstack.push(item); + } + } else if (type === IOP1 && nstack.length > 0) { + n1 = nstack.pop(); + f = unaryOps[item.value]; + item = new Instruction(INUMBER, f(n1.value)); + nstack.push(item); + } else if (type === IEXPR) { + while (nstack.length > 0) { + newexpression.push(nstack.shift()); + } + newexpression.push(new Instruction(IEXPR, simplify(item.value, unaryOps, binaryOps, ternaryOps, values))); + } else if (type === IMEMBER && nstack.length > 0) { + n1 = nstack.pop(); + nstack.push(new Instruction(INUMBER, n1.value[item.value])); + } /* else if (type === IARRAY && nstack.length >= item.value) { + var length = item.value; + while (length-- > 0) { + newexpression.push(nstack.pop()); + } + newexpression.push(new Instruction(IARRAY, item.value)); + } */ else { + while (nstack.length > 0) { + newexpression.push(nstack.shift()); + } + newexpression.push(item); + } + } + while (nstack.length > 0) { + newexpression.push(nstack.shift()); + } + return newexpression; +} + +function substitute(tokens, variable, expr) { + var newexpression = []; + for (var i = 0; i < tokens.length; i++) { + var item = tokens[i]; + var type = item.type; + if (type === IVAR && item.value === variable) { + for (var j = 0; j < expr.tokens.length; j++) { + var expritem = expr.tokens[j]; + var replitem; + if (expritem.type === IOP1) { + replitem = unaryInstruction(expritem.value); + } else if (expritem.type === IOP2) { + replitem = binaryInstruction(expritem.value); + } else if (expritem.type === IOP3) { + replitem = ternaryInstruction(expritem.value); + } else { + replitem = new Instruction(expritem.type, expritem.value); + } + newexpression.push(replitem); + } + } else if (type === IEXPR) { + newexpression.push(new Instruction(IEXPR, substitute(item.value, variable, expr))); + } else { + newexpression.push(item); + } + } + return newexpression; +} + +function evaluate(tokens, expr, values) { + var nstack = []; + var n1, n2, n3; + var f, args, argCount; + + if (isExpressionEvaluator(tokens)) { + return resolveExpression(tokens, values); + } + + var numTokens = tokens.length; + + for (var i = 0; i < numTokens; i++) { + var item = tokens[i]; + var type = item.type; + if (type === INUMBER || type === IVARNAME) { + nstack.push(item.value); + } else if (type === IOP2) { + n2 = nstack.pop(); + n1 = nstack.pop(); + if (item.value === 'and') { + nstack.push(n1 ? !!evaluate(n2, expr, values) : false); + } else if (item.value === 'or') { + nstack.push(n1 ? true : !!evaluate(n2, expr, values)); + } else if (item.value === '=') { + f = expr.binaryOps[item.value]; + nstack.push(f(n1, evaluate(n2, expr, values), values)); + } else { + f = expr.binaryOps[item.value]; + nstack.push(f(resolveExpression(n1, values), resolveExpression(n2, values))); + } + } else if (type === IOP3) { + n3 = nstack.pop(); + n2 = nstack.pop(); + n1 = nstack.pop(); + if (item.value === '?') { + nstack.push(evaluate(n1 ? n2 : n3, expr, values)); + } else { + f = expr.ternaryOps[item.value]; + nstack.push(f(resolveExpression(n1, values), resolveExpression(n2, values), resolveExpression(n3, values))); + } + } else if (type === IVAR) { + if (item.value in expr.functions) { + nstack.push(expr.functions[item.value]); + } else if (item.value in expr.unaryOps && expr.parser.isOperatorEnabled(item.value)) { + nstack.push(expr.unaryOps[item.value]); + } else { + var v = values[item.value]; + if (v !== undefined) { + nstack.push(v); + } else { + throw new Error('undefined variable: ' + item.value); + } + } + } else if (type === IOP1) { + n1 = nstack.pop(); + f = expr.unaryOps[item.value]; + nstack.push(f(resolveExpression(n1, values))); + } else if (type === IFUNCALL) { + argCount = item.value; + args = []; + while (argCount-- > 0) { + args.unshift(resolveExpression(nstack.pop(), values)); + } + f = nstack.pop(); + if (f.apply && f.call) { + nstack.push(f.apply(undefined, args)); + } else { + throw new Error(f + ' is not a function'); + } + } else if (type === IFUNDEF) { + // Create closure to keep references to arguments and expression + nstack.push((function () { + var n2 = nstack.pop(); + var args = []; + var argCount = item.value; + while (argCount-- > 0) { + args.unshift(nstack.pop()); + } + var n1 = nstack.pop(); + var f = function () { + var scope = Object.assign({}, values); + for (var i = 0, len = args.length; i < len; i++) { + scope[args[i]] = arguments[i]; + } + return evaluate(n2, expr, scope); + }; + // f.name = n1 + Object.defineProperty(f, 'name', { + value: n1, + writable: false + }); + values[n1] = f; + return f; + })()); + } else if (type === IEXPR) { + nstack.push(createExpressionEvaluator(item, expr)); + } else if (type === IEXPREVAL) { + nstack.push(item); + } else if (type === IMEMBER) { + n1 = nstack.pop(); + nstack.push(n1[item.value]); + } else if (type === IENDSTATEMENT) { + nstack.pop(); + } else if (type === IARRAY) { + argCount = item.value; + args = []; + while (argCount-- > 0) { + args.unshift(nstack.pop()); + } + nstack.push(args); + } else { + throw new Error('invalid Expression'); + } + } + if (nstack.length > 1) { + throw new Error('invalid Expression (parity)'); + } + // Explicitly return zero to avoid test issues caused by -0 + return nstack[0] === 0 ? 0 : resolveExpression(nstack[0], values); +} + +function createExpressionEvaluator(token, expr, values) { + if (isExpressionEvaluator(token)) return token; + return { + type: IEXPREVAL, + value: function (scope) { + return evaluate(token.value, expr, scope); + } + }; +} + +function isExpressionEvaluator(n) { + return n && n.type === IEXPREVAL; +} + +function resolveExpression(n, values) { + return isExpressionEvaluator(n) ? n.value(values) : n; +} + +function expressionToString(tokens, toJS) { + 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; + if (type === INUMBER) { + if (typeof item.value === 'number' && item.value < 0) { + nstack.push('(' + item.value + ')'); + } else if (Array.isArray(item.value)) { + nstack.push('[' + item.value.map(escapeValue).join(', ') + ']'); + } else { + nstack.push(escapeValue(item.value)); + } + } else if (type === IOP2) { + n2 = nstack.pop(); + n1 = nstack.pop(); + f = item.value; + if (toJS) { + if (f === '^') { + nstack.push('Math.pow(' + n1 + ', ' + n2 + ')'); + } else if (f === 'and') { + nstack.push('(!!' + n1 + ' && !!' + n2 + ')'); + } else if (f === 'or') { + nstack.push('(!!' + n1 + ' || !!' + n2 + ')'); + } else if (f === '||') { + nstack.push('(function(a,b){ return Array.isArray(a) && Array.isArray(b) ? a.concat(b) : String(a) + String(b); }((' + n1 + '),(' + n2 + ')))'); + } else if (f === '==') { + nstack.push('(' + n1 + ' === ' + n2 + ')'); + } else if (f === '!=') { + nstack.push('(' + n1 + ' !== ' + n2 + ')'); + } else if (f === '[') { + nstack.push(n1 + '[(' + n2 + ') | 0]'); + } else { + nstack.push('(' + n1 + ' ' + f + ' ' + n2 + ')'); + } + } else { + if (f === '[') { + nstack.push(n1 + '[' + n2 + ']'); + } else { + nstack.push('(' + n1 + ' ' + f + ' ' + n2 + ')'); + } + } + } else if (type === IOP3) { + n3 = nstack.pop(); + n2 = nstack.pop(); + n1 = nstack.pop(); + f = item.value; + if (f === '?') { + nstack.push('(' + n1 + ' ? ' + n2 + ' : ' + n3 + ')'); + } else { + throw new Error('invalid Expression'); + } + } else if (type === IVAR || type === IVARNAME) { + nstack.push(item.value); + } else if (type === IOP1) { + n1 = nstack.pop(); + f = item.value; + if (f === '-' || f === '+') { + nstack.push('(' + f + n1 + ')'); + } else if (toJS) { + if (f === 'not') { + nstack.push('(' + '!' + n1 + ')'); + } else if (f === '!') { + nstack.push('fac(' + n1 + ')'); + } else { + nstack.push(f + '(' + n1 + ')'); + } + } else if (f === '!') { + nstack.push('(' + n1 + '!)'); + } else { + nstack.push('(' + f + ' ' + n1 + ')'); + } + } else if (type === IFUNCALL) { + argCount = item.value; + args = []; + while (argCount-- > 0) { + args.unshift(nstack.pop()); + } + f = nstack.pop(); + nstack.push(f + '(' + args.join(', ') + ')'); + } else if (type === IFUNDEF) { + n2 = nstack.pop(); + argCount = item.value; + args = []; + while (argCount-- > 0) { + args.unshift(nstack.pop()); + } + n1 = nstack.pop(); + if (toJS) { + nstack.push('(' + n1 + ' = function(' + args.join(', ') + ') { return ' + n2 + ' })'); + } else { + nstack.push('(' + n1 + '(' + args.join(', ') + ') = ' + n2 + ')'); + } + } else if (type === IMEMBER) { + n1 = nstack.pop(); + nstack.push(n1 + '.' + item.value); + } else if (type === IARRAY) { + argCount = item.value; + args = []; + while (argCount-- > 0) { + args.unshift(nstack.pop()); + } + nstack.push('[' + args.join(', ') + ']'); + } else if (type === IEXPR) { + nstack.push('(' + expressionToString(item.value, toJS) + ')'); + } else if (type === IENDSTATEMENT) ; else { + throw new Error('invalid Expression'); + } + } + if (nstack.length > 1) { + if (toJS) { + nstack = [ nstack.join(',') ]; + } else { + nstack = [ nstack.join(';') ]; + } + } + return String(nstack[0]); +} + +function escapeValue(v) { + if (typeof v === 'string') { + return JSON.stringify(v).replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029'); + } + return v; +} + +function contains(array, obj) { + for (var i = 0; i < array.length; i++) { + if (array[i] === obj) { + return true; + } + } + return false; +} + +function getSymbols(tokens, symbols, options) { + options = options || {}; + var withMembers = !!options.withMembers; + var prevVar = null; + + for (var i = 0; i < tokens.length; i++) { + var item = tokens[i]; + if (item.type === IVAR || item.type === IVARNAME) { + if (!withMembers && !contains(symbols, item.value)) { + symbols.push(item.value); + } else if (prevVar !== null) { + if (!contains(symbols, prevVar)) { + symbols.push(prevVar); + } + prevVar = item.value; + } else { + prevVar = item.value; + } + } else if (item.type === IMEMBER && withMembers && prevVar !== null) { + prevVar += '.' + item.value; + } else if (item.type === IEXPR) { + getSymbols(item.value, symbols, options); + } else if (prevVar !== null) { + if (!contains(symbols, prevVar)) { + symbols.push(prevVar); + } + prevVar = null; + } + } + + if (prevVar !== null && !contains(symbols, prevVar)) { + symbols.push(prevVar); + } +} + +function Expression(tokens, parser) { + this.tokens = tokens; + this.parser = parser; + this.unaryOps = parser.unaryOps; + this.binaryOps = parser.binaryOps; + this.ternaryOps = parser.ternaryOps; + this.functions = parser.functions; +} + +Expression.prototype.simplify = function (values) { + values = values || {}; + return new Expression(simplify(this.tokens, this.unaryOps, this.binaryOps, this.ternaryOps, values), this.parser); +}; + +Expression.prototype.substitute = function (variable, expr) { + if (!(expr instanceof Expression)) { + expr = this.parser.parse(String(expr)); + } + + return new Expression(substitute(this.tokens, variable, expr), this.parser); +}; + +Expression.prototype.evaluate = function (values) { + values = values || {}; + return evaluate(this.tokens, this, values); +}; + +Expression.prototype.toString = function () { + return expressionToString(this.tokens, false); +}; + +Expression.prototype.symbols = function (options) { + options = options || {}; + var vars = []; + getSymbols(this.tokens, vars, options); + return vars; +}; + +Expression.prototype.variables = function (options) { + options = options || {}; + var vars = []; + getSymbols(this.tokens, vars, options); + var functions = this.functions; + return vars.filter(function (name) { + return !(name in functions); + }); +}; + +Expression.prototype.toJSFunction = function (param, variables) { + var expr = this; + var f = new Function(param, 'with(this.functions) with (this.ternaryOps) with (this.binaryOps) with (this.unaryOps) { return ' + expressionToString(this.simplify(variables).tokens, true) + '; }'); // eslint-disable-line no-new-func + return function () { + return f.apply(expr, arguments); + }; +}; + +var TEOF = 'TEOF'; +var TOP = 'TOP'; +var TNUMBER = 'TNUMBER'; +var TSTRING = 'TSTRING'; +var TPAREN = 'TPAREN'; +var TBRACKET = 'TBRACKET'; +var TCOMMA = 'TCOMMA'; +var TNAME = 'TNAME'; +var TSEMICOLON = 'TSEMICOLON'; + +function Token(type, value, index) { + this.type = type; + this.value = value; + this.index = index; +} + +Token.prototype.toString = function () { + return this.type + ': ' + this.value; +}; + +function TokenStream(parser, expression) { + this.pos = 0; + this.current = null; + this.unaryOps = parser.unaryOps; + this.binaryOps = parser.binaryOps; + this.ternaryOps = parser.ternaryOps; + this.consts = parser.consts; + this.expression = expression; + this.savedPosition = 0; + this.savedCurrent = null; + this.options = parser.options; + this.parser = parser; +} + +TokenStream.prototype.newToken = function (type, value, pos) { + return new Token(type, value, pos != null ? pos : this.pos); +}; + +TokenStream.prototype.save = function () { + this.savedPosition = this.pos; + this.savedCurrent = this.current; +}; + +TokenStream.prototype.restore = function () { + this.pos = this.savedPosition; + this.current = this.savedCurrent; +}; + +TokenStream.prototype.next = function () { + if (this.pos >= this.expression.length) { + return this.newToken(TEOF, 'EOF'); + } + + if (this.isWhitespace() || this.isComment()) { + return this.next(); + } else if (this.isRadixInteger() || + this.isNumber() || + this.isOperator() || + this.isString() || + this.isParen() || + this.isBracket() || + this.isComma() || + this.isSemicolon() || + this.isNamedOp() || + this.isConst() || + this.isName()) { + return this.current; + } else { + this.parseError('Unknown character "' + this.expression.charAt(this.pos) + '"'); + } +}; + +TokenStream.prototype.isString = function () { + var r = false; + var startPos = this.pos; + var quote = this.expression.charAt(startPos); + + if (quote === '\'' || quote === '"') { + var index = this.expression.indexOf(quote, startPos + 1); + while (index >= 0 && this.pos < this.expression.length) { + this.pos = index + 1; + if (this.expression.charAt(index - 1) !== '\\') { + var rawString = this.expression.substring(startPos + 1, index); + this.current = this.newToken(TSTRING, this.unescape(rawString), startPos); + r = true; + break; + } + index = this.expression.indexOf(quote, index + 1); + } + } + return r; +}; + +TokenStream.prototype.isParen = function () { + var c = this.expression.charAt(this.pos); + if (c === '(' || c === ')') { + this.current = this.newToken(TPAREN, c); + this.pos++; + return true; + } + return false; +}; + +TokenStream.prototype.isBracket = function () { + var c = this.expression.charAt(this.pos); + if ((c === '[' || c === ']') && this.isOperatorEnabled('[')) { + this.current = this.newToken(TBRACKET, c); + this.pos++; + return true; + } + return false; +}; + +TokenStream.prototype.isComma = function () { + var c = this.expression.charAt(this.pos); + if (c === ',') { + this.current = this.newToken(TCOMMA, ','); + this.pos++; + return true; + } + return false; +}; + +TokenStream.prototype.isSemicolon = function () { + var c = this.expression.charAt(this.pos); + if (c === ';') { + this.current = this.newToken(TSEMICOLON, ';'); + this.pos++; + return true; + } + return false; +}; + +TokenStream.prototype.isConst = function () { + var startPos = this.pos; + var i = startPos; + for (; i < this.expression.length; i++) { + var c = this.expression.charAt(i); + if (c.toUpperCase() === c.toLowerCase()) { + if (i === this.pos || (c !== '_' && c !== '.' && (c < '0' || c > '9'))) { + break; + } + } + } + if (i > startPos) { + var str = this.expression.substring(startPos, i); + if (str in this.consts) { + this.current = this.newToken(TNUMBER, this.consts[str]); + this.pos += str.length; + return true; + } + } + return false; +}; + +TokenStream.prototype.isNamedOp = function () { + var startPos = this.pos; + var i = startPos; + for (; i < this.expression.length; i++) { + var c = this.expression.charAt(i); + if (c.toUpperCase() === c.toLowerCase()) { + if (i === this.pos || (c !== '_' && (c < '0' || c > '9'))) { + break; + } + } + } + if (i > startPos) { + var str = this.expression.substring(startPos, i); + if (this.isOperatorEnabled(str) && (str in this.binaryOps || str in this.unaryOps || str in this.ternaryOps)) { + this.current = this.newToken(TOP, str); + this.pos += str.length; + return true; + } + } + return false; +}; + +TokenStream.prototype.isName = function () { + var startPos = this.pos; + var i = startPos; + var hasLetter = false; + for (; i < this.expression.length; i++) { + var c = this.expression.charAt(i); + if (c.toUpperCase() === c.toLowerCase()) { + if (i === this.pos && (c === '$' || c === '_')) { + if (c === '_') { + hasLetter = true; + } + continue; + } else if (i === this.pos || !hasLetter || (c !== '_' && (c < '0' || c > '9'))) { + break; + } + } else { + hasLetter = true; + } + } + if (hasLetter) { + var str = this.expression.substring(startPos, i); + this.current = this.newToken(TNAME, str); + this.pos += str.length; + return true; + } + return false; +}; + +TokenStream.prototype.isWhitespace = function () { + var r = false; + var c = this.expression.charAt(this.pos); + while (c === ' ' || c === '\t' || c === '\n' || c === '\r') { + r = true; + this.pos++; + if (this.pos >= this.expression.length) { + break; + } + c = this.expression.charAt(this.pos); + } + return r; +}; + +var codePointPattern = /^[0-9a-f]{4}$/i; + +TokenStream.prototype.unescape = function (v) { + var index = v.indexOf('\\'); + if (index < 0) { + return v; + } + + var buffer = v.substring(0, index); + while (index >= 0) { + var c = v.charAt(++index); + switch (c) { + case '\'': + buffer += '\''; + break; + case '"': + buffer += '"'; + break; + case '\\': + buffer += '\\'; + break; + case '/': + buffer += '/'; + break; + case 'b': + buffer += '\b'; + break; + case 'f': + buffer += '\f'; + break; + case 'n': + buffer += '\n'; + break; + case 'r': + buffer += '\r'; + break; + case 't': + buffer += '\t'; + break; + case 'u': + // interpret the following 4 characters as the hex of the unicode code point + var codePoint = v.substring(index + 1, index + 5); + if (!codePointPattern.test(codePoint)) { + this.parseError('Illegal escape sequence: \\u' + codePoint); + } + buffer += String.fromCharCode(parseInt(codePoint, 16)); + index += 4; + break; + default: + throw this.parseError('Illegal escape sequence: "\\' + c + '"'); + } + ++index; + var backslash = v.indexOf('\\', index); + buffer += v.substring(index, backslash < 0 ? v.length : backslash); + index = backslash; + } + + return buffer; +}; + +TokenStream.prototype.isComment = function () { + var c = this.expression.charAt(this.pos); + if (c === '/' && this.expression.charAt(this.pos + 1) === '*') { + this.pos = this.expression.indexOf('*/', this.pos) + 2; + if (this.pos === 1) { + this.pos = this.expression.length; + } + return true; + } + return false; +}; + +TokenStream.prototype.isRadixInteger = function () { + var pos = this.pos; + + if (pos >= this.expression.length - 2 || this.expression.charAt(pos) !== '0') { + return false; + } + ++pos; + + var radix; + var validDigit; + if (this.expression.charAt(pos) === 'x') { + radix = 16; + validDigit = /^[0-9a-f]$/i; + ++pos; + } else if (this.expression.charAt(pos) === 'b') { + radix = 2; + validDigit = /^[01]$/i; + ++pos; + } else { + return false; + } + + var valid = false; + var startPos = pos; + + while (pos < this.expression.length) { + var c = this.expression.charAt(pos); + if (validDigit.test(c)) { + pos++; + valid = true; + } else { + break; + } + } + + if (valid) { + this.current = this.newToken(TNUMBER, parseInt(this.expression.substring(startPos, pos), radix)); + this.pos = pos; + } + return valid; +}; + +TokenStream.prototype.isNumber = function () { + var valid = false; + var pos = this.pos; + var startPos = pos; + var resetPos = pos; + var foundDot = false; + var foundDigits = false; + var c; + + while (pos < this.expression.length) { + c = this.expression.charAt(pos); + if ((c >= '0' && c <= '9') || (!foundDot && c === '.')) { + if (c === '.') { + foundDot = true; + } else { + foundDigits = true; + } + pos++; + valid = foundDigits; + } else { + break; + } + } + + if (valid) { + resetPos = pos; + } + + if (c === 'e' || c === 'E') { + pos++; + var acceptSign = true; + var validExponent = false; + while (pos < this.expression.length) { + c = this.expression.charAt(pos); + if (acceptSign && (c === '+' || c === '-')) { + acceptSign = false; + } else if (c >= '0' && c <= '9') { + validExponent = true; + acceptSign = false; + } else { + break; + } + pos++; + } + + if (!validExponent) { + pos = resetPos; + } + } + + if (valid) { + this.current = this.newToken(TNUMBER, parseFloat(this.expression.substring(startPos, pos))); + this.pos = pos; + } else { + this.pos = resetPos; + } + return valid; +}; + +TokenStream.prototype.isOperator = function () { + var startPos = this.pos; + var c = this.expression.charAt(this.pos); + + if (c === '+' || c === '-' || c === '*' || c === '/' || c === '%' || c === '^' || c === '?' || c === ':' || c === '.') { + this.current = this.newToken(TOP, c); + } else if (c === '∙' || c === '•') { + this.current = this.newToken(TOP, '*'); + } else if (c === '>') { + if (this.expression.charAt(this.pos + 1) === '=') { + this.current = this.newToken(TOP, '>='); + this.pos++; + } else { + this.current = this.newToken(TOP, '>'); + } + } else if (c === '<') { + if (this.expression.charAt(this.pos + 1) === '=') { + this.current = this.newToken(TOP, '<='); + this.pos++; + } else { + this.current = this.newToken(TOP, '<'); + } + } else if (c === '|') { + if (this.expression.charAt(this.pos + 1) === '|') { + this.current = this.newToken(TOP, '||'); + this.pos++; + } else { + return false; + } + } else if (c === '=') { + if (this.expression.charAt(this.pos + 1) === '=') { + this.current = this.newToken(TOP, '=='); + this.pos++; + } else { + this.current = this.newToken(TOP, c); + } + } else if (c === '!') { + if (this.expression.charAt(this.pos + 1) === '=') { + this.current = this.newToken(TOP, '!='); + this.pos++; + } else { + this.current = this.newToken(TOP, c); + } + } else { + return false; + } + this.pos++; + + if (this.isOperatorEnabled(this.current.value)) { + return true; + } else { + this.pos = startPos; + return false; + } +}; + +TokenStream.prototype.isOperatorEnabled = function (op) { + return this.parser.isOperatorEnabled(op); +}; + +TokenStream.prototype.getCoordinates = function () { + var line = 0; + var column; + var newline = -1; + do { + line++; + column = this.pos - newline; + newline = this.expression.indexOf('\n', newline + 1); + } while (newline >= 0 && newline < this.pos); + + return { + line: line, + column: column + }; +}; + +TokenStream.prototype.parseError = function (msg) { + var coords = this.getCoordinates(); + throw new Error('parse error [' + coords.line + ':' + coords.column + ']: ' + msg); +}; + +function ParserState(parser, tokenStream, options) { + this.parser = parser; + this.tokens = tokenStream; + this.current = null; + this.nextToken = null; + this.next(); + this.savedCurrent = null; + this.savedNextToken = null; + this.allowMemberAccess = options.allowMemberAccess !== false; +} + +ParserState.prototype.next = function () { + this.current = this.nextToken; + return (this.nextToken = this.tokens.next()); +}; + +ParserState.prototype.tokenMatches = function (token, value) { + if (typeof value === 'undefined') { + return true; + } else if (Array.isArray(value)) { + return contains(value, token.value); + } else if (typeof value === 'function') { + return value(token); + } else { + return token.value === value; + } +}; + +ParserState.prototype.save = function () { + this.savedCurrent = this.current; + this.savedNextToken = this.nextToken; + this.tokens.save(); +}; + +ParserState.prototype.restore = function () { + this.tokens.restore(); + this.current = this.savedCurrent; + this.nextToken = this.savedNextToken; +}; + +ParserState.prototype.accept = function (type, value) { + if (this.nextToken.type === type && this.tokenMatches(this.nextToken, value)) { + this.next(); + return true; + } + return false; +}; + +ParserState.prototype.expect = function (type, value) { + if (!this.accept(type, value)) { + var coords = this.tokens.getCoordinates(); + throw new Error('parse error [' + coords.line + ':' + coords.column + ']: Expected ' + (value || type)); + } +}; + +ParserState.prototype.parseAtom = function (instr) { + var unaryOps = this.tokens.unaryOps; + function isPrefixOperator(token) { + return token.value in unaryOps; + } + + if (this.accept(TNAME) || this.accept(TOP, isPrefixOperator)) { + instr.push(new Instruction(IVAR, this.current.value)); + } else if (this.accept(TNUMBER)) { + instr.push(new Instruction(INUMBER, this.current.value)); + } else if (this.accept(TSTRING)) { + instr.push(new Instruction(INUMBER, this.current.value)); + } else if (this.accept(TPAREN, '(')) { + this.parseExpression(instr); + this.expect(TPAREN, ')'); + } else if (this.accept(TBRACKET, '[')) { + if (this.accept(TBRACKET, ']')) { + instr.push(new Instruction(IARRAY, 0)); + } else { + var argCount = this.parseArrayList(instr); + instr.push(new Instruction(IARRAY, argCount)); + } + } else { + throw new Error('unexpected ' + this.nextToken); + } +}; + +ParserState.prototype.parseExpression = function (instr) { + var exprInstr = []; + if (this.parseUntilEndStatement(instr, exprInstr)) { + return; + } + this.parseVariableAssignmentExpression(exprInstr); + if (this.parseUntilEndStatement(instr, exprInstr)) { + return; + } + this.pushExpression(instr, exprInstr); +}; + +ParserState.prototype.pushExpression = function (instr, exprInstr) { + for (var i = 0, len = exprInstr.length; i < len; i++) { + instr.push(exprInstr[i]); + } +}; + +ParserState.prototype.parseUntilEndStatement = function (instr, exprInstr) { + if (!this.accept(TSEMICOLON)) return false; + if (this.nextToken && this.nextToken.type !== TEOF && !(this.nextToken.type === TPAREN && this.nextToken.value === ')')) { + exprInstr.push(new Instruction(IENDSTATEMENT)); + } + if (this.nextToken.type !== TEOF) { + this.parseExpression(exprInstr); + } + instr.push(new Instruction(IEXPR, exprInstr)); + return true; +}; + +ParserState.prototype.parseArrayList = function (instr) { + var argCount = 0; + + while (!this.accept(TBRACKET, ']')) { + this.parseExpression(instr); + ++argCount; + while (this.accept(TCOMMA)) { + this.parseExpression(instr); + ++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('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('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) { + this.parseOrExpression(instr); + while (this.accept(TOP, '?')) { + var trueBranch = []; + var falseBranch = []; + this.parseConditionalExpression(trueBranch); + this.expect(TOP, ':'); + this.parseConditionalExpression(falseBranch); + instr.push(new Instruction(IEXPR, trueBranch)); + instr.push(new Instruction(IEXPR, falseBranch)); + instr.push(ternaryInstruction('?')); + } +}; + +ParserState.prototype.parseOrExpression = function (instr) { + this.parseAndExpression(instr); + while (this.accept(TOP, 'or')) { + var falseBranch = []; + this.parseAndExpression(falseBranch); + instr.push(new Instruction(IEXPR, falseBranch)); + instr.push(binaryInstruction('or')); + } +}; + +ParserState.prototype.parseAndExpression = function (instr) { + this.parseComparison(instr); + while (this.accept(TOP, 'and')) { + var trueBranch = []; + this.parseComparison(trueBranch); + instr.push(new Instruction(IEXPR, trueBranch)); + instr.push(binaryInstruction('and')); + } +}; + +var COMPARISON_OPERATORS = ['==', '!=', '<', '<=', '>=', '>', 'in']; + +ParserState.prototype.parseComparison = function (instr) { + this.parseAddSub(instr); + while (this.accept(TOP, COMPARISON_OPERATORS)) { + var op = this.current; + this.parseAddSub(instr); + instr.push(binaryInstruction(op.value)); + } +}; + +var ADD_SUB_OPERATORS = ['+', '-', '||']; + +ParserState.prototype.parseAddSub = function (instr) { + this.parseTerm(instr); + while (this.accept(TOP, ADD_SUB_OPERATORS)) { + var op = this.current; + this.parseTerm(instr); + instr.push(binaryInstruction(op.value)); + } +}; + +var TERM_OPERATORS = ['*', '/', '%']; + +ParserState.prototype.parseTerm = function (instr) { + this.parseFactor(instr); + while (this.accept(TOP, TERM_OPERATORS)) { + var op = this.current; + this.parseFactor(instr); + instr.push(binaryInstruction(op.value)); + } +}; + +ParserState.prototype.parseFactor = function (instr) { + var unaryOps = this.tokens.unaryOps; + function isPrefixOperator(token) { + return token.value in unaryOps; + } + + this.save(); + if (this.accept(TOP, isPrefixOperator)) { + if (this.current.value !== '-' && this.current.value !== '+') { + if (this.nextToken.type === TPAREN && this.nextToken.value === '(') { + this.restore(); + this.parseExponential(instr); + return; + } else if (this.nextToken.type === TSEMICOLON || this.nextToken.type === TCOMMA || this.nextToken.type === TEOF || (this.nextToken.type === TPAREN && this.nextToken.value === ')')) { + this.restore(); + this.parseAtom(instr); + return; + } + } + + var op = this.current; + this.parseFactor(instr); + instr.push(unaryInstruction(op.value)); + } else { + this.parseExponential(instr); + } +}; + +ParserState.prototype.parseExponential = function (instr) { + this.parsePostfixExpression(instr); + while (this.accept(TOP, '^')) { + this.parseFactor(instr); + instr.push(binaryInstruction('^')); + } +}; + +ParserState.prototype.parsePostfixExpression = function (instr) { + this.parseFunctionCall(instr); + while (this.accept(TOP, '!')) { + instr.push(unaryInstruction('!')); + } +}; + +ParserState.prototype.parseFunctionCall = function (instr) { + var unaryOps = this.tokens.unaryOps; + function isPrefixOperator(token) { + return token.value in unaryOps; + } + + if (this.accept(TOP, isPrefixOperator)) { + var op = this.current; + this.parseAtom(instr); + instr.push(unaryInstruction(op.value)); + } else { + this.parseMemberExpression(instr); + while (this.accept(TPAREN, '(')) { + if (this.accept(TPAREN, ')')) { + instr.push(new Instruction(IFUNCALL, 0)); + } else { + var argCount = this.parseArgumentList(instr); + instr.push(new Instruction(IFUNCALL, argCount)); + } + } + } +}; + +ParserState.prototype.parseArgumentList = function (instr) { + var argCount = 0; + + while (!this.accept(TPAREN, ')')) { + this.parseExpression(instr); + ++argCount; + while (this.accept(TCOMMA)) { + this.parseExpression(instr); + ++argCount; + } + } + + return argCount; +}; + +ParserState.prototype.parseMemberExpression = function (instr) { + this.parseAtom(instr); + while (this.accept(TOP, '.') || this.accept(TBRACKET, '[')) { + var op = this.current; + + if (op.value === '.') { + if (!this.allowMemberAccess) { + throw new Error('unexpected ".", member access is not permitted'); + } + + this.expect(TNAME); + instr.push(new Instruction(IMEMBER, this.current.value)); + } else if (op.value === '[') { + if (!this.tokens.isOperatorEnabled('[')) { + throw new Error('unexpected "[]", arrays are disabled'); + } + + this.parseExpression(instr); + this.expect(TBRACKET, ']'); + instr.push(binaryInstruction('[')); + } else { + throw new Error('unexpected symbol: ' + op.value); + } + } +}; + +function add(a, b) { + return Number(a) + Number(b); +} + +function sub(a, b) { + return a - b; +} + +function mul(a, b) { + return a * b; +} + +function div(a, b) { + return a / b; +} + +function mod(a, b) { + return a % b; +} + +function concat(a, b) { + if (Array.isArray(a) && Array.isArray(b)) { + return a.concat(b); + } + return '' + a + b; +} + +function equal(a, b) { + return a === b; +} + +function notEqual(a, b) { + return a !== b; +} + +function greaterThan(a, b) { + return a > b; +} + +function lessThan(a, b) { + return a < b; +} + +function greaterThanEqual(a, b) { + return a >= b; +} + +function lessThanEqual(a, b) { + return a <= b; +} + +function andOperator(a, b) { + return Boolean(a && b); +} + +function orOperator(a, b) { + return Boolean(a || b); +} + +function inOperator(a, b) { + return contains(b, a); +} + +function sinh(a) { + return ((Math.exp(a) - Math.exp(-a)) / 2); +} + +function cosh(a) { + return ((Math.exp(a) + Math.exp(-a)) / 2); +} + +function tanh(a) { + if (a === Infinity) return 1; + if (a === -Infinity) return -1; + return (Math.exp(a) - Math.exp(-a)) / (Math.exp(a) + Math.exp(-a)); +} + +function asinh(a) { + if (a === -Infinity) return a; + return Math.log(a + Math.sqrt((a * a) + 1)); +} + +function acosh(a) { + return Math.log(a + Math.sqrt((a * a) - 1)); +} + +function atanh(a) { + return (Math.log((1 + a) / (1 - a)) / 2); +} + +function log10(a) { + return Math.log(a) * Math.LOG10E; +} + +function neg(a) { + return -a; +} + +function not(a) { + return !a; +} + +function trunc(a) { + return a < 0 ? Math.ceil(a) : Math.floor(a); +} + +function random(a) { + return Math.random() * (a || 1); +} + +function factorial(a) { // a! + return gamma(a + 1); +} + +function isInteger(value) { + return isFinite(value) && (value === Math.round(value)); +} + +var GAMMA_G = 4.7421875; +var GAMMA_P = [ + 0.99999999999999709182, + 57.156235665862923517, -59.597960355475491248, + 14.136097974741747174, -0.49191381609762019978, + 0.33994649984811888699e-4, + 0.46523628927048575665e-4, -0.98374475304879564677e-4, + 0.15808870322491248884e-3, -0.21026444172410488319e-3, + 0.21743961811521264320e-3, -0.16431810653676389022e-3, + 0.84418223983852743293e-4, -0.26190838401581408670e-4, + 0.36899182659531622704e-5 +]; + +// Gamma function from math.js +function gamma(n) { + var t, x; + + if (isInteger(n)) { + if (n <= 0) { + return isFinite(n) ? Infinity : NaN; + } + + if (n > 171) { + return Infinity; // Will overflow + } + + var value = n - 2; + var res = n - 1; + while (value > 1) { + res *= value; + value--; + } + + if (res === 0) { + res = 1; // 0! is per definition 1 + } + + return res; + } + + if (n < 0.5) { + return Math.PI / (Math.sin(Math.PI * n) * gamma(1 - n)); + } + + if (n >= 171.35) { + return Infinity; // will overflow + } + + if (n > 85.0) { // Extended Stirling Approx + var twoN = n * n; + var threeN = twoN * n; + var fourN = threeN * n; + var fiveN = fourN * n; + return Math.sqrt(2 * Math.PI / n) * Math.pow((n / Math.E), n) * + (1 + (1 / (12 * n)) + (1 / (288 * twoN)) - (139 / (51840 * threeN)) - + (571 / (2488320 * fourN)) + (163879 / (209018880 * fiveN)) + + (5246819 / (75246796800 * fiveN * n))); + } + + --n; + x = GAMMA_P[0]; + for (var i = 1; i < GAMMA_P.length; ++i) { + x += GAMMA_P[i] / (n + i); + } + + t = n + GAMMA_G + 0.5; + return Math.sqrt(2 * Math.PI) * Math.pow(t, n + 0.5) * Math.exp(-t) * x; +} + +function stringOrArrayLength(s) { + if (Array.isArray(s)) { + return s.length; + } + return String(s).length; +} + +function hypot() { + var sum = 0; + var larg = 0; + for (var i = 0; i < arguments.length; i++) { + var arg = Math.abs(arguments[i]); + var div; + if (larg < arg) { + div = larg / arg; + sum = (sum * div * div) + 1; + larg = arg; + } else if (arg > 0) { + div = arg / larg; + sum += div * div; + } else { + sum += arg; + } + } + return larg === Infinity ? Infinity : larg * Math.sqrt(sum); +} + +function condition(cond, yep, nope) { + return cond ? yep : nope; +} + +/** +* Decimal adjustment of a number. +* From @escopecz. +* +* @param {Number} value The number. +* @param {Integer} exp The exponent (the 10 logarithm of the adjustment base). +* @return {Number} The adjusted value. +*/ +function roundTo(value, exp) { + // If the exp is undefined or zero... + if (typeof exp === 'undefined' || +exp === 0) { + return Math.round(value); + } + value = +value; + exp = -(+exp); + // If the value is not a number or the exp is not an integer... + if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) { + return NaN; + } + // Shift + value = value.toString().split('e'); + value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp))); + // Shift back + value = value.toString().split('e'); + return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)); +} + +function setVar(name, value, variables) { + if (variables) variables[name] = value; + return value; +} + +function arrayIndex(array, index) { + return array[index | 0]; +} + +function max(array) { + if (arguments.length === 1 && Array.isArray(array)) { + return Math.max.apply(Math, array); + } else { + return Math.max.apply(Math, arguments); + } +} + +function min(array) { + if (arguments.length === 1 && Array.isArray(array)) { + return Math.min.apply(Math, array); + } else { + return Math.min.apply(Math, arguments); + } +} + +function arrayMap(f, a) { + if (typeof f !== 'function') { + throw new Error('First argument to map is not a function'); + } + if (!Array.isArray(a)) { + throw new Error('Second argument to map is not an array'); + } + return a.map(function (x, i) { + return f(x, i); + }); +} + +function arrayFold(f, init, a) { + if (typeof f !== 'function') { + throw new Error('First argument to fold is not a function'); + } + if (!Array.isArray(a)) { + throw new Error('Second argument to fold is not an array'); + } + return a.reduce(function (acc, x, i) { + return f(acc, x, i); + }, init); +} + +function arrayFilter(f, a) { + if (typeof f !== 'function') { + throw new Error('First argument to filter is not a function'); + } + if (!Array.isArray(a)) { + throw new Error('Second argument to filter is not an array'); + } + return a.filter(function (x, i) { + return f(x, i); + }); +} + +function stringOrArrayIndexOf(target, s) { + if (!(Array.isArray(s) || typeof s === 'string')) { + throw new Error('Second argument to indexOf is not a string or array'); + } + + return s.indexOf(target); +} + +function arrayJoin(sep, a) { + if (!Array.isArray(a)) { + throw new Error('Second argument to join is not an array'); + } + + return a.join(sep); +} + +function sign(x) { + return ((x > 0) - (x < 0)) || +x; +} + +var ONE_THIRD = 1/3; +function cbrt(x) { + return x < 0 ? -Math.pow(-x, ONE_THIRD) : Math.pow(x, ONE_THIRD); +} + +function expm1(x) { + return Math.exp(x) - 1; +} + +function log1p(x) { + return Math.log(1 + x); +} + +function log2(x) { + return Math.log(x) / Math.LN2; +} + +class Parser { + constructor(options) { + this.options = options || {}; + this.unaryOps = { + sin: Math.sin, + cos: Math.cos, + tan: Math.tan, + asin: Math.asin, + acos: Math.acos, + atan: Math.atan, + sinh: Math.sinh || sinh, + cosh: Math.cosh || cosh, + tanh: Math.tanh || tanh, + asinh: Math.asinh || asinh, + acosh: Math.acosh || acosh, + atanh: Math.atanh || atanh, + sqrt: Math.sqrt, + cbrt: Math.cbrt || cbrt, + log: Math.log, + log2: Math.log2 || log2, + ln: Math.log, + lg: Math.log10 || log10, + log10: Math.log10 || log10, + expm1: Math.expm1 || expm1, + log1p: Math.log1p || log1p, + abs: Math.abs, + ceil: Math.ceil, + floor: Math.floor, + round: Math.round, + trunc: Math.trunc || trunc, + '-': neg, + '+': Number, + exp: Math.exp, + not: not, + length: stringOrArrayLength, + '!': factorial, + sign: Math.sign || sign + }; + + this.binaryOps = { + '+': add, + '-': sub, + '*': mul, + '/': div, + '%': mod, + '^': Math.pow, + '||': concat, + '==': equal, + '!=': notEqual, + '>': greaterThan, + '<': lessThan, + '>=': greaterThanEqual, + '<=': lessThanEqual, + and: andOperator, + or: orOperator, + 'in': inOperator, + '=': setVar, + '[': arrayIndex + }; + + this.ternaryOps = { + '?': condition + }; + + this.functions = { + random: random, + fac: factorial, + min: min, + max: max, + hypot: Math.hypot || hypot, + pyt: Math.hypot || hypot, // backward compat + pow: Math.pow, + atan2: Math.atan2, + 'if': condition, + gamma: gamma, + roundTo: roundTo, + map: arrayMap, + fold: arrayFold, + filter: arrayFilter, + indexOf: stringOrArrayIndexOf, + join: arrayJoin + }; + + this.consts = { + E: Math.E, + PI: Math.PI, + 'true': true, + 'false': false + }; + } + + parse(expr) { + var instr = []; + var parserState = new ParserState( + this, + new TokenStream(this, expr), + { allowMemberAccess: this.options.allowMemberAccess } + ); + + parserState.parseExpression(instr); + parserState.expect(TEOF, 'EOF'); + + return new Expression(instr, this); + } + + evaluate(expr, variables) { + return this.parse(expr).evaluate(variables); + } +}; + + +var sharedParser = new Parser(); + +Parser.parse = function (expr) { + return sharedParser.parse(expr); +}; + +Parser.evaluate = function (expr, variables) { + return sharedParser.parse(expr).evaluate(variables); +}; + +var optionNameMap = { + '+': 'add', + '-': 'subtract', + '*': 'multiply', + '/': 'divide', + '%': 'remainder', + '^': 'power', + '!': 'factorial', + '<': 'comparison', + '>': 'comparison', + '<=': 'comparison', + '>=': 'comparison', + '==': 'comparison', + '!=': 'comparison', + '||': 'concatenate', + 'and': 'logical', + 'or': 'logical', + 'not': 'logical', + '?': 'conditional', + ':': 'conditional', + '=': 'assignment', + '[': 'array', + '()=': 'fndef' +}; + +function getOptionName(op) { + return optionNameMap.hasOwnProperty(op) ? optionNameMap[op] : op; +} + +Parser.prototype.isOperatorEnabled = function (op) { + var optionName = getOptionName(op); + var operators = this.options.operators || {}; + + return !(optionName in operators) || !!operators[optionName]; +}; + +/*! + Based on ndef.parser, by Raphael Graf(r@undefined.ch) + http://www.undefined.ch/mparser/index.html + + Ported to JavaScript and modified by Matthew Crumley (email@matthewcrumley.com, http://silentmatt.com/) + + You are free to use and modify this code in anyway you find useful. Please leave this comment in the code + to acknowledge its original source. If you feel like it, I enjoy hearing about projects that use my code, + but don't feel like you have to let me know or ask permission. +*/ + + diff --git a/common/src/history/color.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/color.js similarity index 58% rename from common/src/history/color.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/color.js index 0efa29d..4c52aa7 100644 --- a/common/src/history/color.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/color.js @@ -1,61 +1,59 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 - * + * Copyright (C) 2022 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 . */ -import EditedProperty from "./editproperty.mjs" -import Objects from "../module/objects.mjs" +.pragma library -export default class ColorChanged extends EditedProperty { +.import "editproperty.js" as EP +.import "../objects.js" as Objects + + +class ColorChanged extends EP.EditedProperty { // Action used everytime when an object's color is changed - type() { - return "ColorChanged" - } - - icon() { - return "appearance" - } - + type(){return 'ColorChanged'} + + icon(){return 'appearance'} + + constructor(targetName = "", targetType = "Point", oldColor = "black", newColor = "white") { super(targetName, targetType, "color", oldColor, newColor) } - + export() { return [this.targetName, this.targetType, this.previousValue, this.newValue] } - - color(darkVer = false) { - return darkVer ? "purple" : "plum" - } - + + color(darkVer=false){return darkVer ? 'purple' : 'plum'} + getReadableString() { - return qsTranslate("color", "%1 %2's color changed from %3 to %4.") - .arg(Objects.types[this.targetType].displayType()).arg(this.targetName) - .arg(this.previousValue).arg(this.newValue) + return qsTr("%1 %2's color changed from %3 to %4.") + .arg(Objects.types[this.targetType].displayType()).arg(this.targetName) + .arg(this.previousValue).arg(this.newValue) } - + formatColor(color) { return `██` } - + getHTMLString() { - return qsTranslate("color", "%1 %2's color changed from %3 to %4.") - .arg(Objects.types[this.targetType].displayType()) - .arg(" " + this.targetName + " ") - .arg(this.formatColor(this.previousValue)).arg(this.formatColor(this.newValue)) + return qsTr("%1 %2's color changed from %3 to %4.") + .arg(Objects.types[this.targetType].displayType()) + .arg(' ' + this.targetName + " ") + .arg(this.formatColor(this.previousValue)).arg(this.formatColor(this.newValue)) } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.js new file mode 100644 index 0000000..16f9c6e --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.js @@ -0,0 +1,58 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +var themeTextColor; + + +class Action { + // Type of the action done. + type(){return 'Unknown'} + + // Icon associated with the item + + // TargetName is the name of the object that's targeted by the event. + constructor(targetName = "", targetType = "Point") { + this.targetName = targetName + this.targetType = targetType + } + + undo() {} + + redo() {} + + export() { + return [this.targetName, this.targetType] + } + + // String used in the toolkit + getReadableString() { + return 'Unknown action' + } + + // Returns an HTML tag containing the icon of a type + getIconRichText(type) { + return `` + } + + // String used in the preview + getHTMLString() { + return this.getReadableString() + } +} diff --git a/common/src/history/create.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/create.js similarity index 56% rename from common/src/history/create.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/create.js index fb27f62..2c3fe18 100644 --- a/common/src/history/create.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/create.js @@ -1,69 +1,62 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 - * + * Copyright (C) 2022 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 . */ -import Objects from "../module/objects.mjs" -import { Action } from "./common.mjs" +.pragma library -/** - * Action used for the creation of an object. - */ -export default class CreateNewObject extends Action { - type() { - return "CreateNewObject" - } - - icon() { - return "create" - } - - color(darkVer = false) { - return darkVer ? "green" : "lime" - } +.import "../objects.js" as Objects +.import "common.js" as C +class CreateNewObject extends C.Action { + // Action used for the creation of an object + type(){return 'CreateNewObject'} + + icon(){return 'create'} + + color(darkVer=false){return darkVer ? 'green' : 'lime'} + constructor(targetName = "", targetType = "Point", properties = []) { super(targetName, targetType) this.targetProperties = properties } - + undo() { - Objects.deleteObject(this.targetName) + var targetIndex = Objects.getObjectsName(this.targetType).indexOf(this.targetName) + Objects.currentObjects[this.targetType][targetIndex].delete() + Objects.currentObjects[this.targetType].splice(targetIndex, 1) } - + redo() { Objects.createNewRegisteredObject(this.targetType, this.targetProperties) - Objects.currentObjectsByName[this.targetName].update() } - + export() { return [this.targetName, this.targetType, this.targetProperties] } - + getReadableString() { - return qsTranslate("create", "New %1 %2 created.") - .arg(Objects.types[this.targetType].displayType()) - .arg(this.targetName) + return qsTr("New %1 %2 created.").arg(Objects.types[this.targetType].displayType()).arg(this.targetName) } - + getHTMLString() { - return qsTranslate("create", "New %1 %2 created.") - .arg(Objects.types[this.targetType].displayType()) - .arg("" + this.targetName + "") + return qsTr("New %1 %2 created.") + .arg(Objects.types[this.targetType].displayType()) + .arg('' + this.targetName + "") } } diff --git a/common/src/history/delete.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/delete.js similarity index 69% rename from common/src/history/delete.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/delete.js index 2f8d8fe..407f29c 100644 --- a/common/src/history/delete.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/delete.js @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,14 +16,14 @@ * along with this program. If not, see . */ -import Objects from "../module/objects.mjs" -import CreateNewObject from "./create.mjs" +.pragma library + +.import "../objects.js" as Objects +.import "create.js" as Create -/** - * Action used at the deletion of an object. Basically the same thing as creating a new object, except Redo & Undo are reversed. - */ -export default class DeleteObject extends CreateNewObject { +class DeleteObject extends Create.CreateNewObject { + // Action used at the deletion of an object. Basicly the same thing as creating a new object, except Redo & Undo are reversed. type(){return 'DeleteObject'} icon(){return 'delete'} @@ -39,13 +39,11 @@ export default class DeleteObject extends CreateNewObject { } getReadableString() { - return qsTranslate("delete", "%1 %2 deleted.") - .arg(Objects.types[this.targetType].displayType()) - .arg(this.targetName) + return qsTr("%1 %2 deleted.").arg(Objects.types[this.targetType].displayType()).arg(this.targetName) } getHTMLString() { - return qsTranslate("delete", "%1 %2 deleted.") + return qsTr("%1 %2 deleted.") .arg(Objects.types[this.targetType].displayType()) .arg('' + this.targetName + "") } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/editproperty.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/editproperty.js new file mode 100644 index 0000000..31c21df --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/editproperty.js @@ -0,0 +1,121 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "../objects.js" as Objects +.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 + type(){return 'EditedProperty'} + + icon(){return 'modify'} + + color(darkVer=false){ + return darkVer ? 'darkslateblue' : 'cyan'; + } + + constructor(targetName = "", targetType = "Point", targetProperty = "visible", previousValue = false, newValue = true, valueIsExpressionNeedingImport = false) { + super(targetName, targetType) + this.targetProperty = targetProperty + this.targetPropertyReadable = qsTranslate("prop", this.targetProperty) + this.previousValue = previousValue + this.newValue = newValue + this.propertyType = Objects.types[targetType].properties()[targetProperty] + if(valueIsExpressionNeedingImport) { + if(this.propertyType == "Expression") { + this.previousValue = new MathLib.Expression(this.previousValue); + this.newValue = new MathLib.Expression(this.newValue); + } else if(this.propertyType == "Domain") { + this.previousValue = MathLib.parseDomain(this.previousValue); + this.newValue = MathLib.parseDomain(this.newValue); + } else { + // Objects + this.previousValue = Objects.getObjectByName(this.previousValue); + this.newValue = Objects.getObjectByName(this.newValue); + } + } + this.setReadableValues() + } + + undo() { + Objects.getObjectByName(this.targetName, this.targetType)[this.targetProperty] = this.previousValue + } + + redo() { + Objects.getObjectByName(this.targetName, this.targetType)[this.targetProperty] = this.newValue + } + + export() { + if(this.previousValue instanceof MathLib.Expression) { + return [this.targetName, this.targetType, this.targetProperty, this.previousValue.toEditableString(), this.newValue.toEditableString(), true] + } else if(this.previousValue instanceof Common.DrawableObject) { + return [this.targetName, this.targetType, this.targetProperty, this.previousValue.name, this.newValue.name, true] + } else { + return [this.targetName, this.targetType, this.targetProperty, this.previousValue, this.newValue, false] + } + } + + setReadableValues() { + this.prev = ""; + this.next = ""; + if(this.propertyType instanceof Object) { + switch(this.propertyType.type) { + case "Enum": + this.prev = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.previousValue)] + this.next = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.newValue)] + break; + case "ObjectType": + this.prev = this.previousValue == null ? "null" : this.previousValue.name + this.next = this.newValue == null ? "null" : this.newValue.name + break; + case "List": + this.prev = this.previousValue.join(",") + this.next = this.newValue.name.join(",") + break; + case "Dict": + this.prev = JSON.stringify(this.previousValue).replace("'", "\\'").replace('"', "'") + this.next = JSON.stringify(this.newValue).replace("'", "\\'").replace('"', "'") + break; + } + } else { + this.prev = this.previousValue == null ? "null" : this.previousValue.toString() + this.next = this.newValue == null ? "null" : this.newValue.toString() + } + } + + getReadableString() { + return qsTr('%1 of %2 %3 changed from "%4" to "%5".') + .arg(this.targetPropertyReadable) + .arg(Objects.types[this.targetType].displayType()) + .arg(this.targetName).arg(this.prev).arg(this.next) + } + + getHTMLString() { + return qsTr('%1 of %2 changed from %3 to %4.') + .arg(this.targetPropertyReadable) + .arg(' ' + this.targetName + ' ') + .arg(' '+this.prev+' ') + .arg(' '+this.next+'') +// .arg('' + Objects.types[this.targetType].displayType()) + + } +} diff --git a/common/src/history/name.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/name.js similarity index 75% rename from common/src/history/name.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/name.js index 61bb6b5..015895f 100644 --- a/common/src/history/name.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/name.js @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,17 +16,19 @@ * along with this program. If not, see . */ -import EditedProperty from "./editproperty.mjs" -import Objects from "../module/objects.mjs" +.pragma library -/** - * Action used everytime an object's name has been changed. - */ -export default class NameChanged extends EditedProperty { +.import "editproperty.js" as EP +.import "../objects.js" as Objects + + +class NameChanged extends EP.EditedProperty { + // Action used everytime an object's property has been changed type(){return 'NameChanged'} icon(){return 'name'} - + + color(darkVer=false){return darkVer ? 'darkorange' : 'orange'} constructor(targetName = "", targetType = "Point", newName = "") { @@ -38,21 +40,21 @@ export default class NameChanged extends EditedProperty { } undo() { - Objects.renameObject(this.newValue, this.previousValue) + Objects.getObjectByName(this.newValue, this.targetType)['name'] = this.previousValue } redo() { - Objects.renameObject(this.previousValue, this.newValue) + Objects.getObjectByName(this.previousValue, this.targetType)['name'] = this.newValue } getReadableString() { - return qsTranslate("name", '%1 %2 renamed to %3.') + return qsTr('%1 %2 renamed to %3.') .arg(Objects.types[this.targetType].displayType()) .arg(this.targetName).arg(this.newValue) } getHTMLString() { - return qsTranslate("name", '%1 %2 renamed to %3.') + return qsTr('%1 %2 renamed to %3.') .arg(Objects.types[this.targetType].displayType()) .arg('' + this.targetName + "").arg(''+this.newValue+'') } diff --git a/common/src/history/visibility.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/visibility.js similarity index 60% rename from common/src/history/visibility.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/visibility.js index 9254d67..ec1a5c8 100644 --- a/common/src/history/visibility.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/visibility.js @@ -1,61 +1,57 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 - * + * Copyright (C) 2022 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 . */ -import EditedProperty from "./editproperty.mjs" -import Objects from "../module/objects.mjs" +.pragma library + +.import "editproperty.js" as EP +.import "../objects.js" as Objects -/** - * Action used when an object's shown or hidden. - */ -export default class EditedVisibility extends EditedProperty { - type() { - return "EditedVisibility" - } - - icon() { - return "visibility" - } - - color(darkVer = false) { +class EditedVisibility extends EP.EditedProperty { + // Action used when an object's shown or hidden. + type(){return 'EditedVisibility'} + + icon(){return 'visibility'} + + color(darkVer=false){ return this.newValue ? - (darkVer ? "darkgray" : "whitesmoke") : - (darkVer ? "dimgray" : "lightgray") + (darkVer ? 'darkgray' : 'whitesmoke') : + (darkVer ? 'dimgray' : 'lightgray') } - + constructor(targetName = "", targetType = "Point", newValue = true) { super(targetName, targetType, "visible", !newValue, newValue) } - + export() { return [this.targetName, this.targetType, this.newValue] } - + getReadableString() { - return (this.newValue ? qsTranslate("visibility", "%1 %2 shown.") : qsTranslate("visibility", "%1 %2 hidden.")) + return (this.newValue ? qsTr('%1 %2 shown.') : qsTr('%1 %2 hidden.')) .arg(Objects.types[this.targetType].displayType()) .arg(this.targetName) } - + getHTMLString() { - return (this.newValue ? qsTranslate("visibility", "%1 %2 shown.") : qsTranslate("visibility", "%1 %2 hidden.")) + return (this.newValue ? qsTr('%1 %2 shown.') : qsTr('%1 %2 hidden.')) .arg(Objects.types[this.targetType].displayType()) - .arg("" + this.targetName + "") + .arg('' + this.targetName + "") } } diff --git a/common/src/history/index.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/historylib.js similarity index 65% rename from common/src/history/index.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/historylib.js index a2ef1ea..c360cb7 100644 --- a/common/src/history/index.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/historylib.js @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -18,32 +18,32 @@ // This library helps containing actions to be undone or redone (in other words, editing history) // Each type of event is repertoried as an action that can be listed for everything that's undoable. +.pragma library -import { Action as A } from "./common.mjs" -import Create from "./create.mjs" -import Delete from "./delete.mjs" -import EP from "./editproperty.mjs" -import Pos from "./position.mjs" -import V from "./visibility.mjs" -import Name from "./name.mjs" -import Color from "./color.mjs" +.import "history/common.js" as Common +.import "history/create.js" as Create +.import "history/delete.js" as Delete +.import "history/editproperty.js" as EP +.import "history/visibility.js" as V +.import "history/name.js" as Name +.import "history/color.js" as Color + +var history = null; -export const Action = A -export const CreateNewObject = Create -export const DeleteObject = Delete -export const EditedProperty = EP -export const EditedPosition = Pos -export const EditedVisibility = V -export const NameChanged = Name -export const ColorChanged = Color +var Action = Common.Action +var CreateNewObject = Create.CreateNewObject +var DeleteObject = Delete.DeleteObject +var EditedProperty = EP.EditedProperty +var EditedVisibility = V.EditedVisibility +var NameChanged = Name.NameChanged +var ColorChanged = Color.ColorChanged -export const Actions = { +var Actions = { "Action": Action, "CreateNewObject": CreateNewObject, "DeleteObject": DeleteObject, "EditedProperty": EditedProperty, - "EditedPosition": EditedPosition, "EditedVisibility": EditedVisibility, "NameChanged": NameChanged, "ColorChanged": ColorChanged, diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js new file mode 100644 index 0000000..4cadcf5 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js @@ -0,0 +1,50 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "../expr-eval.js" as ExprEval +.import "../utils.js" as Utils +.import "latex.js" as Latex + +var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manualy + "pi": Math.PI, + "π": Math.PI, + "inf": Infinity, + "Infinity": Infinity, + "∞": Infinity, + "e": Math.E, + "E": Math.E +} + +var currentVars = {} + +const parser = new ExprEval.Parser() +parser.functions.integral = function(a, b, f, variable) { + // https://en.wikipedia.org/wiki/Simpson%27s_rule + f = parser.parse(f).toJSFunction(variable, currentVars) + return (b-a)/6*(f(a)+4*f((a+b)/2)+f(b)) +} + +const DERIVATION_PRECISION = 0.1 + +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 +} + diff --git a/common/src/math/domain.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/domain.js similarity index 64% rename from common/src/math/domain.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/domain.js index b81e527..48c23cf 100644 --- a/common/src/math/domain.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/domain.js @@ -1,67 +1,59 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 - * + * Copyright (C) 2022 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 . */ -import { Expression, executeExpression } from "./expression.mjs" +.pragma library + +.import "expression.js" as Expr /** * Main abstract domain class * It doesn't represent any kind of domain and is meant to be extended. */ -export class Domain { - constructor() { - this.latexMarkup = "#INVALID" - } - +class Domain { + constructor() {} + /** * Checks whether x is included in the domain. * @param {number} x - The x value. - * @return {boolean} true if included, false otherwise. + * @return {bool} true if included, false otherwise. */ - includes(x) { - return false - } - + includes(x) { return false } + /** * Returns a string representation of the domain. * @return {string} String representation of the domain. */ - toString() { - return "???" - } - + toString() { return '???' } + /** * Returns a new domain that is the union between this domain and another. * @param {Domain} domain - Domain to unionise with this. * @return {Domain} newly created domain. */ - union(domain) { - return domain - } - + union(domain) { return domain } + /** * Returns a new domain that is the intersection between this domain and another. * @param {Domain} domain - Domain to get the interscection with this. * @return {Domain} newly created domain. */ - intersection(domain) { - return this - } - + intersection(domain) { return this } + /** * Imports a domain from a string. * @return {Domain} Found domain, string otherwise. @@ -71,88 +63,87 @@ export class Domain { case "R": case "ℝ": return Domain.R + break; case "RE": case "R*": case "ℝ*": return Domain.RE + break; case "RP": case "R+": case "ℝ⁺": - case "ℝ+": return Domain.RP + break; case "RM": case "R-": case "ℝ⁻": - case "ℝ-": return Domain.RM + break; case "RPE": case "REP": case "R+*": case "R*+": case "ℝ*⁺": case "ℝ⁺*": - case "ℝ*+": - case "ℝ+*": return Domain.RPE + break; case "RME": case "REM": case "R-*": case "R*-": case "ℝ⁻*": case "ℝ*⁻": - case "ℝ-*": - case "ℝ*-": return Domain.RME + break; case "ℕ": case "N": case "ZP": - case "Z+": case "ℤ⁺": - case "ℤ+": return Domain.N + break; case "NLOG": case "ℕˡᵒᵍ": - case "ℕLOG": return Domain.NLog + break; case "NE": case "NP": case "N*": case "N+": case "ℕ*": case "ℕ⁺": - case "ℕ+": case "ZPE": case "ZEP": case "Z+*": case "Z*+": case "ℤ⁺*": case "ℤ*⁺": - case "ℤ+*": - case "ℤ*+": return Domain.NE + break; case "Z": case "ℤ": return Domain.Z + break; case "ZM": case "Z-": case "ℤ⁻": - case "ℤ-": return Domain.ZM + break; case "ZME": case "ZEM": case "Z-*": case "Z*-": case "ℤ⁻*": case "ℤ*⁻": - case "ℤ-*": - case "ℤ*-": return Domain.ZME + break; case "ZE": case "Z*": case "ℤ*": return Domain.ZE + break; default: - return Domain.EmptySet + return new EmptySet() + break; } } } @@ -160,63 +151,50 @@ export class Domain { /** * Represents an empty set. */ -export class EmptySet extends Domain { +class EmptySet extends Domain { constructor() { super() this.displayName = "∅" this.latexMarkup = "\\emptyset" } - - includes(x) { - return false - } - - toString() { - return this.displayName - } - - union(domain) { - return domain - } - - intersection(domain) { - return this - } - - static import(frm) { - return new EmptySet() - } + + includes(x) { return false } + + toString() { return this.displayName } + + union(domain) { return domain } + + intersection(domain) { return this } + + static import(frm) { return new EmptySet() } } -Domain.EmptySet = new EmptySet() // To prevent use prior to declaration. - /** * Domain classes for ranges (e.g ]0;3[, [1;2[ ...) */ -export class Range extends Domain { +class Range extends Domain { constructor(begin, end, openBegin, openEnd) { super() - if(typeof begin == "number" || typeof begin == "string") begin = new Expression(begin.toString()) + if(typeof begin == 'number' || typeof begin == 'string') begin = new Expr.Expression(begin.toString()) this.begin = begin - if(typeof end == "number" || typeof end == "string") end = new Expression(end.toString()) + if(typeof end == 'number' || typeof end == 'string') end = new Expr.Expression(end.toString()) this.end = end this.openBegin = openBegin this.openEnd = openEnd this.displayName = (openBegin ? "]" : "[") + begin.toString() + ";" + end.toString() + (openEnd ? "[" : "]") this.latexMarkup = `\\mathopen${openBegin ? "]" : "["}${this.begin.latexMarkup};${this.end.latexMarkup}\\mathclose${openEnd ? "[" : "]"}` } - + includes(x) { - if(typeof x == "string") x = executeExpression(x) - if(x instanceof Expression) x = x.execute() + if(typeof x == 'string') x = Expr.executeExpression(x) return ((this.openBegin && x > this.begin.execute()) || (!this.openBegin && x >= this.begin.execute())) && ((this.openEnd && x < this.end.execute()) || (!this.openEnd && x <= this.end.execute())) } - + toString() { return this.displayName } - + union(domain) { if(domain instanceof EmptySet) return this if(domain instanceof DomainSet) return domain.union(this) @@ -225,7 +203,7 @@ export class Range extends Domain { if(domain instanceof MinusDomain) return new UnionDomain(this, domain) if(domain instanceof Range) return new UnionDomain(this, domain) } - + intersection(domain) { if(domain instanceof EmptySet) return domain if(domain instanceof DomainSet) return domain.intersection(this) @@ -234,11 +212,11 @@ export class Range extends Domain { if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) if(domain instanceof Range) return new IntersectionDomain(this, domain) } - + static import(frm) { - let openBegin = frm.trim().charAt(0) === "]" - let openEnd = frm.trim().charAt(frm.length - 1) === "[" - let [begin, end] = frm.substring(1, frm.length - 1).split(";") + var openBegin = frm.trim().charAt(0) == "]" + var openEnd = frm.trim().charAt(frm.length -1) == "[" + var [begin, end] = frm.substr(1, frm.length-2).split(";") return new Range(begin.trim(), end.trim(), openBegin, openEnd) } } @@ -246,49 +224,45 @@ export class Range extends Domain { /** * Domain classes for special domains (N, Z, ...) */ -export class SpecialDomain extends Domain { +class SpecialDomain extends Domain { /** * @constructs SpecialDomain * @param {string} displayName - * @param {string} latexMarkup - markup representing the domain. * @param {function} isValid - function returning true when number is in domain false when it isn't. * @param {function} next - function provides the next positive value in the domain after the one given. * @param {function} previous - function provides the previous positive value in the domain before the one given. - * @param {boolean} moveSupported - Only true if next and previous functions are valid. + * @param {bool} moveSupported - Only true if next and previous functions are valid. + * @param items */ - constructor(displayName, latexMarkup, isValid, next = () => true, previous = () => true, - moveSupported = true) { + constructor(displayName, isValid, next = x => true, previous = x => true, + moveSupported = true) { super() this.displayName = displayName - this.latexMarkup = latexMarkup this.isValid = isValid this.nextValue = next this.prevValue = previous this.moveSupported = moveSupported } - + includes(x) { - if(x instanceof Expression) x = x.execute() - if(typeof x == "string") x = executeExpression(x) + if(typeof x == 'string') x = Expr.executeExpression(x) return this.isValid(x) } - + next(x) { - if(x instanceof Expression) x = x.execute() - if(typeof x == "string") x = executeExpression(x) + if(typeof x == 'string') x = Expr.executeExpression(x) return this.nextValue(x) } - + previous(x) { - if(x instanceof Expression) x = x.execute() - if(typeof x == "string") x = executeExpression(x) + if(typeof x == 'string') x = Expr.executeExpression(x) return this.prevValue(x) } - + toString() { return this.displayName } - + union(domain) { if(domain instanceof EmptySet) return this if(domain instanceof DomainSet) return domain.union(this) @@ -297,7 +271,7 @@ export class SpecialDomain extends Domain { if(domain instanceof MinusDomain) return new UnionDomain(this, domain) if(domain instanceof Range) return new UnionDomain(this, domain) } - + intersection(domain) { if(domain instanceof EmptySet) return domain if(domain instanceof DomainSet) return domain.intersection(this) @@ -311,125 +285,122 @@ export class SpecialDomain extends Domain { /** * Domain classes for sets (e.g {0;3}, {0;1;2;pi} ...) */ -export class DomainSet extends SpecialDomain { +class DomainSet extends SpecialDomain { constructor(values) { - super("", () => true, x => x, true) - let newVals = {} + super('', x => true, x => x, true) + var newVals = {} this.executedValues = [] - for(let value of values) { - let expr = new Expression(value.toString()) - let ex = expr.execute() + for(var value of values) { + var expr = new Expr.Expression(value.toString()) + var ex = expr.execute() newVals[ex] = expr this.executedValues.push(ex) } - this.executedValues.sort((a, b) => a - b) + this.executedValues.sort((a,b) => a-b) this.values = this.executedValues.map(val => newVals[val]) this.displayName = "{" + this.values.join(";") + "}" this.latexMarkup = `\\{${this.values.join(";")}\\}` } - + includes(x) { - if(x instanceof Expression) x = x.execute() - if(typeof x == "string") x = executeExpression(x) - for(let value of this.values) - if(x === value.execute()) return true + if(typeof x == 'string') x = Expr.executeExpression(x) + for(var value of this.values) + if(x == value.execute()) return true return false } - + next(x) { - if(x instanceof Expression) x = x.execute() - if(typeof x == "string") x = executeExpression(x) + if(typeof x == 'string') x = Expr.executeExpression(x) if(x < this.executedValues[0]) return this.executedValues[0] - for(let i = 1; i < this.values.length; i++) { - let prevValue = this.executedValues[i - 1] - let value = this.executedValues[i] + for(var i = 1; i < this.values.length; i++) { + var prevValue = this.executedValues[i-1] + var value = this.executedValues[i] if(x >= prevValue && x < value) return value } return null } - + previous(x) { - if(x instanceof Expression) x = x.execute() - if(typeof x == "string") x = executeExpression(x) - if(x > this.executedValues[this.executedValues.length - 1]) - return this.executedValues[this.executedValues.length - 1] - for(let i = 1; i < this.values.length; i++) { - let prevValue = this.executedValues[i - 1] - let value = this.executedValues[i] + if(typeof x == 'string') x = Expr.executeExpression(x) + if(x > this.executedValues[this.executedValues.length-1]) + return this.executedValues[this.executedValues.length-1] + for(var i = 1; i < this.values.length; i++) { + var prevValue = this.executedValues[i-1] + var value = this.executedValues[i] if(x > prevValue && x <= value) return prevValue } return null } - + toString() { return this.displayName } - + union(domain) { if(domain instanceof EmptySet) return this if(domain instanceof DomainSet) { - let newValues = [] - let values = this.values.concat(domain.values).filter(function(val) { + var newValues = [] + var values = this.values.concat(domain.values).filter(function(val){ newValues.push(val.execute()) - return newValues.indexOf(val.execute()) === newValues.length - 1 + return newValues.indexOf(val.execute()) == newValues.length - 1 }) return new DomainSet(values) } - let notIncludedValues = [] - for(let i = 0; i < this.values.length; i++) { - let value = this.executedValues[i] + var notIncludedValues = [] + for(var value in this.values) { + var value = this.executedValues[i] if(domain instanceof Range) { - if(domain.begin.execute() === value && domain.openBegin) { + if(domain.begin.execute() == value && domain.openBegin) { domain.openBegin = false } - if(domain.end.execute() === value && domain.openEnd) { + if(domain.end.execute() == value && domain.openEnd) { domain.openEnd = false } } - if(!domain.includes(value)) + if(!domain.includes(value)) notIncludedValues.push(this.values[i].toEditableString()) } - if(notIncludedValues.length === 0) return domain + if(notIncludedValues.length == 0) return domain return new UnionDomain(domain, new DomainSet(notIncludedValues)) } - + intersection(domain) { if(domain instanceof EmptySet) return domain if(domain instanceof DomainSet) { - let domValues = domain.values.map(expr => expr.execute()) - this.values = this.values.filter(function(val) { + var domValues = domain.values.map(expr => expr.execute()) + this.values = this.values.filter(function(val){ return domValues.indexOf(val.execute()) >= 0 }) return this } - let includedValues = [] - for(let i in this.values) { - let value = this.executedValues[i] + var includedValues = [] + for(var i in this.values) { + var value = this.executedValues[i] if(domain instanceof Range) { - if(domain.begin.execute() === value && !domain.openBegin) { + if(domain.begin.execute() == value && !domain.openBegin) { domain.openBegin = false } - if(domain.end.execute() === value && !domain.openEnd) { + if(domain.end.execute() == value && !domain.openEnd) { domain.openEnd = false } } - if(domain.includes(value)) + if(domain.includes(value)) includedValues.push(this.values[i].toEditableString()) } - if(includedValues.length === 0) return new EmptySet() - if(includedValues.length === this.values.length) return this + if(includedValues.length == 0) return new EmptySet() + if(includedValues.length == this.values.length) return this return new IntersectionDomain(domain, new DomainSet(includedValues)) } - + static import(frm) { - return new DomainSet(frm.substring(1, frm.length - 1).split(";")) + return new DomainSet(frm.substr(1, frm.length-2).split(";")) } } /** * Domain representing the union between two domains. */ -export class UnionDomain extends Domain { +class UnionDomain extends Domain { constructor(dom1, dom2) { super() this.dom1 = dom1 @@ -437,15 +408,15 @@ export class UnionDomain extends Domain { this.displayName = this.dom1.toString() + " ∪ " + this.dom2.toString() this.latexMarkup = `${dom1.latexMarkup}\\cup${dom2.latexMarkup}` } - + includes(x) { return this.dom1.includes(x) || this.dom2.includes(x) } - + toString() { return this.displayName } - + union(domain) { if(domain instanceof EmptySet) return this if(domain instanceof DomainSet) return domain.union(this) @@ -454,7 +425,7 @@ export class UnionDomain extends Domain { if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain) if(domain instanceof MinusDomain) return new MinusDomain(this, domain) } - + intersection(domain) { if(domain instanceof EmptySet) return domain if(domain instanceof DomainSet) return domain.intersection(this) @@ -462,12 +433,12 @@ export class UnionDomain extends Domain { if(domain instanceof IntersectionDomain) return this.dom1.intersection(domain.dom1).intersection(this.dom2).intersection(domain.dom2) if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) } - + static import(frm) { - let domains = frm.trim().split("∪") - if(domains.length === 1) domains = frm.trim().split("U") // Fallback - let dom2 = parseDomain(domains.pop()) - let dom1 = parseDomain(domains.join("∪")) + var domains = frm.trim().split("∪") + if(domains.length == 1) domains = frm.trim().split("U") // Fallback + var dom1 = parseDomain(domains.pop()) + var dom2 = parseDomain(domains.join('∪')) return dom1.union(dom2) } } @@ -475,7 +446,7 @@ export class UnionDomain extends Domain { /** * Domain representing the intersection between two domains. */ -export class IntersectionDomain extends Domain { +class IntersectionDomain extends Domain { constructor(dom1, dom2) { super() this.dom1 = dom1 @@ -483,24 +454,24 @@ export class IntersectionDomain extends Domain { this.displayName = dom1.toString() + " ∩ " + dom2.toString() this.latexMarkup = `${dom1.latexMarkup}\\cap${dom2.latexMarkup}` } - + includes(x) { return this.dom1.includes(x) && this.dom2.includes(x) } - + toString() { return this.displayName } - + union(domain) { if(domain instanceof EmptySet) return this if(domain instanceof DomainSet) return domain.union(this) if(domain instanceof Range) return domain.union(this) - if(domain instanceof UnionDomain) return this.dom1.union(domain.dom1).union(this.dom2).union(domain.dom2) + if(domain instanceof UnionDomain) return this.dom1.union(domain.dom1).union(this.dom2).union(domain.dom2) if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain) if(domain instanceof MinusDomain) return new MinusDomain(this, domain) } - + intersection(domain) { if(domain instanceof EmptySet) return domain if(domain instanceof DomainSet) return domain.intersection(this) @@ -508,11 +479,11 @@ export class IntersectionDomain extends Domain { if(domain instanceof IntersectionDomain) return new IntersectionDomain(this, domain) if(domain instanceof MinusDomain) return new IntersectionDomain(this, domain) } - + static import(frm) { - let domains = frm.trim().split("∩") - let dom1 = parseDomain(domains.pop()) - let dom2 = parseDomain(domains.join("∩")) + var domains = frm.trim().split("∩") + var dom1 = parseDomain(domains.pop()) + var dom2 = parseDomain(domains.join('∩')) return dom1.intersection(dom2) } } @@ -520,7 +491,7 @@ export class IntersectionDomain extends Domain { /** * Domain representing the minus between two domains. */ -export class MinusDomain extends Domain { +class MinusDomain extends Domain { constructor(dom1, dom2) { super() this.dom1 = dom1 @@ -528,20 +499,20 @@ export class MinusDomain extends Domain { this.displayName = dom1.toString() + "∖" + dom2.toString() this.latexMarkup = `${dom1.latexMarkup}\\setminus${dom2.latexMarkup}` } - + includes(x) { return this.dom1.includes(x) && !this.dom2.includes(x) } - + toString() { return this.displayName } - + static import(frm) { - let domains = frm.trim().split("∖") - if(domains.length === 1) domains = frm.trim().split("\\") // Fallback - let dom1 = parseDomain(domains.shift()) - let dom2 = parseDomain(domains.join("∪")) + var domains = frm.trim().split("∖") + if(domains.length == 1) domains = frm.trim().split("\\") // Fallback + var dom1 = parseDomain(domains.shift()) + var dom2 = parseDomain(domains.join('∪')) return new MinusDomain(dom1, dom2) } } @@ -550,104 +521,89 @@ Domain.RE = new MinusDomain("R", "{0}") Domain.RE.displayName = "ℝ*" Domain.RE.latexMarkup = "\\mathbb{R}^{*}" -Domain.R = new Range(-Infinity, Infinity, true, true) +Domain.R = new Range(-Infinity,Infinity,true,true) Domain.R.displayName = "ℝ" Domain.R.latexMarkup = "\\mathbb{R}" -Domain.RP = new Range(0, Infinity, true, false) +Domain.RP = new Range(0,Infinity,true,false) Domain.RP.displayName = "ℝ⁺" Domain.RP.latexMarkup = "\\mathbb{R}^{+}" -Domain.RM = new Range(-Infinity, 0, true, false) +Domain.RM = new Range(-Infinity,0,true,false) Domain.RM.displayName = "ℝ⁻" Domain.RM.latexMarkup = "\\mathbb{R}^{-}" -Domain.RPE = new Range(0, Infinity, true, true) +Domain.RPE = new Range(0,Infinity,true,true) Domain.RPE.displayName = "ℝ⁺*" 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.latexMarkup = "\\mathbb{R}^{+*}" -Domain.N = new SpecialDomain( - "ℕ", "\\mathbb{N}", - x => x % 1 === 0 && x >= 0, - x => Math.max(Math.floor(x) + 1, 0), - x => Math.max(Math.ceil(x) - 1, 0) -) -Domain.NE = new SpecialDomain( - "ℕ*", "\\mathbb{N}^{*}", - x => x % 1 === 0 && x > 0, - x => Math.max(Math.floor(x) + 1, 1), - x => Math.max(Math.ceil(x) - 1, 1) -) -Domain.Z = new SpecialDomain( - "ℤ", "\\mathbb{Z}", - x => x % 1 === 0, - x => Math.floor(x) + 1, - x => Math.ceil(x) - 1 -) -Domain.ZE = new SpecialDomain( - "ℤ*", "\\mathbb{Z}^{*}", - x => x % 1 === 0 && x !== 0, - x => Math.floor(x) + 1 === 0 ? Math.floor(x) + 2 : Math.floor(x) + 1, - x => Math.ceil(x) - 1 === 0 ? Math.ceil(x) - 2 : Math.ceil(x) - 1 -) -Domain.ZM = new SpecialDomain( - "ℤ⁻", "\\mathbb{Z}^{-}", - x => x % 1 === 0 && x <= 0, - x => Math.min(Math.floor(x) + 1, 0), - x => Math.min(Math.ceil(x) - 1, 0) -) -Domain.ZME = new SpecialDomain( - "ℤ⁻*", "\\mathbb{Z}^{-*}", - x => x % 1 === 0 && x < 0, - x => Math.min(Math.floor(x) + 1, -1), - x => Math.min(Math.ceil(x) - 1, -1) -) -Domain.NLog = new SpecialDomain( - "ℕˡᵒᵍ", "\\mathbb{N}^{log}", - x => x / Math.pow(10, Math.ceil(Math.log10(x))) % 1 === 0 && x > 0, - x => { - let x10pow = Math.pow(10, Math.ceil(Math.log10(x))) - return Math.max(1, (Math.floor(x / x10pow) + 1) * x10pow) - }, - x => { - let x10pow = Math.pow(10, Math.ceil(Math.log10(x))) - return Math.max(1, (Math.ceil(x / x10pow) - 1) * x10pow) - } -) +Domain.N = new SpecialDomain('ℕ', x => x%1==0 && x >= 0, + x => Math.max(Math.floor(x)+1, 0), + x => Math.max(Math.ceil(x)-1, 0)) +Domain.N.latexMarkup = "\\mathbb{N}" +Domain.NE = new SpecialDomain('ℕ*', x => x%1==0 && x > 0, + x => Math.max(Math.floor(x)+1, 1), + x => Math.max(Math.ceil(x)-1, 1)) +Domain.NE.latexMarkup = "\\mathbb{N}^{*}" +Domain.Z = new SpecialDomain('ℤ', x => x%1==0, x => Math.floor(x)+1, x => Math.ceil(x)-1) +Domain.Z.latexMarkup = "\\mathbb{Z}" +Domain.ZE = new SpecialDomain('ℤ*', x => x%1==0 && x != 0, + x => Math.floor(x)+1 == 0 ? Math.floor(x)+2 : Math.floor(x)+1, + x => Math.ceil(x)-1 == 0 ? Math.ceil(x)-2 : Math.ceil(x)-1) +Domain.ZE.latexMarkup = "\\mathbb{Z}^{*}" +Domain.ZM = new SpecialDomain('ℤ⁻', x => x%1==0 && x <= 0, + x => Math.min(Math.floor(x)+1, 0), + x => Math.min(Math.ceil(x)-1, 0)) +Domain.ZM.latexMarkup = "\\mathbb{Z}^{-}" +Domain.ZME = new SpecialDomain('ℤ⁻*', x => x%1==0 && x < 0, + x => Math.min(Math.floor(x)+1, -1), + x => Math.min(Math.ceil(x)-1, -1)) +Domain.ZME.latexMarkup = "\\mathbb{Z}^{-*}" +Domain.NLog = new SpecialDomain('ℕˡᵒᵍ', + x => x/Math.pow(10, x.toString().length-1) % 1 == 0 && x > 0, + function(x) { + var x10pow = Math.pow(10, x.toString().length-1) + return Math.max(1, (Math.floor(x/x10pow)+1)*x10pow) + }, + function(x) { + var x10pow = Math.pow(10, x.toString().length-1) + return Math.max(1, (Math.ceil(x/x10pow)-1)*x10pow) + }) +Domain.NLog.latexMarkup = "\\mathbb{N}^{log}" -let refedDomains = [] +var refedDomains = [] /** - * Parses a domain, that can use parentheses. + * Parses a domain, that can use parenthesises. * e.g (N ∪ [-1;0[) ∩ (Z \ {0;3}) * @param {string} domain - string of the domain to be parsed. * @returns {Domain} Parsed domain. */ -export function parseDomain(domain) { - if(!domain.includes(")") && !domain.includes("(")) return parseDomainSimple(domain) - let domStr +function parseDomain(domain) { + if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain) + var domStr while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) { - let dom = parseDomainSimple(domStr[1].trim()) - domain = domain.replace(domStr[0], "D" + refedDomains.length) + var dom = parseDomainSimple(domStr[1].trim()); + domain = domain.replace(domStr[0], 'D' + refedDomains.length) refedDomains.push(dom) } return parseDomainSimple(domain) } /** - * Parses a domain, without parentheses. + * Parses a domain, without parenthesises. * e.g N ∪ [-1;0[, Z \ {0;3}, N+*... * @param {string} domain - string of the domain to be parsed. * @returns {Domain} Parsed domain. */ -export function parseDomainSimple(domain) { +function parseDomainSimple(domain) { domain = domain.trim() if(domain.includes("U") || domain.includes("∪")) return UnionDomain.import(domain) if(domain.includes("∩")) return IntersectionDomain.import(domain) if(domain.includes("∖") || domain.includes("\\")) return MinusDomain.import(domain) - if(domain.at(0) === "{" && domain.at(-1) === "}") return DomainSet.import(domain) + if(domain.charAt(0) == "{" && domain.charAt(domain.length -1) == "}") return DomainSet.import(domain) if(domain.includes("]") || domain.includes("[")) return Range.import(domain) if(["R", "ℝ", "N", "ℕ", "Z", "ℤ"].some(str => domain.toUpperCase().includes(str))) return Domain.import(domain) - if(domain[0] === "D") return refedDomains[parseInt(domain.substring(1))] + if(domain[0] == 'D') return refedDomains[parseInt(domain.substr(1))] return new EmptySet() } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js new file mode 100644 index 0000000..58c4528 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js @@ -0,0 +1,77 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "common.js" as C +.import "latex.js" as Latex +.import "../utils.js" as Utils + +/** + * Represents any kind of x-based or non variable based expression. + */ +class Expression { + constructor(expr) { + this.expr = expr + this.calc = C.parser.parse(expr).simplify() + this.cached = this.isConstant() + this.cachedValue = this.cached ? this.calc.evaluate(C.evalVariables) : null + this.latexMarkup = Latex.expression(this.calc.tokens) + } + + isConstant() { + return !this.expr.includes("x") && !this.expr.includes("n") + } + + execute(x = 1) { + if(this.cached) return this.cachedValue + C.currentVars = Object.assign({'x': x}, C.evalVariables) + return this.calc.evaluate(C.currentVars) + } + + simplify(x) { + var expr = this.calc.substitute('x', x).simplify() + if(expr.evaluate(C.evalVariables) == 0) return '0' + var str = Utils.makeExpressionReadable(expr.toString()); + if(str != undefined && str.match(/^\d*\.\d+$/)) { + if(str.split('.')[1].split('0').length > 7) { + // Likely rounding error + str = parseFloat(str.substring(0, str.length-1)).toString(); + } + } + return str + } + + duplicate() { + return new Expression(this.toEditableString()) + } + + toEditableString() { + return this.calc.toString() + } + + toString(forceSign=false) { + var str = Utils.makeExpressionReadable(this.calc.toString()) + if(str[0] != '-' && forceSign) str = '+' + str + return str + } +} + +function executeExpression(expr){ + return (new Expression(expr.toString())).execute() +} diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.js new file mode 100644 index 0000000..4c57b07 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.js @@ -0,0 +1,260 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "../expr-eval.js" as ExprEval + + +/** + * true if latex has been enabled by the user, false otherwise. + */ +var enabled = false + +/** + * 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 to convert + * @returns {string} + */ +function variable(vari) { + 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"] + for(let i = 0; i < unicodechars.length; i++) { + //console.log(vari, unicodechars[i], equivalchars[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 (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]); +} diff --git a/common/src/math/sequence.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.js similarity index 52% rename from common/src/math/sequence.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.js index 6049e53..cdee74c 100644 --- a/common/src/math/sequence.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.js @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,16 +16,18 @@ * along with this program. If not, see . */ -import * as Expr from "./expression.mjs" -import * as Utils from "../utils/index.mjs" -import Latex from "../module/latex.mjs" -import Objects from "../module/objects.mjs" -import ExprParser from "../module/expreval.mjs" +.pragma library + +.import "common.js" as C +.import "expression.js" as Expr +.import "../utils.js" as Utils +.import "../math/latex.js" as Latex + /** * Represents mathematical object for sequences. */ -export class Sequence extends Expr.Expression { +class Sequence extends Expr.Expression { constructor(name, baseValues = {}, valuePlus = 1, expr = "") { // u[n+valuePlus] = expr super(expr) @@ -33,17 +35,17 @@ export class Sequence extends Expr.Expression { this.baseValues = baseValues this.calcValues = Object.assign({}, baseValues) this.latexValues = Object.assign({}, baseValues) - for(let n in this.calcValues) + for(var n in this.calcValues) if(['string', 'number'].includes(typeof this.calcValues[n])) { - let parsed = ExprParser.parse(this.calcValues[n].toString()).simplify() + let parsed = C.parser.parse(this.calcValues[n].toString()).simplify() this.latexValues[n] = Latex.expression(parsed.tokens) - this.calcValues[n] = parsed.evaluate() + this.calcValues[n] = parsed.evaluate(C.evalVariables) } this.valuePlus = parseInt(valuePlus) } isConstant() { - return this.expr.indexOf("n") === -1 + return this.expr.indexOf("n") == -1 } execute(n = 1) { @@ -54,31 +56,26 @@ export class Sequence extends Expr.Expression { } simplify(n = 1) { - if(!(n in this.calcValues)) - this.cache(n) - return this.calcValues[n].toString() + if(n in this.calcValues) + return Utils.makeExpressionReadable(this.calcValues[n].toString()) + this.cache(n) + return Utils.makeExpressionReadable(this.calcValues[n].toString()) } cache(n = 1) { - let str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString()) - let expr = ExprParser.parse(str).simplify() - // Cache values required for this one. - if(!this.calcValues[n-this.valuePlus] && n-this.valuePlus > 0) - this.cache(n-this.valuePlus) - // Setting current variables - ExprParser.currentVars = Object.assign( - {'n': n-this.valuePlus}, // Just in case, add n (for custom functions) - Objects.currentObjectsByName, - {[this.name]: this.calcValues} - ) - this.calcValues[n] = expr.evaluate(ExprParser.currentVars) + var str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString()) + var expr = C.parser.parse(str).simplify() + var l = {'n': n-this.valuePlus} // Just in case, add n (for custom functions) + l[this.name] = this.calcValues + C.currentVars = Object.assign(l, C.evalVariables) + this.calcValues[n] = expr.evaluate(C.currentVars) } toString(forceSign=false) { - let str = Utils.makeExpressionReadable(this.calc.toString()) - if(str[0] !== '-' && forceSign) str = '+' + str - let subtxt = this.valuePlus === 0 ? 'ₙ' : Utils.textsub('n+' + this.valuePlus) - let ret = `${this.name}${subtxt} = ${str}${this.baseValues.length === 0 ? '' : "\n"}` + var str = Utils.makeExpressionReadable(this.calc.toString()) + if(str[0] != '-' && forceSign) str = '+' + str + var subtxt = this.valuePlus == 0 ? 'ₙ' : Utils.textsub('n+' + this.valuePlus) + var ret = `${this.name}${subtxt} = ${str}${this.baseValues.length == 0 ? '' : "\n"}` ret += Object.keys(this.baseValues).map( n => `${this.name}${Utils.textsub(n)} = ${this.baseValues[n]}` ).join('; ') @@ -86,10 +83,10 @@ export class Sequence extends Expr.Expression { } toLatexString(forceSign=false) { - let str = this.latexMarkup - if(str[0] !== '-' && forceSign) str = '+' + str - let subtxt = '_{n' + (this.valuePlus === 0 ? '' : '+' + this.valuePlus) + '}' - let ret = `\\begin{array}{l}${Latex.variable(this.name)}${subtxt} = ${str}${this.latexValues.length === 0 ? '' : "\n"}\\\\` + var str = this.latexMarkup + if(str[0] != '-' && forceSign) str = '+' + str + var subtxt = '_{n' + (this.valuePlus == 0 ? '' : '+' + this.valuePlus) + '}' + var ret = `\\begin{array}{l}${Latex.variable(this.name)}${subtxt} = ${str}${this.latexValues.length == 0 ? '' : "\n"}\\\\` ret += Object.keys(this.latexValues).map( n => `${this.name}_{${n}} = ${this.latexValues[n]}` ).join('; ') + "\\end{array}" diff --git a/common/src/index.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/mathlib.js similarity index 56% rename from common/src/index.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/mathlib.js index 57efb01..1013f82 100644 --- a/common/src/index.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/mathlib.js @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,13 +16,26 @@ * along with this program. If not, see . */ -import js from "./lib/polyfills/js.mjs" +.pragma library -export * as Utils from "./utils/index.mjs" +.import "math/expression.js" as Expr +.import "math/sequence.js" as Seq -import * as ObjsAutoload from "./objs/autoload.mjs" +.import "math/domain.js" as Dom -export * as Modules from "./module/index.mjs" -export * as MathLib from "./math/index.mjs" -export * as HistoryLib from "./history/index.mjs" -export * as Parsing from "./parsing/index.mjs" +var Expression = Expr.Expression +var executeExpression = Expr.executeExpression +var Sequence = Seq.Sequence + +// Domains +var Domain = Dom.Domain +var EmptySet = Dom.EmptySet +var Range = Dom.Range +var SpecialDomain = Dom.SpecialDomain +var DomainSet = Dom.DomainSet +var UnionDomain = Dom.UnionDomain +var IntersectionDomain = Dom.IntersectionDomain +var MinusDomain = Dom.MinusDomain + +var parseDomain = Dom.parseDomain +var parseDomainSimple = Dom.parseDomainSimple diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.js new file mode 100644 index 0000000..e27562d --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.js @@ -0,0 +1,76 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "utils.js" as Utils +.import "mathlib.js" as MathLib +.import "parameters.js" as P + +var types = {} + +var currentObjects = {} + +function getObjectByName(objName, objType = null) { + var objectTypes = Object.keys(currentObjects) + if(typeof objType == 'string' && objType != "") { + if(objType == "ExecutableObject") { + objectTypes = getExecutableTypes() + } else if(currentObjects[objType] != undefined) { + objectTypes = [objType] + } + } + if(Array.isArray(objType)) objectTypes = objType + var retObj = null + if(objName != "" && objName != null) { + objectTypes.forEach(function(objType){ + if(currentObjects[objType] == undefined) return null + currentObjects[objType].forEach(function(obj){ + if(obj.name == objName) retObj = obj + }) + }) + } + return retObj +} + +function getObjectsName(objType) { + if(objType == "ExecutableObject") { + var types = getExecutableTypes() + var elementNames = [''] + types.forEach(function(elemType){ + 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() { + return Object.keys(currentObjects).filter(objType => types[objType].executable()) +} + +function createNewRegisteredObject(objType, args=[]) { + 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) + return newobj +} diff --git a/common/src/parsing/index.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/autoload.js similarity index 53% rename from common/src/parsing/index.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/autoload.js index f0fde37..5044942 100644 --- a/common/src/parsing/index.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/autoload.js @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,17 +16,27 @@ * along with this program. If not, see . */ -import * as Reference from "./reference.mjs" -import * as T from "./tokenizer.mjs" -import InputExpression from "./common.mjs" +.pragma library -export const Input = InputExpression -export const TokenType = T.TokenType -export const Token = T.Token -export const Tokenizer = T.ExpressionTokenizer +.import "common.js" as C +.import "point.js" as P +.import "text.js" as T +.import "function.js" as F +.import "gainbode.js" as GB +.import "phasebode.js" as PB +.import "sommegainsbode.js" as SGB +.import "sommephasesbode.js" as SPB +.import "xcursor.js" as X +.import "sequence.js" as S +.import "repartition.js" as R -export const FUNCTIONS_LIST = Reference.FUNCTIONS_LIST -export const FUNCTIONS = Reference.FUNCTIONS -export const FUNCTIONS_USAGE = Reference.FUNCTIONS_USAGE -export const CONSTANTS_LIST = Reference.CONSTANTS_LIST -export const CONSTANTS = Reference.CONSTANTS +C.registerObject(P.Point) +C.registerObject(T.Text) +C.registerObject(F.Function) +C.registerObject(GB.GainBode) +C.registerObject(PB.PhaseBode) +C.registerObject(SGB.SommeGainsBode) +C.registerObject(SPB.SommePhasesBode) +C.registerObject(X.XCursor) +C.registerObject(S.Sequence) +C.registerObject(R.RepartitionFunction) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js new file mode 100644 index 0000000..3864b2f --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js @@ -0,0 +1,374 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "../utils.js" as Utils +.import "../objects.js" as Objects +.import "../math/latex.js" as Latex + +// This file contains the default data to be imported from all other objects + +/** + * Creates a new name for an object, based on the \c allowedLetters. + * If variables with each of the allowedLetters is created, a subscript + * number is added to the name. + * @return {string} New unused name for a new object. + */ +function getNewName(allowedLetters) { + // Allows to get a new name, based on the allowed letters, + // as well as adding a sub number when needs be. + var newid = 0 + var ret + do { + var letter = allowedLetters[newid % allowedLetters.length] + var num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length) + ret = letter + (num > 0 ? Utils.textsub(num-1) : '') + newid += 1 + } while(Objects.getObjectByName(ret) != null) + return ret +} + +/** + * Class to extend for every type of object that + * can be drawn on the canvas. + */ +class DrawableObject { + /** + * Base name of the object. Needs to be constant over time. + * @return {string} Type of the object. + */ + static type(){return 'Unknown'} + + /** + * Translated name of the object to be shown to the user. + * @return {string} + */ + static displayType(){return 'Unknown'} + + /** + * Translated name of the object in plural form to be shown to the user. + * @return {string} + */ + static displayTypeMultiple(){return 'Unknowns'} + + /** + * True if this object can be created by the user, false if it can only + * be instantiated by other objects + * @return {bool} + */ + static createable() {return true} + + /** + * List of properties used in the Object Editor. + * + * Properties are set with key as property name and + * value as it's type name (e.g 'Expression', 'string'...), + * an Enum for enumerations, an ObjectType for DrawableObjects + * with a specific type, a List instance for lists, a + * Dictionary instance for dictionaries... + * + * If the property is to be translated, the key should be passed + * through the QT_TRANSLATE_NOOP macro in that form: + * [QT_TRANSLATE_NOOP('prop','key')] + * Enums that are translated should be indexed in parameters.js and + * then be linked directly here. + * + * @return {Dictionary} + */ + static properties() {return {}} + + /** + * True if this object can be executed, so that an y value might be computed + * for an x value. Only ExecutableObjects have that property set to true. + * @return {bool} + */ + static executable() {return false} + + /** + * Base constructor for the object. + * @param {string} name - Name of the object + * @param {bool} visible - true if the object is visible, false otherwise. + * @param {color} color - Color of the object (can be string or QColor) + * @param {Enum} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label. + * @constructor() + */ + constructor(name, visible = true, color = null, labelContent = 'name + value') { + if(color == null) color = Utils.getRandomColor() + this.type = 'Unknown' + this.name = name + this.visible = visible + this.color = color + this.labelContent = labelContent // "null", "name", "name + value" + this.requiredBy = [] + } + + /** + * Serilizes the object in an array that can be JSON serialized. + * These parameters will be re-entered in the constructor when restored. + * @return {array} + */ + export() { + // Should return what will be inputed as arguments when a file is loaded (serializable form) + return [this.name, this.visible, this.color.toString(), this.labelContent] + } + + /** + * String representing the object that will be displayed to the user. + * It allows for 2 lines and translated text, but it shouldn't be too long. + * @return {string} + */ + getReadableString() { + return `${this.name} = Unknown` + } + + /** + * Latex markuped version of the readable string. + * Every non latin character should be passed as latex symbols and formulas + * should be in latex form. + * See ../latex.js for helper methods. + * @return {string} + */ + getLatexString() { + return this.getReadableString() + } + + /** + * Readable string content of the label depending on the value of the \c latexContent. + * @return {string} + */ + getLabel() { + switch(this.labelContent) { + case 'name': + return this.name + case 'name + value': + return this.getReadableString() + case 'null': + return '' + + } + } + + /** + * Latex markup string content of the label depending on the value of the \c latexContent. + * Every non latin character should be passed as latex symbols and formulas + * should be in latex form. + * See ../latex.js for helper methods. + * @return {string} + */ + getLatexLabel() { + switch(this.labelContent) { + case 'name': + return Latex.variable(this.name) + case 'name + value': + return this.getLatexString() + case 'null': + return '' + + } + } + + /** + * Callback method when one of the properties of the object is updated. + */ + update() { + for(var req of this.requiredBy) { + req.update() + } + } + + /** + * Callback method when the object is about to get deleted. + */ + delete() { + for(var toRemove of this.requiredBy) { + toRemove.delete() + Objects.currentObjects[toRemove.type] = Objects.currentObjects[toRemove.type].filter(obj => obj.name != toRemove.name) + } + } + + /** + * Abstract method. Draw the object onto the \c canvas with the 2D context \c ctx. + * @param {Canvas} canvas + * @param {Context2D} ctx + */ + draw(canvas, ctx) {} + + /** + * Applicates a \c drawFunction with two position arguments depending on + * both the \c posX and \c posY of where the label should be displayed, + * and the \c labelPosition which declares the label should be displayed + * relatively to that 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 {Dictionary} size - Size of the label item, containing two properties, "width" and "height" + * @param {number} posX - Component of the marked position on the x-axis + * @param {number} posY - Component of the marked position on the y-axis + * @param {function} drawFunction - Function with two arguments (x, y) that will be called to draw the label + */ + drawPositionDivergence(labelPosition, offset, size, posX, posY, drawFunction) { + switch(labelPosition) { + case 'center': + drawFunction(posX-size.width/2, posY-size.height/2) + break; + case 'top': + case 'above': + drawFunction(posX-size.width/2, posY-size.height-offset) + break; + case 'bottom': + case 'below': + drawFunction(posX-size.width/2, posY+offset) + break; + case 'left': + drawFunction(posX-size.width-offset, posY-size.height/2) + break; + case 'right': + drawFunction(posX+offset, posY-size.height/2) + break; + case 'top-left': + case 'above-left': + drawFunction(posX-size.width, posY-size.height-offset) + break; + case 'top-right': + case 'above-right': + drawFunction(posX+offset, posY-size.height-offset) + break; + case 'bottom-left': + case 'below-left': + drawFunction(posX-size.width-offset, posY+offset) + break; + case 'bottom-right': + case 'below-right': + drawFunction(posX+offset, posY+offset) + break; + } + } + + /** + * Automaticly draw text (by default the label of the object on the \c canvas with + * the 2D context \c ctx depending on user settings. + * This method takes into account both the \c posX and \c posY of where the label + * should be displayed, including the \c labelPosition relative to it. + * The text is get both through the \c getLatexFunction and \c getTextFunction + * depending on whether to use latex. + * Then, it's displayed using the \c drawFunctionLatex (x,y,imageData) and + * \c drawFunctionText (x,y,text) depending on whether to use latex. + * + * @param {Canvas} canvas + * @param {Context2D} ctx + * @param {string|Enum} labelPosition - Position of the label relative to the marked position + * @param {number} posX - Component of the marked position on the x-axis + * @param {number} posY - Component of the marked position on the y-axis + * @param {bool} forceText - Force the rendering of the label as text + * @param {function|null} getLatexFunction - Function (no argument) to get the latex markup to be displayed + * @param {function|null} getTextFunction - Function (no argument) to get the text to be displayed + * @param {function|null} drawFunctionLatex - Function (x,y,imageData) to display the latex image + * @param {function|null} drawFunctionText - Function (x,y,text,textSize) to display the text + */ + drawLabel(canvas, ctx, labelPosition, posX, posY, forceText = false, + getLatexFunction = null, getTextFunction = null, drawFunctionLatex = null, drawFunctionText = null) { + // Default functions + if(getLatexFunction == null) + getLatexFunction = this.getLatexLabel.bind(this) + if(getTextFunction == null) + getTextFunction = this.getLabel.bind(this) + if(drawFunctionLatex == null) + drawFunctionLatex = (x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, y, ltxImg.width, ltxImg.height) + if(drawFunctionText == null) + drawFunctionText = (x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, y+textSize.height) // Positioned from left bottom + // Drawing the label + let offset + if(!forceText && Latex.enabled) { // TODO: Check for user setting with Latex. + // With latex + let drawLblCb = function(canvas, ctx, ltxImg) { + this.drawPositionDivergence(labelPosition, 8+ctx.lineWidth/2, ltxImg, posX, posY, (x,y) => drawFunctionLatex(x,y,ltxImg)) + } + let ltxLabel = getLatexFunction(); + if(ltxLabel != "") + canvas.renderLatexImage(ltxLabel, this.color, drawLblCb.bind(this)) + } else { + // Without latex + let text = getTextFunction() + ctx.font = `${canvas.textsize}px sans-serif` + let textSize = canvas.measureText(ctx, text) + this.drawPositionDivergence(labelPosition, 8+ctx.lineWidth/2, textSize, posX, posY, (x,y) => drawFunctionText(x,y,text,textSize)) + } + } + + toString() { + return this.name; + } +} + +/** + * Class to be extended for every object on which + * an y for a x can be computed with the execute function. + * If a value cannot be found during execute, it will + * return null. However, theses same x values will + * return false when passed to canExecute. + */ +class ExecutableObject extends DrawableObject { + /** + * Returns the corresponding y value for an x value. + * If the object isn't defined on the given x, then + * this function will return null. + * + * @param {number} x + * @returns {number|null} + */ + execute(x = 1) {return 0} + /** + * Returns false if the object isn't defined on the given x, true otherwise. + * + * @param {number} x + * @returns {bool} + */ + canExecute(x = 1) {return true} + /** + * Returns the simplified expression string for a given x. + * + * @param {number} x + * @returns {bool} + */ + simplify(x = 1) {return '0'} + + + /** + * True if this object can be executed, so that an y value might be computed + * for an x value. Only ExecutableObjects have that property set to true. + * @return {bool} + */ + 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.") + } +} + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.js new file mode 100644 index 0000000..44dc1f4 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.js @@ -0,0 +1,165 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "common.js" as Common +.import "../utils.js" as Utils +.import "../mathlib.js" as MathLib +.import "../parameters.js" as P +.import "../math/latex.js" as Latex + + +class Function extends Common.ExecutableObject { + static type(){return 'Function'} + static displayType(){return qsTr('Function')} + static displayTypeMultiple(){return qsTr('Functions')} + static properties() {return { + [QT_TRANSLATE_NOOP('prop','expression')]: 'Expression', + [QT_TRANSLATE_NOOP('prop','definitionDomain')]: 'Domain', + [QT_TRANSLATE_NOOP('prop','destinationDomain')]: 'Domain', + 'comment1': QT_TRANSLATE_NOOP( + 'comment', + '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','labelX')]: 'number', + 'comment2': QT_TRANSLATE_NOOP( + 'comment', + 'The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...)' + ), + [QT_TRANSLATE_NOOP('prop','drawPoints')]: 'boolean', + [QT_TRANSLATE_NOOP('prop','drawDashedLines')]: 'boolean' + }} + + constructor(name = null, visible = true, color = null, labelContent = 'name + value', + expression = 'x', definitionDomain = 'RPE', destinationDomain = 'R', + displayMode = 'application', labelPosition = 'above', labelX = 1, + drawPoints = true, drawDashedLines = true) { + if(name == null) name = Common.getNewName('fghjqlmnopqrstuvwabcde') + super(name, visible, color, labelContent) + this.type = 'Function' + if(typeof expression == 'number' || typeof expression == 'string') expression = new MathLib.Expression(expression.toString()) + this.expression = expression + if(typeof definitionDomain == 'string') definitionDomain = MathLib.parseDomain(definitionDomain) + this.definitionDomain = definitionDomain + if(typeof destinationDomain == 'string') destinationDomain = MathLib.parseDomain(destinationDomain) + this.destinationDomain = destinationDomain + this.displayMode = displayMode + this.labelPosition = labelPosition + this.labelX = labelX + this.drawPoints = drawPoints + this.drawDashedLines = drawDashedLines + } + + getReadableString() { + if(this.displayMode == 'application') { + return `${this.name}: ${this.definitionDomain} ⟶ ${this.destinationDomain}\n ${' '.repeat(this.name.length)}x ⟼ ${this.expression.toString()}` + } else { + return `${this.name}(x) = ${this.expression.toString()}\nD${Utils.textsub(this.name)} = ${this.definitionDomain}` + } + } + + getLatexString() { + if(this.displayMode == 'application') { + return `${Latex.variable(this.name)}:\\begin{array}{llll}${this.definitionDomain.latexMarkup}\\textrm{ } & \\rightarrow & \\textrm{ }${this.destinationDomain.latexMarkup}\\\\ + x\\textrm{ } & \\mapsto & \\textrm{ }${this.expression.latexMarkup}\\end{array}` + } else { + 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) { + if(this.definitionDomain.includes(x)) + return this.expression.execute(x) + return null + } + + canExecute(x = 1) { + return this.definitionDomain.includes(x) + } + + simplify(x = 1) { + if(this.definitionDomain.includes(x)) + return this.expression.simplify(x) + return '' + } + + draw(canvas, ctx) { + Function.drawFunction(canvas, ctx, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines) + // Label + this.drawLabel(canvas, ctx, 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. + // Drawing small traits every 0.2px + var pxprecision = 1 + var previousX = canvas.px2x(0) + var previousY; + if(definitionDomain instanceof MathLib.SpecialDomain && definitionDomain.moveSupported) { + // Point based functions. + previousX = definitionDomain.next(previousX) + if(previousX === null) previousX = definitionDomain.next(canvas.px2x(0)) + previousY = expr.execute(previousX) + if(!drawPoints && !drawDash) return + while(previousX !== null && canvas.x2px(previousX) < canvas.canvasSize.width) { + // Reconverted for canvas to fix for logarithmic scales. + var currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX)+pxprecision)); + var currentY = expr.execute(currentX) + if(currentX === null) break; + if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) && + (destinationDomain.includes(currentY) || destinationDomain.includes(previousY))) { + if(drawDash) + canvas.drawDashedLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY)) + if(drawPoints) { + ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2) + ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10) + } + } + previousX = currentX + previousY = currentY + } + if(drawPoints) { + // Drawing the last cross + ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2) + ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10) + } + } else { + previousY = expr.execute(previousX) + for(var px = pxprecision; px < canvas.canvasSize.width; px += pxprecision) { + var currentX = canvas.px2x(px) + var currentY = expr.execute(currentX) + if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) && + (destinationDomain.includes(currentY) || destinationDomain.includes(previousY)) && + Math.abs(previousY-currentY)<100) { + canvas.drawLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY)) + } + previousX = currentX + previousY = currentY + } + } + } +} diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/gainbode.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/gainbode.js new file mode 100644 index 0000000..ff8236c --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/gainbode.js @@ -0,0 +1,149 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "common.js" as Common +.import "function.js" as F +.import "../objects.js" as Objects +.import "../utils.js" as Utils +.import "../mathlib.js" as MathLib +.import "../historylib.js" as HistoryLib +.import "../parameters.js" as P +.import "../math/latex.js" as Latex + + +class GainBode extends Common.ExecutableObject { + static type(){return 'Gain Bode'} + static displayType(){return qsTr('Bode Magnitude')} + static displayTypeMultiple(){return qsTr('Bode Magnitudes')} + static properties() {return { + [QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'), + [QT_TRANSLATE_NOOP('prop','pass')]: P.Enum.BodePass, + [QT_TRANSLATE_NOOP('prop','gain')]: 'Expression', + [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, + [QT_TRANSLATE_NOOP('prop','labelX')]: 'number', + [QT_TRANSLATE_NOOP('prop','omGraduation')]: 'boolean' + }} + + constructor(name = null, visible = true, color = null, labelContent = 'name + value', + om_0 = '', pass = 'high', gain = '20', labelPosition = 'above', labelX = 1, omGraduation = false) { + if(name == null) name = Common.getNewName('G') + if(name == 'G') name = 'G₀' // G is reserved for sum of BODE magnitudes (Somme gains Bode). + super(name, visible, color, labelContent) + this.type = 'Gain Bode' + if(typeof om_0 == "string") { + // Point name or create one + om_0 = Objects.getObjectByName(om_0, 'Point') + if(om_0 == null) { + // Create new point + om_0 = Objects.createNewRegisteredObject('Point') + om_0.name = Common.getNewName('ω') + om_0.labelContent = 'name' + om_0.color = this.color + HistoryLib.history.addToHistory(new HistoryLib.CreateNewObject(om_0.name, 'Point', om_0.export())) + labelPosition = 'below' + } + om_0.requiredBy.push(this) + } + this.om_0 = om_0 + this.pass = pass + if(typeof gain == 'number' || typeof gain == 'string') gain = new MathLib.Expression(gain.toString()) + this.gain = gain + this.labelPosition = labelPosition + this.labelX = labelX + this.omGraduation = omGraduation + } + + getReadableString() { + let pass = this.pass == "low" ? qsTr("low-pass") : qsTr("high-pass"); + return `${this.name}: ${pass}; ${this.om_0.name} = ${this.om_0.x}\n ${' '.repeat(this.name.length)}${this.gain.toString(true)} dB/dec` + } + + getLatexString() { + let pass = this.pass == "low" ? qsTr("low-pass") : qsTr("high-pass"); + return `\\mathrm{${Latex.variable(this.name)}:}\\begin{array}{l} + \\textsf{${pass}};${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup} \\\\ + ${this.gain.latexMarkup}\\textsf{ dB/dec} + \\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) { + if(typeof x == 'string') x = MathLib.executeExpression(x) + if((this.pass == 'high' && x < this.om_0.x) || (this.pass == 'low' && x > this.om_0.x)) { + var dbfn = new MathLib.Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`) + return dbfn.execute(x) + } else { + return this.om_0.y.execute() + } + } + + simplify(x = 1) { + var xval = x + if(typeof x == 'string') xval = MathLib.executeExpression(x) + if((this.pass == 'high' && xval < this.om_0.x) || (this.pass == 'low' && xval > this.om_0.x)) { + var dbfn = new MathLib.Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`) + return dbfn.simplify(x) + } else { + return this.om_0.y.toString() + } + } + + canExecute(x = 1) { + return true + } + + draw(canvas, ctx) { + var base = [canvas.x2px(this.om_0.x), canvas.y2px(this.om_0.y)] + var dbfn = new MathLib.Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`) + var inDrawDom = new MathLib.EmptySet() + + if(this.pass == 'high') { + // High pass, linear line from begining, then constant to the end. + canvas.drawLine(ctx, base[0], base[1], canvas.canvasSize.width, base[1]) + inDrawDom = MathLib.parseDomain(`]-inf;${this.om_0.x}[`) + } else { + // Low pass, constant from the beginning, linear line to the end. + canvas.drawLine(ctx, base[0], base[1], 0, base[1]) + inDrawDom = MathLib.parseDomain(`]${this.om_0.x};+inf[`) + } + F.Function.drawFunction(canvas, ctx, dbfn, inDrawDom, MathLib.Domain.R) + // Dashed line representing break in function + var xpos = canvas.x2px(this.om_0.x.execute()) + var dashPxSize = 10 + for(var i = 0; i < canvas.canvasSize.height && this.omGraduation; i += dashPxSize*2) + canvas.drawLine(ctx, xpos, i, xpos, i+dashPxSize) + + // Label + this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) + } + + update() { + super.update() + if(Objects.currentObjects['Somme gains Bode'] != undefined && Objects.currentObjects['Somme gains Bode'].length > 0) { + Objects.currentObjects['Somme gains Bode'][0].recalculateCache() + } else { + Objects.createNewRegisteredObject('Somme gains Bode') + } + } +} diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/phasebode.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/phasebode.js new file mode 100644 index 0000000..e3d7be0 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/phasebode.js @@ -0,0 +1,132 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "common.js" as Common +.import "../objects.js" as Objects +.import "../mathlib.js" as MathLib +.import "../historylib.js" as HistoryLib +.import "../parameters.js" as P +.import "../math/latex.js" as Latex + + +class PhaseBode extends Common.ExecutableObject { + static type(){return 'Phase Bode'} + static displayType(){return qsTr('Bode Phase')} + static displayTypeMultiple(){return qsTr('Bode Phases')} + static properties() {return { + [QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'), + [QT_TRANSLATE_NOOP('prop','phase')]: 'Expression', + [QT_TRANSLATE_NOOP('prop','unit')]: new P.Enum('°', 'deg', 'rad'), + [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, + [QT_TRANSLATE_NOOP('prop','labelX')]: 'number' + }} + + constructor(name = null, visible = true, color = null, labelContent = 'name + value', + om_0 = '', phase = 90, unit = '°', labelPosition = 'above', labelX = 1) { + if(name == null) name = Common.getNewName('φ') + if(name == 'φ') name = 'φ₀' // φ is reserved for sum of BODE phases (Somme phases Bode). + super(name, visible, color, labelContent) + this.type = 'Phase Bode' + if(typeof phase == 'number' || typeof phase == 'string') phase = new MathLib.Expression(phase.toString()) + this.phase = phase + if(typeof om_0 == "string") { + // Point name or create one + om_0 = Objects.getObjectByName(om_0, 'Point') + if(om_0 == null) { + // Create new point + om_0 = Objects.createNewRegisteredObject('Point') + om_0.name = Common.getNewName('ω') + om_0.color = this.color + om_0.labelContent = 'name' + om_0.labelPosition = this.phase.execute() >= 0 ? 'above' : 'below' + HistoryLib.history.addToHistory(new HistoryLib.CreateNewObject(om_0.name, 'Point', om_0.export())) + labelPosition = 'below' + } + om_0.requiredBy.push(this) + } + this.om_0 = om_0 + this.unit = unit + this.labelPosition = labelPosition + this.labelX = labelX + } + + export() { + return [this.name, this.visible, this.color.toString(), this.labelContent, + this.om_0.name, this.phase.toEditableString(), this.unit, this.labelPosition, this.labelX] + } + + getReadableString() { + return `${this.name}: ${this.phase.toString(true)}${this.unit} at ${this.om_0.name} = ${this.om_0.x}` + } + + getLatexString() { + return `${Latex.variable(this.name)}: ${this.phase.latexMarkup}\\textsf{${this.unit} at }${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup}` + } + + execute(x=1) { + if(typeof x == 'string') x = MathLib.executeExpression(x) + if(x < this.om_0.x) { + return this.om_0.y.execute() + } else { + return this.om_0.y.execute() + this.phase.execute() + } + } + + simplify(x = 1) { + var xval = x + if(typeof x == 'string') xval = MathLib.executeExpression(x) + if(xval < this.om_0.x) { + return this.om_0.y.toString() + } else { + var newExp = this.om_0.y.toEditableString() + ' + ' + this.phase.toEditableString() + return (new MathLib.Expression(newExp)).toString() + } + } + + canExecute(x = 1) { + return true + } + + draw(canvas, ctx) { + var baseX = canvas.x2px(this.om_0.x.execute()) + var omy = this.om_0.y.execute() + var augmt = this.phase.execute() + var baseY = canvas.y2px(omy) + var augmtY = canvas.y2px(omy+augmt) + // Before change line. + canvas.drawLine(ctx, 0, baseY, Math.min(baseX, canvas.canvasSize.height), baseY) + // Transition line. + canvas.drawLine(ctx, baseX, baseY, baseX, augmtY) + // After change line + canvas.drawLine(ctx, Math.max(0, baseX), augmtY, canvas.canvasSize.width, augmtY) + + // Label + this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) + } + + update() { + if(Objects.currentObjects['Somme phases Bode'] != undefined && Objects.currentObjects['Somme phases Bode'].length > 0) { + Objects.currentObjects['Somme phases Bode'][0].recalculateCache() + } else { + Objects.createNewRegisteredObject('Somme phases Bode') + } + } +} + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/point.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/point.js new file mode 100644 index 0000000..66ba301 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/point.js @@ -0,0 +1,84 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "common.js" as Common +.import "../mathlib.js" as MathLib +.import "../parameters.js" as P +.import "../math/latex.js" as Latex + + +class Point extends Common.DrawableObject { + static type(){return 'Point'} + static displayType(){return qsTr('Point')} + static displayTypeMultiple(){return qsTr('Points')} + + static properties() {return { + [QT_TRANSLATE_NOOP('prop','x')]: 'Expression', + [QT_TRANSLATE_NOOP('prop','y')]: 'Expression', + [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, + [QT_TRANSLATE_NOOP('prop','pointStyle')]: new P.Enum('●', '✕', '+') + }} + + constructor(name = null, visible = true, color = null, labelContent = 'name + value', + x = 1, y = 0, labelPosition = 'above', pointStyle = '●') { + if(name == null) name = Common.getNewName('ABCDEFJKLMNOPQRSTUVW') + super(name, visible, color, labelContent) + this.type = 'Point' + if(typeof x == 'number' || typeof x == 'string') x = new MathLib.Expression(x.toString()) + this.x = x + if(typeof y == 'number' || typeof y == 'string') y = new MathLib.Expression(y.toString()) + this.y = y + this.labelPosition = labelPosition + this.pointStyle = pointStyle + } + + getReadableString() { + return `${this.name} = (${this.x}, ${this.y})` + } + + getLatexString() { + return `${Latex.variable(this.name)} = \\left(${this.x.latexMarkup}, ${this.y.latexMarkup}\\right)` + } + + export() { + return [this.name, this.visible, this.color.toString(), this.labelContent, this.x.toEditableString(), this.y.toEditableString(), this.labelPosition, this.pointStyle] + } + + draw(canvas, ctx) { + var [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())] + var pointSize = 8+(ctx.lineWidth*2) + switch(this.pointStyle) { + case '●': + ctx.beginPath(); + ctx.ellipse(canvasX-pointSize/2, canvasY-pointSize/2, pointSize, pointSize) + ctx.fill(); + break; + case '✕': + canvas.drawLine(ctx, canvasX-pointSize/2, canvasY-pointSize/2, canvasX+pointSize/2, canvasY+pointSize/2) + canvas.drawLine(ctx, canvasX-pointSize/2, canvasY+pointSize/2, canvasX+pointSize/2, canvasY-pointSize/2) + break; + case '+': + ctx.fillRect(canvasX-pointSize/2, canvasY-1, pointSize, 2) + ctx.fillRect(canvasX-1, canvasY-pointSize/2, 2, pointSize) + break; + } + this.drawLabel(canvas, ctx, this.labelPosition, canvasX, canvasY) + } +} diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.js new file mode 100644 index 0000000..da8c4e1 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.js @@ -0,0 +1,159 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "common.js" as Common +.import "../parameters.js" as P +.import "../math/latex.js" as Latex + + +class RepartitionFunction extends Common.ExecutableObject { + static type(){return 'Repartition'} + static displayType(){return qsTr('Repartition')} + static displayTypeMultiple(){return qsTr('Repartition functions')} + /*static properties() {return { + 'beginIncluded': 'boolean', + 'drawLineEnds': 'boolean', + 'comment1': 'Note: Specify the properties for each potential result.', + 'probabilities': new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d\.,]+$/, 'P({name} = ', ') = '), + 'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'), + 'labelX': 'number' + }}*/ + static properties() {return { + [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, + [QT_TRANSLATE_NOOP('prop','labelX')]: 'number', + 'comment1': QT_TRANSLATE_NOOP( + 'comment', + 'Note: Specify the probability for each value.' + ), + [QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d\.,]+$/, 'P({name} = ', ') = '), + }} + + constructor(name = null, visible = true, color = null, labelContent = 'name + value', + beginIncluded = true, drawLineEnds = true, probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) { + if(name == null) name = Common.getNewName('XYZUVW') + super(name, visible, color, labelContent) + this.beginIncluded = beginIncluded + this.drawLineEnds = drawLineEnds + this.probabilities = probabilities + this.labelPosition = labelPosition + this.labelX = labelX + this.update() + } + + export() { + return [this.name, this.visible, this.color.toString(), this.labelContent, + this.beginIncluded, this.drawLineEnds, this.probabilities, this.labelPosition, this.labelX] + } + + + getReadableString() { + var keys = Object.keys(this.probabilities).sort((a,b) => a-b); + return `F_${this.name}(x) = P(${this.name} ≤ x)\n` + keys.map(idx => `P(${this.name}=${idx})=${this.probabilities[idx]}`).join("; ") + } + + getLatexString() { + let keys = Object.keys(this.probabilities).sort((a,b) => a-b); + let varName = Latex.variable(this.name) + return `\\begin{array}{l}F_{${varName}}(x) = P(${varName} \\le x)\\\\` + keys.map(idx => `P(${varName}=${idx})=${this.probabilities[idx]}`).join("; ") + '\\end{array}' + } + + execute(x = 1) { + var ret = 0; + Object.keys(this.probabilities).sort((a,b) => a-b).forEach(idx => { + if(x >= idx) ret += parseFloat(this.probabilities[idx].replace(/,/g, '.')) + }) + return ret + } + + canExecute(x = 1) {return true} + // Simplify returns the simplified string of the expression. + simplify(x = 1) { + return this.execute(x) + } + + getLabel() { + switch(this.labelContent) { + case 'name': + return `P(${this.name} ≤ x)` + case 'name + value': + return this.getReadableString() + case 'null': + return '' + } + } + + draw(canvas, ctx) { + var currentY = 0; + var keys = Object.keys(this.probabilities).map(idx => parseInt(idx)).sort((a,b) => a-b) + if(canvas.visible(keys[0],this.probabilities[keys[0]].replace(/,/g, '.'))) { + canvas.drawLine(ctx, + 0, + canvas.y2px(0), + canvas.x2px(keys[0]), + canvas.y2px(0) + ) + if(canvas.visible(keys[0],0)) { + ctx.beginPath(); + ctx.arc(canvas.x2px(keys[0])+4,canvas.y2px(0), 4, Math.PI / 2, 3 * Math.PI / 2); + ctx.stroke(); + } + } + for(var i = 0; i < keys.length-1; i++) { + var idx = keys[i]; + currentY += parseFloat(this.probabilities[idx].replace(/,/g, '.')); + if(canvas.visible(idx,currentY) || canvas.visible(keys[i+1],currentY)) { + canvas.drawLine(ctx, + Math.max(0,canvas.x2px(idx)), + canvas.y2px(currentY), + Math.min(canvas.canvasSize.width,canvas.x2px(keys[i+1])), + canvas.y2px(currentY) + ) + if(canvas.visible(idx,currentY)) { + ctx.beginPath(); + ctx.arc(canvas.x2px(idx),canvas.y2px(currentY), 4, 0, 2 * Math.PI); + ctx.fill(); + } + if(canvas.visible(keys[i+1],currentY)) { + ctx.beginPath(); + ctx.arc(canvas.x2px(keys[i+1])+4,canvas.y2px(currentY), 4, Math.PI / 2, 3 * Math.PI / 2); + ctx.stroke(); + } + } + } + if(canvas.visible(keys[keys.length-1],currentY+parseFloat(this.probabilities[keys[keys.length-1]]))) { + canvas.drawLine(ctx, + Math.max(0,canvas.x2px(keys[keys.length-1])), + canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))), + canvas.canvasSize.width, + canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))) + ) + ctx.beginPath(); + ctx.arc( + canvas.x2px(keys[keys.length-1]), + canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))), + 4, 0, 2 * Math.PI); + ctx.fill(); + } + + // Label + this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) + } +} + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sequence.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sequence.js new file mode 100644 index 0000000..484b7a4 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sequence.js @@ -0,0 +1,131 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "common.js" as Common +.import "function.js" as F +.import "../mathlib.js" as MathLib +.import "../parameters.js" as P +.import "../math/latex.js" as Latex + + +class Sequence extends Common.ExecutableObject { + static type(){return 'Sequence'} + static displayType(){return qsTr('Sequence')} + static displayTypeMultiple(){return qsTr('Sequences')} + + static properties() {return { + [QT_TRANSLATE_NOOP('prop','drawPoints')]: 'boolean', + [QT_TRANSLATE_NOOP('prop','drawDashedLines')]: 'boolean', + [QT_TRANSLATE_NOOP('prop','defaultExpression')]: new P.Dictionary('string', 'int', /^.+$/, /^\d+$/, '{name}[n+', '] = ', true), + 'comment1': QT_TRANSLATE_NOOP( + 'comment', + 'Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁...' + ), + [QT_TRANSLATE_NOOP('prop','baseValues')]: new P.Dictionary('string', 'int', /^.+$/, /^\d+$/, '{name}[', '] = '), + [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, + [QT_TRANSLATE_NOOP('prop','labelX')]: 'number', + }} + + constructor(name = null, visible = true, color = null, labelContent = 'name + value', + drawPoints = true, drawDashedLines = true, defaultExp = {1: "n"}, + baseValues = {0: 0}, labelPosition = 'above', labelX = 1) { + if(name == null) name = Common.getNewName('uvwPSUVWabcde') + super(name, visible, color, labelContent) + this.drawPoints = drawPoints + this.drawDashedLines = drawDashedLines + this.defaultExpression = defaultExp + this.baseValues = baseValues + this.labelPosition = labelPosition + this.labelX = labelX + this.update() + } + + export() { + return [this.name, this.visible, this.color.toString(), this.labelContent, + this.drawPoints, this.drawDashedLines, this.defaultExpression, this.baseValues, this.labelPosition, this.labelX] + } + + update() { + super.update() + if( + this.sequence == null || this.baseValues != this.sequence.baseValues || + this.sequence.name != this.name || + this.sequence.expr != Object.values(this.defaultExpression)[0] || + this.sequence.valuePlus != Object.keys(this.defaultExpression)[0] + ) + this.sequence = new MathLib.Sequence( + this.name, this.baseValues, + Object.keys(this.defaultExpression)[0], + Object.values(this.defaultExpression)[0] + ) + } + + getReadableString() { + return this.sequence.toString() + } + + getLatexString() { + return this.sequence.toLatexString() + } + + execute(x = 1) { + if(x % 1 == 0) + return this.sequence.execute(x) + return null + } + canExecute(x = 1) {return x%1 == 0} + + // Simplify returns the simplified string of the expression. + simplify(x = 1) { + if(x % 1 == 0) + return this.sequence.simplify(x) + return null + } + + getLabel() { + switch(this.labelContent) { + case 'name': + return `(${this.name}ₙ)` + case 'name + value': + return this.getReadableString() + case 'null': + return '' + } + } + + getLatexLabel() { + switch(this.labelContent) { + case 'name': + return `(${Latex.variable(this.name)}_n)` + case 'name + value': + return this.getLatexString() + case 'null': + return '' + } + } + + draw(canvas, ctx) { + F.Function.drawFunction(canvas, ctx, this.sequence, canvas.logscalex ? MathLib.Domain.NE : MathLib.Domain.N, MathLib.Domain.R, this.drawPoints, this.drawDashedLines) + + // Label + this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) + } +} + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommegainsbode.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommegainsbode.js new file mode 100644 index 0000000..8f5854c --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommegainsbode.js @@ -0,0 +1,145 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "common.js" as Common +.import "function.js" as F +.import "../objects.js" as Objects +.import "../mathlib.js" as MathLib +.import "../parameters.js" as P +.import "../math/latex.js" as Latex + + +class SommeGainsBode extends Common.DrawableObject { + static type(){return 'Somme gains Bode'} + static displayType(){return qsTr('Bode Magnitudes Sum')} + static displayTypeMultiple(){return qsTr('Bode Magnitudes Sum')} + static createable() {return false} + static properties() {return { + [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, + [QT_TRANSLATE_NOOP('prop','labelX')]: 'number', + }} + + constructor(name = null, visible = true, color = null, labelContent = 'name + value', + labelPosition = 'above', labelX = 1) { + if(name == null) name = 'G' + super(name, visible, color, labelContent) + this.labelPosition = labelPosition + this.labelX = labelX + this.recalculateCache() + } + + export() { + return [this.name, this.visible, this.color.toString(), this.labelContent, this.labelPosition, this.labelX] + } + + getReadableString() { + return `${this.name} = ${Objects.getObjectsName('Gain Bode').join(' + ')}` + } + + getLatexString() { + return `${Latex.variable(this.name)} = ${Objects.getObjectsName('Gain Bode').map(Latex.variable).join(' + ')}` + } + + execute(x = 0) { + for(var [dbfn, inDrawDom] of this.cachedParts) { + if(inDrawDom.includes(x)) { + return dbfn.execute(x) + } + } + return null + } + + canExecute(x = 1) { + return true // Should always be true + } + + simplify(x = 1) { + for(var [dbfn, inDrawDom] of this.cachedParts) { + if(inDrawDom.includes(x)) { + return dbfn.simplify(x) + } + } + return '' + } + + recalculateCache() { + this.cachedParts = [] + // Calculating this is fairly resource expansive so it's cached. + if(Objects.currentObjects['Gain Bode'] != undefined) { + console.log('Recalculating cache gain') + // Minimum to draw (can be expended if needed, just not infinite or it'll cause issues. + var drawMin = 0.001 + + var baseY = 0 + var om0xGains = {1000000000: 0} // To draw the last part + var om0xPass = {1000000000: 'high'} // To draw the last part + Objects.currentObjects['Gain Bode'].forEach(function(gainObj) { // Sorting by their om_0 position. + var om0x = gainObj.om_0.x.execute() + if(om0xGains[om0x] == undefined) { + om0xGains[om0x] = gainObj.gain.execute() + om0xPass[om0x] = gainObj.pass == 'high' + } else { + om0xGains[om0x+0.001] = gainObj.gain.execute() + om0xPass[om0x+0.001] = gainObj.pass == 'high' + } + baseY += gainObj.execute(drawMin) + }) + // Sorting the om_0x + var om0xList = Object.keys(om0xGains).map(x => parseFloat(x)) // THEY WERE CONVERTED TO STRINGS... + om0xList.sort((a,b) => a - b) + // Adding the total gains. + var gainsBeforeP = [] + var gainsAfterP = [] + var gainTotal = 0 + for(var om0x of om0xList){ + if(om0xPass[om0x]) { // High-pass + gainsBeforeP.push(om0xGains[om0x]) + gainsAfterP.push(0) + gainTotal += om0xGains[om0x] // Gain at first + } else { + gainsBeforeP.push(0) + gainsAfterP.push(om0xGains[om0x]) + } + } + // Calculating parts + var previousPallier = drawMin + for(var pallier = 0; pallier < om0xList.length; pallier++) { + var dbfn = new MathLib.Expression(`${gainTotal}*(ln(x)-ln(${previousPallier}))/ln(10)+${baseY}`) + var inDrawDom = MathLib.parseDomain(`]${previousPallier};${om0xList[pallier]}]`) + this.cachedParts.push([dbfn, inDrawDom]) + previousPallier = om0xList[pallier] + baseY = dbfn.execute(om0xList[pallier]) + gainTotal += gainsAfterP[pallier] - gainsBeforeP[pallier] + } + } + } + + draw(canvas, ctx) { + if(this.cachedParts.length > 0) { + for(var [dbfn, inDrawDom] of this.cachedParts) { + F.Function.drawFunction(canvas, ctx, dbfn, inDrawDom, MathLib.Domain.R) + if(inDrawDom.includes(this.labelX)) { + // Label + this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) + } + } + } + } +} diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommephasesbode.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommephasesbode.js new file mode 100644 index 0000000..bfa6c4b --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommephasesbode.js @@ -0,0 +1,133 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "common.js" as Common +.import "../objects.js" as Objects +.import "../mathlib.js" as MathLib +.import "../parameters.js" as P +.import "../math/latex.js" as Latex + + +class SommePhasesBode extends Common.ExecutableObject { + static type(){return 'Somme phases Bode'} + static displayType(){return qsTr('Bode Phases Sum')} + static displayTypeMultiple(){return qsTr('Bode Phases Sum')} + static createable() {return false} + /*static properties() {return { + 'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'), + 'labelX': 'number' + }}*/ + static properties() {return { + [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, + [QT_TRANSLATE_NOOP('prop','labelX')]: 'number', + }} + + constructor(name = null, visible = true, color = null, labelContent = 'name + value', + labelPosition = 'above', labelX = 1) { + if(name == null) name = 'φ' + super(name, visible, color, labelContent) + this.labelPosition = labelPosition + this.labelX = labelX + this.recalculateCache() + } + + export() { + return [this.name, this.visible, this.color.toString(), this.labelContent, this.labelPosition, this.labelX] + } + + getReadableString() { + return `${this.name} = ${Objects.getObjectsName('Phase Bode').join(' + ')}` + } + + getLatexString() { + return `${Latex.variable(this.name)} = ${Objects.getObjectsName('Phase Bode').map(Latex.variable).join(' + ')}` + } + + execute(x=1) { + if(typeof x == 'string') x = MathLib.executeExpression(x) + for(var i = 0; i < this.om0xList.length-1; i++) { + if(x >= this.om0xList[i] && x < this.om0xList[i+1]) return this.phasesList[i] + } + } + + simplify(x = 1) { + var xval = x + if(typeof x == 'string') xval = MathLib.executeExpression(x) + for(var i = 0; i < this.om0xList.length-1; i++) { + if(xval >= this.om0xList[i] && xval < this.om0xList[i+1]) { + return (new MathLib.Expression(this.phasesExprList[i])).simplify() + } + } + return '0' + } + + canExecute(x = 1) { + return true + } + + recalculateCache() { + // Minimum to draw (can be expended if needed, just not infinite or it'll cause issues. + var drawMin = 0.001 + var drawMax = 100000 + this.om0xList = [drawMin, drawMax] + this.phasesList = [0] + this.phasesExprList = ['0'] + var phasesDict = {} + var phasesExprDict = {} + phasesDict[drawMax] = 0 + + if(Objects.currentObjects['Phase Bode'] != undefined) { + console.log('Recalculating cache phase') + for(var obj of Objects.currentObjects['Phase Bode']) { + this.om0xList.push(obj.om_0.x.execute()) + if(phasesDict[obj.om_0.x.execute()] == undefined) { + phasesDict[obj.om_0.x.execute()] = obj.phase.execute() + phasesExprDict[obj.om_0.x.execute()] = obj.phase.toEditableString() + } else { + phasesDict[obj.om_0.x.execute()] += obj.phase.execute() + phasesExprDict[obj.om_0.x.execute()] += '+' + obj.phase.toEditableString() + } + this.phasesList[0] += obj.om_0.y.execute() + this.phasesExprList[0] += '+' + obj.om_0.y.toEditableString() + } + this.om0xList.sort((a,b) => a - b) + var totalAdded = this.phasesList[0] + for(var i = 1; i < this.om0xList.length; i++) { + this.phasesList[i] = this.phasesList[i-1] + phasesDict[this.om0xList[i]] + this.phasesExprList[i] = this.phasesExprList[i-1] + '+' + phasesDict[this.om0xList[i]] + } + } + } + + draw(canvas, ctx) { + for(var i = 0; i < this.om0xList.length-1; i++) { + var om0xBegin = canvas.x2px(this.om0xList[i]) + var om0xEnd = canvas.x2px(this.om0xList[i+1]) + var baseY = canvas.y2px(this.phasesList[i]) + var nextY = canvas.y2px(this.phasesList[i+1]) + canvas.drawLine(ctx, om0xBegin, baseY, om0xEnd, baseY) + canvas.drawLine(ctx, om0xEnd, baseY, om0xEnd, nextY) + } + + // Label + this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) + } +} + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.js new file mode 100644 index 0000000..a46e840 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.js @@ -0,0 +1,102 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "common.js" as Common +.import "../mathlib.js" as MathLib +.import "../parameters.js" as P +.import "../math/latex.js" as Latex + + + +class Text extends Common.DrawableObject { + static type(){return 'Text'} + static displayType(){return qsTr('Text')} + static displayTypeMultiple(){return qsTr('Texts')} + /*static properties() {return { + 'x': 'Expression', + 'y': 'Expression', + 'labelPosition': new P.Enum('center', 'above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'), + 'text': 'string', + }}*/ + static properties() {return { + [QT_TRANSLATE_NOOP('prop','x')]: 'Expression', + [QT_TRANSLATE_NOOP('prop','y')]: 'Expression', + [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning, + [QT_TRANSLATE_NOOP('prop','text')]: 'string', + 'comment1': QT_TRANSLATE_NOOP( + 'comment', + 'If you have latex enabled, you can use use latex markup in between $$ to create equations.' + ), + [QT_TRANSLATE_NOOP('prop','disableLatex')]: 'boolean' + }} + + constructor(name = null, visible = true, color = null, labelContent = 'null', + x = 1, y = 0, labelPosition = 'center', text = 'New text', disableLatex = false) { + if(name == null) name = Common.getNewName('t') + super(name, visible, color, labelContent) + this.type = 'Point' + if(typeof x == 'number' || typeof x == 'string') x = new MathLib.Expression(x.toString()) + this.x = x + if(typeof y == 'number' || typeof y == 'string') y = new MathLib.Expression(y.toString()) + this.y = y + this.labelPosition = labelPosition + this.text = text + this.disableLatex = disableLatex + } + + getReadableString() { + return `${this.name} = "${this.text}"` + } + + latexMarkupText() { + let txt = Latex.variable(this.text) + let i + for(i = 0; txt.includes('$$'); i++) + if(i & 0x01) // Every odd number + txt = txt.replace('$$', '\\textsf{') + else + txt = txt.replace('$$', '}') + if(i & 0x01) // Finished by a } + txt += "{" + return txt + } + + getLatexString() { + 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] + } + + getLabel() { + return this.text + } + + getLatexLabel() { + return `\\textsf{${this.latexMarkupText()}}` + } + + draw(canvas, ctx) { + 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) + } +} + diff --git a/common/src/objs/xcursor.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/xcursor.js similarity index 63% rename from common/src/objs/xcursor.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/xcursor.js index 63fd182..52c989f 100644 --- a/common/src/objs/xcursor.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/xcursor.js @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,24 +16,26 @@ * along with this program. If not, see . */ -import { Expression } from "../math/index.mjs" -import * as P from "../parameters.mjs" -import Latex from "../module/latex.mjs" -import Objects from "../module/objects.mjs" +.pragma library -import { DrawableObject } from "./common.mjs" +.import "common.js" as Common +.import "../objects.js" as Objects +.import "../mathlib.js" as MathLib +.import "../parameters.js" as P +.import "../math/latex.js" as Latex -export default class XCursor extends DrawableObject { + +class XCursor extends Common.DrawableObject { static type(){return 'X Cursor'} - static displayType(){return qsTranslate("xcursor", 'X Cursor')} - static displayTypeMultiple(){return qsTranslate("xcursor", 'X Cursors')} + static displayType(){return qsTr('X Cursor')} + static displayTypeMultiple(){return qsTr('X Cursors')} static properties() {return { - [QT_TRANSLATE_NOOP('prop','x')]: new P.Expression(), - [QT_TRANSLATE_NOOP('prop','targetElement')]: new P.ObjectType('ExecutableObject', true), + [QT_TRANSLATE_NOOP('prop','x')]: 'Expression', + [QT_TRANSLATE_NOOP('prop','targetElement')]: new P.ObjectType('ExecutableObject'), [QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position, [QT_TRANSLATE_NOOP('prop','approximate')]: 'boolean', - [QT_TRANSLATE_NOOP('prop','rounding')]: 'int', + [QT_TRANSLATE_NOOP('prop','rounding')]: 'number', [QT_TRANSLATE_NOOP('prop','displayStyle')]: new P.Enum( '— — — — — — —', '⸺⸺⸺⸺⸺⸺', @@ -45,21 +47,28 @@ export default class XCursor extends DrawableObject { constructor(name = null, visible = true, color = null, labelContent = 'name + value', x = 1, targetElement = null, labelPosition = 'left', approximate = true, rounding = 3, displayStyle = '— — — — — — —', targetValuePosition = 'Next to target') { - if(name == null) name = Objects.getNewName('X') + if(name == null) name = Common.getNewName('X') super(name, visible, color, labelContent) + this.type = 'X Cursor' this.approximate = approximate this.rounding = rounding - if(typeof x == 'number' || typeof x == 'string') x = new Expression(x.toString()) + if(typeof x == 'number' || typeof x == 'string') x = new MathLib.Expression(x.toString()) this.x = x this.targetElement = targetElement if(typeof targetElement == "string") { - this.targetElement = Objects.currentObjectsByName[targetElement] + this.targetElement = Objects.getObjectByName(targetElement, elementTypes) } this.labelPosition = labelPosition this.displayStyle = displayStyle this.targetValuePosition = targetValuePosition } + export() { + return [this.name, this.visible, this.color.toString(), this.labelContent, + this.x.toEditableString(), this.targetElement == null ? null : this.targetElement.name, this.labelPosition, + this.approximate, this.rounding, this.displayStyle, this.targetValuePosition] + } + getReadableString() { if(this.targetElement == null) return `${this.name} = ${this.x.toString()}` return `${this.name} = ${this.x.toString()}\n${this.getTargetValueLabel()}` @@ -69,33 +78,29 @@ export default class XCursor extends DrawableObject { if(this.targetElement == null) return `${Latex.variable(this.name)} = ${this.x.latexMarkup}` return `\\begin{array}{l} ${Latex.variable(this.name)} = ${this.x.latexMarkup} \\\\ - ${this.getTargetValueLatexLabel()} - \\end{array}` - } - - getApprox() { - let approx = '' - if(this.approximate) { - approx = (this.targetElement.execute(this.x.execute())) - let intLength = Math.round(approx).toString().length - let rounding = Math.max(0, Math.min(this.rounding, approx.toString().length - intLength - 1)) - approx = approx.toPrecision(rounding + intLength) - } - return approx + ${this.getTargetValueLatexLabel()}` } getTargetValueLabel() { - const t = this.targetElement - const approx = this.getApprox() + var t = this.targetElement + var approx = '' + if(this.approximate) { + approx = t.execute(this.x.execute()) + approx = approx.toPrecision(this.rounding + Math.round(approx).toString().length) + } return `${t.name}(${this.name}) = ${t.simplify(this.x.toEditableString())}` + (this.approximate ? ' ≈ ' + approx : '') } getTargetValueLatexLabel() { - const t = this.targetElement - const approx = this.getApprox() - const simpl = t.simplify(this.x.toEditableString()) - return `${Latex.variable(t.name)}(${Latex.variable(this.name)}) = ${simpl.latexMarkup ? simpl.latexMarkup : Latex.variable(simpl)}` + + var t = this.targetElement + var approx = '' + if(this.approximate) { + approx = t.execute(this.x.execute()) + approx = approx.toPrecision(this.rounding + Math.round(approx).toString().length) + } + let simpl = t.simplify(this.x.toEditableString()) + return `${Latex.variable(t.name)}(${Latex.variable(this.name)}) = ${simpl.tokens ? Latex.expression(simpl.tokens) : simpl}` + (this.approximate ? ' \\simeq ' + approx : '') } @@ -103,13 +108,16 @@ export default class XCursor extends DrawableObject { switch(this.labelContent) { case 'name': return this.name + break; case 'name + value': switch(this.targetValuePosition) { case 'Next to target': case 'Hidden': return `${this.name} = ${this.x.toString()}` + break; case 'With label': return this.getReadableString() + break; } case 'null': return '' @@ -120,48 +128,55 @@ export default class XCursor extends DrawableObject { switch(this.labelContent) { case 'name': return Latex.variable(this.name) + break; case 'name + value': switch(this.targetValuePosition) { case 'Next to target': case 'Hidden': return `${Latex.variable(this.name)} = ${this.x.latexMarkup}` + break; case 'With label': return this.getLatexString() + break; } case 'null': return '' } } - draw(canvas) { + draw(canvas, ctx) { let xpos = canvas.x2px(this.x.execute()) switch(this.displayStyle) { case '— — — — — — —': - canvas.drawDashedLine(xpos, 0, xpos, canvas.height, 20) + var dashPxSize = 10 + for(var i = 0; i < canvas.canvasSize.height; i += dashPxSize*2) + canvas.drawLine(ctx, xpos, i, xpos, i+dashPxSize) break; case '⸺⸺⸺⸺⸺⸺': - canvas.drawXLine(this.x.execute()) + canvas.drawXLine(ctx, this.x.execute()) break; case '• • • • • • • • • •': - let pointDistancePx = 10 - let pointSize = 2 - for(let i = 0; i < canvas.height; i += pointDistancePx) - canvas.disc(xpos, i, pointSize) + var pointDistancePx = 10 + var pointSize = 2 + ctx.beginPath(); + for(var i = 0; i < canvas.canvasSize.height; i += pointDistancePx) + ctx.ellipse(xpos-pointSize/2, i-pointSize/2, pointSize, pointSize) + ctx.fill(); break; } // Drawing label at the top of the canvas. - this.drawLabel(canvas, this.labelPosition, xpos, 0, false, null, null, - (x,y,ltxImg) => canvas.drawVisibleImage(ltxImg.source, x, 5, ltxImg.width, ltxImg.height), - (x,y,text,textSize) => canvas.drawVisibleText(text, x, textSize.height+5)) + this.drawLabel(canvas, ctx, this.labelPosition, xpos, 0, false, null, null, + (x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, 5, ltxImg.width, ltxImg.height), + (x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, textSize.height+5)) // Drawing label at the position of the target element. - if(this.targetValuePosition === 'Next to target' && this.targetElement != null) { + if(this.targetValuePosition == 'Next to target' && this.targetElement != null) { let ypos = canvas.y2px(this.targetElement.execute(this.x.execute())) - this.drawLabel(canvas, this.labelPosition, xpos, ypos, false, + this.drawLabel(canvas, ctx, this.labelPosition, xpos, ypos, false, this.getTargetValueLatexLabel.bind(this), this.getTargetValueLabel.bind(this), - (x,y,ltxImg) => canvas.drawVisibleImage(ltxImg.source, x, y, ltxImg.width, ltxImg.height), - (x,y,text,textSize) => canvas.drawVisibleText(text, x, y+textSize.height)) + (x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, y, ltxImg.width, ltxImg.height), + (x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, y+textSize.height)) } } } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parameters.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parameters.js new file mode 100644 index 0000000..75309a8 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parameters.js @@ -0,0 +1,101 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +class Enum { + constructor(...values) { + this.type = 'Enum' + this.values = values + this.translatedValues = values.map(x => qsTr(x)) + } +} + +class ObjectType { + constructor(objType) { + this.type = 'ObjectType' + this.objType = objType + } +} + +class List { + constructor(type, format = /^.+$/, label = '', forbidAdding = false) { + // type can be string, int and double. + this.type = 'List' + this.valueType = type + this.format = format + this.label = label + this.forbidAdding = forbidAdding + } +} + +class Dictionary { + constructor(type, keyType = 'string', format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = '', postKeyLabel = ': ', forbidAdding = false) { + // type & keyType can be string, int and double. + this.type = 'Dict' + this.valueType = type + this.keyType = keyType + this.format = format + this.keyFormat = keyFormat + this.preKeyLabel = preKeyLabel + this.postKeyLabel = postKeyLabel + this.forbidAdding = forbidAdding + } +} + +// Common parameters for Enums + +Enum.Position = new Enum( + QT_TR_NOOP('above'), + QT_TR_NOOP('below'), + QT_TR_NOOP('left'), + QT_TR_NOOP('right'), + QT_TR_NOOP('above-left'), + QT_TR_NOOP('above-right'), + QT_TR_NOOP('below-left'), + QT_TR_NOOP('below-right') +) + +Enum.Positioning = new Enum( + QT_TR_NOOP('center'), + QT_TR_NOOP('top'), + QT_TR_NOOP('bottom'), + QT_TR_NOOP('left'), + QT_TR_NOOP('right'), + QT_TR_NOOP('top-left'), + QT_TR_NOOP('top-right'), + QT_TR_NOOP('bottom-left'), + QT_TR_NOOP('bottom-right') +) + +Enum.FunctionDisplayType = new Enum( + QT_TR_NOOP('application'), + QT_TR_NOOP('function') +) + +Enum.BodePass = new Enum( + QT_TR_NOOP('high'), + QT_TR_NOOP('low') +) + + +Enum.XCursorValuePosition = new Enum( + QT_TR_NOOP('Next to target'), + QT_TR_NOOP('With label'), + QT_TR_NOOP('Hidden') +) + + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/ast.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/ast.js new file mode 100644 index 0000000..30633b6 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/ast.js @@ -0,0 +1,663 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "reference.js" as Reference + +const DERIVATION_PRECISION = 0.01 + +const OPERATION_PRIORITY = { + "+": 10, "-": 10, + "*": 20, "/": 20 +} + +enum ASEType { + UNKNOWN, + VARIABLE, + NUMBER, + STRING, + FUNCTION, + CONSTANT, + OPERATION, + NEGATION // Example: -x. +} + +class AbstractSyntaxElement { + type = ASEType.UNKNOWN; + + execute(variables) { + return null; + } + simplify() { + return this; + } + derivate(variable) { + return this; + } + integrate(variable) { + return this; + } + toEditableString() { + return ""; + } + toLatex() { + return ""; + } + isConstant() { + return true; + } +} + +class Variable extends AbstractSyntaxElement { + type = ASEType.VARIABLE; + + constructor(variableName) { + this.varName = variableName; + } + + execute(variables) { + if(variables.includes(this.varName)) { + return variables[this.varName]; + } else { + throw new EvalError("Unknown variable " + this.varName + "."); + } + } + + derivate(variable) { + if(variable == this.varName) + return new NumberElement(1); + return this; + } + + integrate(variable) { + if(variable == this.varName) + // ^2/2 + return new Operation(new Operation(this, '^', new NumberElement(2)), '/', new NumberElement(2)); + return this; + } + + toEditableString() { + return this.varName; + } + + toLatex() { + return this.varName; + } + + isConstant() { + return false; + } +} + +class ArrayVariable extends Variable { + constructor(arrayName, astIndex) { + super(arrayName + "[" + astIndex.toEditableString() + "]") + this.arrayName = arrayName; + this.astIndex = astIndex; + } + + execute(variables) { + if(variables.includes(this.arrayName)) { + let index = this.astIndex.execute(variables) + if(index % 1 != 0 || index < 0) { // Float index. + throw new EvalError("Non-integer array index " + index + " used as array index for " + this.varName + "."); + } else if(variables[this.arrayName].length <= index) { + throw new EvalError("Out-of-range index " + index + " used as array index for " + this.varName + "."); + } else { + return variables[this.arrayName][index]; + } + } else { + throw new EvalError("Unknown variable " + this.varName + "."); + } + + toLatex() { + return this.varName; + } + } + + simplify() { + return new ArrayVariable(this.arrayName, this.astIndex.simplify()); + } + + toLatex() { + return this.arrayName + '\\left[' + this.astIndex.toLatex() + '\\right]'; + } + + isConstant() { + return false; + } +} + + +class Constant extends Variable { + type = ASEType.CONSTANT; + + constructor(constant) { + super(constant) + } + + execute(variables) { + if(Reference.CONSTANTS_LIST.includes(this.varName)) { + return Reference.CONSTANTS[this.varName]; + } else { + throw new EvalError("Unknown constant " + this.varName + "."); + } + } + + derivate(variable) { + if(variable == this.varName) + return new NumberElement(0); + return this; + } + + integrate(variable) { + return new Operation(new Variable(variable), '^', this); + } + + toEditableString() { + return this.varName; + } + + toLatex() { + return this.varName; + } + + isConstant() { + return true; + } +} + +class NumberElement extends AbstractSyntaxElement { + type = ASEType.NUMBER; + + constructor(number) { + this.value = parseFloat(number); + } + + derivate(variable) { + return new NumberElement(0); + } + + integrate(variable) { + return new Variable(variable); + } + + toEditableString() { + return this.value.toString(); + } + + toLatex() { + return this.value.toString(); + } + + isConstant() { + return true; + } +} + +class StringElement extends AbstractSyntaxElement { + type = ASEType.STRING; + + constructor(str) { + this.str = str; + } + + execute(variables) { + return this.str + } + + derivate(variable) { + return this; + } + + integrate(variable) { + return this; + } + + toEditableString() { + return '"' + this.str + '"'; + } + + toLatex() { + return this.str; + } + + isConstant() { + return true; + } +} + +class FunctionElement extends AbstractSyntaxElement { + type = ASEType.FUNCTION; + + constructor(functionName, astArguments) { + this.function = functionName; + this.args = astArguments; + } + + execute(variables) { + if(this.function == "derivate") { + return executeDerivative(variables) + } else if(this.function == "integrate") { + return executeIntegral(variables) + } else if(Reference.FUNCTIONS_LIST.includes(this.function)) { + let args = this.args.map(arg => arg.execute(variables)); + return Reference.FUNCTIONS[this.function](...args); + } else { + throw new EvalError("Unknown function " + this.function + "."); + } + } + + executeDerivative(variables) { + // Calculate derivation. + if(this.args.length == 2) + if(this.args[1] instanceof Variable) { + let d = this.args[1].varName; // derivative variable name. + if(Object.keys(variables).includes(d)) { + let plus = this.args[0].execute(Object.assign({}, variables, {d: variables[d]+DERIVATION_PRECISION/2})); + let min = this.args[0].execute(Object.assign({}, variables, {d: variables[d]-DERIVATION_PRECISION/2})); + return (plus-min)/DERIVATION_PRECISION + } else + throw new EvalError("Undefined variable " + d + "."); + } else + throw new EvalError(`Argument 1 of function derivate must be a variable.`) + else + throw new EvalError(`Function 'derivate' can only have 2 arguments. ${this.args.length} provided.`) + } + + executeIntegral(variables) { + // Calculate integral. + // Using simons rule + // https://en.wikipedia.org/wiki/Simpson%27s_rule + let d, f, a, b; + if(this.args.length == 2) + // Integral(f,var) integral of f by var. + if(this.args[1] instanceof Variable) + if(Object.keys(variables).includes(d)) { + d = this.args[1].varName; // derivative variable name. + if(!Object.keys(variables).includes(d)) + throw new EvalError("Undefined variable " + d + ".") + a = 0; + b = variables[d]; + f = this.args[0].execute; + } else + else + throw new EvalError(`Argument 2 of function derivate must be a variable.`) + else if(this.args.length == 4) + // Integral(a,b,f,var) integral from a to b of f by var. + if(this.args[3] instanceof Variable) + if(Object.keys(variables).includes(d)) { + a = this.args[0].execute(variables); + b = this.args[1].execute(variables); + f = this.args[2].execute; + d = this.args[3].varName; // derivative variable name. + if(!Object.keys(variables).includes(d)) + throw new EvalError("Undefined variable " + d + "."); + } + else + throw new EvalError(`Argument 4 of function derivate must be a variable.`) + else + throw new EvalError(`Function 'derivate' can only have 2 or 4 arguments. ${this.args.length} provided.`) + + // (b-a)/6*(f(a)+4*f((a+b)/2)+f(b)) + let f_a = f(Object.assign({}, variables, {d: a})), f_b = f(Object.assign({}, variables, {d: b})); + let f_m = f(Object.assign({}, variables, {d: (a+b)/2})) + return (b-a)/6*(f_a+4*f_m+f_b); + } + + simplify() { + let args = this.args.map(arg => arg.simplify(variables)); + let newFunc = new FunctionElement(this.function, args); + let result; + if(newFunc.isConstant() && (result = newFunc.execute({})) % 1 == 0) { // Simplification (e.g. cos(0), sin(π/2)...) + return new NumberElement(result); + } else { + return newFunc; + } + } + + derivate(variable) { + //TODO: Use DERIVATIVES elements in reference. + return new FunctionElement("derivate", this, variable); + } + + integrate(variable) { + //TODO: Use INTEGRALS elements in reference. + return new FunctionElement("integrate", this, variable); + } + + toEditableString() { + return this.function + '(' + this.args.map(arg => arg.toEditableString()).join(', ') + ')'; + } + + toLatex() { + switch(this.function) { + case "sqrt": + return '\\sqrt{' + this.args.map(arg => arg.toLatex()).join(', ') + '}'; + case "abs": + return '\\left|' + this.args.map(arg => arg.toLatex()).join(', ') + '\\right|'; + case "floor": + return '\\left\\lfloor' + this.args.map(arg => arg.toLatex()).join(', ') + '\\right\\rfloor'; + case "ceil": + return '\\left\\lceil' + this.args.map(arg => arg.toLatex()).join(', ') + '\\right\\rceil'; + default: + return '\\mathrm{' + this.function + '}\\left(' + this.args.map(arg => arg.toLatex()).join(', ') + '\\right)'; + } + } + + isConstant() { + if(this.function == "derivate") + return this.args[0].isConstant(); + else if(this.function == "integrate") + return this.args.length == 4 && this.args[0].isConstant() && this.args[1].isConstant() && this.args[2].isConstant(); + else + return this.args.every(x => x.isConstant()); + } +} + +class Operation extends AbstractSyntaxElement { + type = ASEType.OPERATION; + + constructor(leftHand, operation, rightHand) { + this.leftHand = leftHand; + this.ope = operation; + this.rightHand = rightHand; + } + + evaluate(variables) { + switch(this.ope) { + case '+': + return this.leftHand.evaluate(variables) + this.rightHand.evaluate(variables); + case '-': + return this.leftHand.evaluate(variables) - this.rightHand.evaluate(variables); + case '*': + return this.leftHand.evaluate(variables) * this.rightHand.evaluate(variables); + case '/': + return this.leftHand.evaluate(variables) / this.rightHand.evaluate(variables); + case '%': + return this.leftHand.evaluate(variables) % this.rightHand.evaluate(variables); + case '^': + return Math.pow(this.leftHand.evaluate(variables), this.rightHand.evaluate(variables)); + default: + throw new EvalError("Unknown operator " + ope + "."); + } + } + + simplify() { + let leftHand = this.leftHand.simplify(); + let rightHand = this.rightHand.simplify(); + let newOpe = new Operation(leftHand, this.ope, rightHand); + if(leftHand.isConstant() && rightHand.isConstant() && Math.abs(newOpe.execute({})) < 1000000) { + // Do not simplify to too big numbers + switch(this.ope) { + case '+': + case '-': + case '*': + case '^': + case '%': + return new NumberElement(newOpe.execute({})); + case '/': + if(result % 1 == 0) + return new NumberElement(newOpe.execute({})); + else { + let simplified = simplifyFraction(leftHand.number, rightHand.number) + return new Operation(new NumberElement(simplified[0]), '/', new NumberElement(simplified[1])) + } + return this.leftHand.evaluate(variables) / this.rightHand.evaluate(variables); + return Math.pow(this.leftHand.evaluate(variables), this.rightHand.evaluate(variables)); + default: + throw new EvalError("Unknown operator " + ope + "."); + } + } else { + // Simplifications of +- 0 or *1 + switch(this.ope) { + case '+': + case '-': + if(leftHand.type == ASEType.NUMBER && leftHand.value == 0) + return rightHand; + else if(rightHand.type == ASEType.NUMBER && rightHand.value == 0) { + if(ope == '-') leftHand.value = -leftHand.value; + return leftHand; + } else + return newOpe + case '*': + if((leftHand.type == ASEType.NUMBER && leftHand.value == 0) || (rightHand.type == ASEType.NUMBER && rightHand.value == 0)) + return new NumberElement(0); + else if(leftHand.type == ASEType.NUMBER && leftHand.value == 1) + return rightHand; + else if(rightHand.type == ASEType.NUMBER && rightHand.value == 1) + return leftHand; + else + return newOpe + case '^': + if(rightHand.type == ASEType.NUMBER && rightHand.value == 0) + return new NumberElement(1); + else if(rightHand.type == ASEType.NUMBER && rightHand.value == 1) + return new NumberElement(leftHand.value); + else + return newOpe; + case '/': + if(rightHand.type == ASEType.NUMBER && rightHand.value == 1) + return new NumberElement(leftHand.value); + else + return newOpe; + case '%': + return newOpe; + default: + throw new EvalError("Unknown operator " + ope + "."); + } + } + } + + derivate(variable) { + switch(this.ope) { + case '-': + case '+': + return new Operation(this.leftHand.derivate(variable), this.ope, this.rightHand.derivate(variable)); + case '*': + return new Operation( + new Operation(this.leftHand, '*', this.rightHand.derivate(variable)), + '+', + new Operation(this.leftHand.derivate(variable), '*', this.rightHand), + ); + case '/': + return new Operation( + new Operation( + new Operation(this.leftHand, '*', this.rightHand.derivate(variable)), + '-', + new Operation(this.leftHand.derivate(variable), '*', this.rightHand), + ), + '/', + new Operation(this.rightHand, '^', new NumberElement(2)) + ); + case '^': + case '%': + return new FunctionElement("derivate", this.toEditableString()); + default: + throw new EvalError("Unknown operator " + ope + "."); + } + } + + integrate(variable) { + switch(this.ope) { + case '-': + case '+': + return new Operation(this.leftHand.integrate(variable), this.ope, this.rightHand.integrate(variable)); + case '*': + return new Operation( + new Operation(this.leftHand.derivate(variable), '*', this.rightHand), + '+', + new Operation(this.leftHand, '*', this.rightHand.derivate(variable)) + ); + case '/': + return new Operation( + new Operation(this.leftHand.derivate(variable), '*', this.rightHand), + '+', + new Operation(this.leftHand, '*', this.rightHand.derivate(variable)) + ); + case '^': + case '%': + return new FunctionElement("integrate", this.toEditableString()); + default: + throw new EvalError("Unknown operator " + ope + "."); + } + } + + toEditableString() { + let leftString = this.leftHand.toEditableString(); + let rightString = this.rightHand.toEditableString(); + if(this.leftHand.type == ASEType.OPERATION && OPERATION_PRIORITY[this.ope] > OPERATION_PRIORITY[this.leftHand.ope]) + leftString = "(" + leftString + ")" + if(this.rightHand.type == ASEType.OPERATION && OPERATION_PRIORITY[this.ope] > OPERATION_PRIORITY[this.rightHand.ope]) + rightString = "(" + rightString + ")" + return leftString + " " + this.ope + " " + rightString; + } + + + toLatex() { + switch(this.ope) { + case '-': + case '+': + return this.leftHand.toLatex() + this.ope + this.rightHand.toLatex(); + case '*': + return this.leftHand.toLatex() + " \\times " + this.rightHand.toLatex(); + case '%': + return this.leftHand.toLatex() + " \\mathrm{mod} " + this.rightHand.toLatex(); + case '/': + return "\\frac{" + this.leftHand.toLatex() + "}{" + this.rightHand.toLatex() + "}" + case '^': + return this.leftHand.toLatex() + "^{" + this.rightHand.toLatex() + "}"; + default: + throw new EvalError("Unknown operator " + ope + "."); + } + return this.leftHand.toLatex() + ope + this.rightHand.toLatex(); + } + + isConstant() { + return this.leftHand.isConstant() && this.rightHand.isConstant(); + } +} + +function simplifyFraction(num,den) { + // More than gcd because it allows decimals fractions. + let mult = 1; + if(num%1 != 0) + mult = Math.max(mult,Math.pow(10,num.toString().split('.')[1].length)) + else if(den%1 != 0) + mult = Math.max(mult,Math.pow(10,den.toString().split('.')[1].length)) + let a = Math.abs(num*mult); + let b = Math.abs(den*mult); + let gcd = 0 + if (b > a) {let temp = a; a = b; b = temp;} + while (gcd == 0) { + if (b == 0) gcd = a; + a %= b; + if (a == 0) gcd = b; + b %= a; + } + return [num*mult/gcd, den*mult/gcd] +} + +class Negation extends AbstractSyntaxElement { + type = ASEType.NEGATION; + + constructor(variableName) { + this.varName = variableName; + } + + execute(variables) { + if(variables.includes(this.varName)) { + return variables[this.varName]; + } else { + throw new EvalError("Unknown variable " + this.varName + "."); + } + } + + derivate(variable) { + if(variable == this.varName) + return new NumberElement(1); + return this; + } + + integrate(variable) { + if(variable == this.varName) + // ^2/2 + return new Operation(new Operation(this, '^', new NumberElement(2)), '/', new NumberElement(2)); + return this; + } + + toEditableString() { + return this.varName; + } + + toLatex() { + return this.varName; + } + + isConstant() { + return false; + } +} + +class Negation extends AbstractSyntaxElement { + type = ASEType.NEGATION; + + constructor(expression) { + this.expression = expression; + } + + execute(variables) { + if(variables.includes(this.arrayName)) { + let index = this.astIndex.execute(variables) + if(index % 1 != 0 || index < 0) { // Float index. + throw new EvalError("Non-integer array index " + index + " used as array index for " + this.varName + "."); + } else if(variables[this.arrayName].length <= index) { + throw new EvalError("Out-of-range index " + index + " used as array index for " + this.varName + "."); + } else { + return variables[this.arrayName][index]; + } + } else { + throw new EvalError("Unknown variable " + this.varName + "."); + } + + toLatex() { + return this.varName; + } + } + + simplify() { + return new Negation(this.expression.simplify()); + } + + derivate(variable) { + return new Negation(this.expression.derivate(variable)); + } + + integrate(variable) { + return new Negation(this.expression.integrate(variable)); + } + + toLatex() { + return '-' + this.expression.toLatex(); + } + + isConstant() { + return this.expression.isConstant(); + } +} diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/builder.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/builder.js new file mode 100644 index 0000000..c431fab --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/builder.js @@ -0,0 +1,47 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +import "ast.js" as AST +import "tokenizer.js" as TK + + +class ExpressionBuilder { + constructor(tokenizer) { + this.tokenizer = tokenizer; + } + + parseExpression(delimitors = '') { + // Parse a sequence of operations, and orders them based on OPERATION_PRIORITY. + let elements = [] + let operators = [] + let firstToken = this.tokenizer.peek(); + if(firstToken.type == TK.TokenType.OPERATOR) // First operations. + if(firstToken.value == "-") { + // TODO: Set initial argument. + this.tokenizer.skip(TK.TokenType.OPERATOR) + } else + tokenizer.input.raise(`Invalid operator ${firstToken.value} at begining of statement.`) + else { + + } + } + + parseOperation()` +} diff --git a/common/src/parsing/common.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/common.js similarity index 68% rename from common/src/parsing/common.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/common.js index 998f038..bda1b2c 100644 --- a/common/src/parsing/common.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/common.js @@ -1,48 +1,49 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 - * + * Copyright (C) 2022 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 . */ +.pragma library -export default class InputExpression { +class InputExpression { constructor(expression) { - this.position = 0 - this.input = expression + this.position = 0; + this.input = expression; } - + next() { - return this.input[this.position++] + return this.input[this.position++]; } - + peek() { - return this.input[this.position] + return this.input[this.position]; } - + skip(char) { - if(!this.atEnd() && this.peek() === char) { - this.position++ + if(!atEnd() && peek() == char) { + this.position++; } else { - this.raise("Unexpected character " + this.peek() + ". Expected character " + char) + this.raise("Unexpected character " + peek() + ". Expected character " + char); } } - + atEnd() { - return this.position >= this.input.length + return this.position >= this.input.length; } - + raise(message) { throw new SyntaxError(message + " at " + this.position + ".") } diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/reference.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/reference.js new file mode 100644 index 0000000..9838446 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/reference.js @@ -0,0 +1,88 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +const CONSTANTS = { + "π": Math.PI, + "pi": Math.PI, + "inf": Infinity, + "infinity": Infinity, + "∞": Infinity, + "e": Infinity +}; +const CONSTANTS_LIST = Object.keys(CONSTANTS); + +const FUNCTIONS = { + "abs": Math.abs, + "acos": Math.acos, + "acosh": Math.acosh, + "asin": Math.asin, + "asinh": Math.asinh, + "atan": Math.atan, + "atan2": Math.atan2, + "atanh": Math.atanh, + "cbrt": Math.cbrt, + "ceil": Math.ceil, + "clz32": Math.clz32, + "cos": Math.cos, + "cosh": Math.cosh, + "exp": Math.exp, + "expm1": Math.expm1, + "floor": Math.floor, + "fround": Math.fround, + "hypot": Math.hypot, + "imul": Math.imul, + "log": Math.log, + "log10": Math.log10, + "log1p": Math.log1p, + "log2": Math.log2, + "max": Math.max, + "min": Math.min, + "pow": Math.log2, + "random": Math.random, + "round": Math.round, + "sign": Math.sign, + "sin": Math.sin, + "sinh": Math.sinh, + "sqrt": Math.sqrt, + "tan": Math.tan, + "tanh": Math.tanh, + "trunc": Math.trunc, +} +const FUNCTIONS_LIST = Object.keys(FUNCTIONS); +// TODO: Complete +const DERIVATIVES = { + "abs": "abs(<1>)/<1>", + "acos": "-derivate(<1>)/sqrt(1-(<1>)^2)", + "acosh": "derivate(<1>)/sqrt((<1>)^2-1)", + "asin": "derivate(<1>)/sqrt(1-(<1>)^2)", + "asinh": "derivate(<1>)/sqrt((<1>)^2+1)", + "atan": "derivate(<1>)/(1+(<1>)^2)", + "atan2": "", +} +const INTEGRALS = { + "abs": "integrate(<1>)*sign(<1>)", + "acos": "", + "acosh": "", + "asin": "", + "asinh": "", + "atan": "", + "atan2": "", +} + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/tokenizer.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/tokenizer.js new file mode 100644 index 0000000..d600f5b --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/tokenizer.js @@ -0,0 +1,141 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +.import "reference.js" as Reference + +const WHITESPACES = " \t\n\r" +const STRING_LIMITORS = '"\'`'; +const OPERATORS = "+-*/^%"; +const PUNCTUTATION = "()[]{},"; +const NUMBER_CHARS = "0123456789." +const IDENTIFIER_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_₀₁₂₃₄₅₆₇₈₉αβγδεζηθκλμξρςστφχψωₐₑₒₓₔₕₖₗₘₙₚₛₜ" + +enum TokenType { + // Expression type + VARIABLE, + CONSTANT, + FUNCTION, + OPERATOR, + PUNCT, + NUMBER, + STRING +} + +class Token { + constructor(type, value) { + this.type = type; + this.value = value; + } +} + +class ExpressionTokenizer { + constructor(input) { + this.input = input; + this.currentToken = null; + } + + skipWhitespaces() { + while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek())) + this.input.next(); + } + + readString() { + let delimitation = this.input.peek(); + if(STRING_LIMITORS.includes(delimitation)) { + this.input.skip(delimitation) + let included = ""; + let justEscaped = false; + while(!this.input.atEnd() && (!STRING_LIMITORS.includes(this.input.peek()) || justEscaped)) { + justEscaped = this.input.peek() == "\\" + if(!justEscaped) + included += this.input.next(); + } + this.input.skip(delimitation) + return new Token(TokenType.STRING, included); + } else { + this.input.raise("Unexpected " + delimitation + ". Expected string delimitator") + } + } + + readNumber() { + let included = ""; + let hasDot = false; + while(!this.input.atEnd() && NUMBER_CHARS.includes(this.input.peek())) { + if(this.input.peek() == ".") { + if(hasDot) this.input.raise("Unexpected '.'. Expected digit") + hasDot = true; + } + included += this.input.next(); + } + } + + readIdentifier() { + let identifier = ""; + let hasDot = false; + while(!this.input.atEnd() && IDENTIFIER_CHARS.includes(this.input.peek())) { + identifier += this.input.next(); + } + if(Reference.CONSTANTS_LIST.includes(identifier.toLowerCase())) { + return new Token(TokenType.CONSTANT, identifier.toLowerCase()) + } else if(Reference.FUNCTIONS_LIST.includes(identifier.toLowerCase())) { + return new Token(TokenType.FUNCTION, identifier.toLowerCase()) + } else { + return new Token(TokenType.VARIABLE, identifier) + } + } + + readNextToken() { + this.skipWhitespaces() + if(input.atEnd()) return null; + let c = input.peek(); + if(STRING_LIMITORS.includes(c)) return this.readString(); + if(NUMBER_CHARS.includes(c)) return this.readNumber(); + if(IDENTIFIER_CHARS.includes(c)) return this.readIdentifier(); + if(Reference.CONSTANTS_LIST.includes(c)) return new Token(TokenType.CONSTANT, c); + if(OPERATORS.includes(c)) return new Token(TokenType.OPERATOR, c); + if(PUNCTUTATION.includes(c)) return new Token(TokenType.PUNCT, c); + this.input.throw("Unknown token character " + c) + } + + peek() { + if(this.currentToken == null) this.currentToken = this.readNextToken(); + return this.currentToken; + } + + next() { + let tmp; + if(this.currentToken == null) + tmp = this.readNextToken(); + else + tmp = this.currentToken; + this.currentToken = null; + return tmp; + } + + atEnd() { + return this.peek() == null; + } + + skip(type) { + Token next = Next(); + if(next.type != type) + input.raise("Unexpected token " + next.type.oLowerCase() + ' "' + next.value + '". Expected ' + type.toLowerCase()); + } +} diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/utils.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/utils.js new file mode 100644 index 0000000..017b411 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/utils.js @@ -0,0 +1,349 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . + */ + +.pragma library + +var powerpos = { + "-": "⁻", + "+": "⁺", + "=": "⁼", + " ": " ", + "(": "⁽", + ")": "⁾", + "0": "⁰", + "1": "¹", + "2": "²", + "3": "³", + "4": "⁴", + "5": "⁵", + "6": "⁶", + "7": "⁷", + "8": "⁸", + "9": "⁹", + "a": "ᵃ", + "b": "ᵇ", + "c": "ᶜ", + "d": "ᵈ", + "e": "ᵉ", + "f": "ᶠ", + "g": "ᵍ", + "h": "ʰ", + "i": "ⁱ", + "j": "ʲ", + "k": "ᵏ", + "l": "ˡ", + "m": "ᵐ", + "n": "ⁿ", + "o": "ᵒ", + "p": "ᵖ", + "r": "ʳ", + "s": "ˢ", + "t": "ᵗ", + "u": "ᵘ", + "v": "ᵛ", + "w": "ʷ", + "x": "ˣ", + "y": "ʸ", + "z": "ᶻ" +} + +var indicepos = { + "-": "₋", + "+": "₊", + "=": "₌", + "(": "₍", + ")": "₎", + " ": " ", + "0": "₀", + "1": "₁", + "2": "₂", + "3": "₃", + "4": "₄", + "5": "₅", + "6": "₆", + "7": "₇", + "8": "₈", + "9": "₉", + "a": "ₐ", + "e": "ₑ", + "h": "ₕ", + "i": "ᵢ", + "j": "ⱼ", + "k": "ₖ", + "l": "ₗ", + "m": "ₘ", + "n": "ₙ", + "o": "ₒ", + "p": "ₚ", + "r": "ᵣ", + "s": "ₛ", + "t": "ₜ", + "u": "ᵤ", + "v": "ᵥ", + "x": "ₓ", +} +// Put a text in sup position +function textsup(text) { + var ret = "" + text = text.toString() + for (var i = 0; i < text.length; i++) { + if(Object.keys(powerpos).indexOf(text[i]) >= 0) { + ret += powerpos[text[i]] + } else { + ret += text[i] + } + } + return ret +} + +// Put a text in sub position +function textsub(text) { + var ret = "" + text = text.toString() + for (var i = 0; i < text.length; i++) { + if(Object.keys(indicepos).indexOf(text[i]) >= 0) { + ret += indicepos[text[i]] + } else { + ret += text[i] + } + } + return ret +} + +function simplifyExpression(str) { + var replacements = [ + // Operations not done by parser. + [// Decomposition way 2 + /(^.?|[+-] |\()([-.\d\w]+) ([*/]) \((([-.\d\w] [*/] )?[-\d\w.]+) ([+\-]) (([-.\d\w] [*/] )?[\d\w.+]+)\)(.?$| [+-]|\))/g, + "$1$2 $3 $4 $6 $2 $3 $7$9" + ], + [ // Decomposition way 2 + /(^.?|[+-] |\()\((([-.\d\w] [*/] )?[-\d\w.]+) ([+\-]) (([-.\d\w] [*/] )?[\d\w.+]+)\) ([*/]) ([-.\d\w]+)(.?$| [+-]|\))/g, + "$1$2 $7 $8 $4 $5 $7 $8$9" + ], + [ // Factorisation of π elements. + /(([-\d\w.]+ [*/] )*)(pi|π)(( [/*] [-\d\w.]+)*) ([+-]) (([-\d\w.]+ [*/] )*)(pi|π)(( [/*] [-\d\w.]+)*)?/g, + function(match, m1, n1, pi1, m2, ope2, n2, opeM, m3, n3, pi2, m4, ope4, n4) { + // g1, g2, g3 , g4, g5, g6, g7, g8, g9, g10, g11,g12 , g13 + // We don't care about mx & pix, ope2 & ope4 are either / or * for n2 & n4. + // n1 & n3 are multiplied, opeM is the main operation (- or +). + // Putting all n in form of number + //n2 = n2 == undefined ? 1 : parseFloat(n) + n1 = m1 == undefined ? 1 : eval(m1 + '1') + n2 = m2 == undefined ? 1 : eval('1' + m2) + n3 = m3 == undefined ? 1 : eval(m3 + '1') + n4 = m4 == undefined ? 1 : eval('1' + m4) + //var [n1, n2, n3, n4] = [n1, n2, n3, n4].map(n => n == undefined ? 1 : parseFloat(n)) + // Falling back to * in case it does not exist (the corresponding n would be 1) + var [ope2, ope4] = [ope2, ope4].map(ope => ope == '/' ? '/' : '*') + var coeff1 = n1*n2 + var coeff2 = n3*n4 + var coefficient = coeff1+coeff2-(opeM == '-' ? 2*coeff2 : 0) + + return `${coefficient} * π` + } + ], + [ // Removing parenthesis when content is only added from both sides. + /(^.?|[+-] |\()\(([^)(]+)\)(.?$| [+-]|\))/g, + function(match, b4, middle, after) {return `${b4}${middle}${after}`} + ], + [ // Removing parenthesis when content is only multiplied. + /(^.?|[*\/] |\()\(([^)(+-]+)\)(.?$| [*\/+-]|\))/g, + function(match, b4, middle, after) {return `${b4}${middle}${after}`} + ], + [ // Removing parenthesis when content is only multiplied. + /(^.?|[*\/-+] |\()\(([^)(+-]+)\)(.?$| [*\/]|\))/g, + function(match, b4, middle, after) {return `${b4}${middle}${after}`} + ], + [// Simplification additions/substractions. + /(^.?|[^*\/] |\()([-.\d]+) (\+|\-) (\([^)(]+\)|[^)(]+) (\+|\-) ([-.\d]+)(.?$| [^*\/]|\))/g, + function(match, b4, n1, op1, middle, op2, n2, after) { + var total + if(op2 == '+') { + total = parseFloat(n1) + parseFloat(n2) + } else { + total = parseFloat(n1) - parseFloat(n2) + } + return `${b4}${total} ${op1} ${middle}${after}` + } + ], + [// Simplification multiplications/divisions. + /([-.\d]+) (\*|\/) (\([^)(]+\)|[^)(+-]+) (\*|\/) ([-.\d]+)/g, + function(match, n1, op1, middle, op2, n2) { + if(parseInt(n1) == n1 && parseInt(n2) == n2 && op2 == '/' && + (parseInt(n1) / parseInt(n2)) % 1 != 0) { + // Non int result for int division. + return `(${n1} / ${n2}) ${op1} ${middle}` + } else { + if(op2 == '*') { + return `${parseFloat(n1) * parseFloat(n2)} ${op1} ${middle}` + } else { + return `${parseFloat(n1) / parseFloat(n2)} ${op1} ${middle}` + } + } + } + ], + [// Starting & ending parenthesis if not needed. + /^\s*\((.*)\)\s*$/g, + function(match, middle) { + var str = middle + // Replace all groups + while(/\([^)(]+\)/g.test(str)) + str = str.replace(/\([^)(]+\)/g, '') + // There shouldn't be any more parenthesis + // If there is, that means the 2 parenthesis are needed. + if(!str.includes(')') && !str.includes('(')) { + return middle + } else { + return `(${middle})` + } + + } + ], + // Simple simplifications + [/(\s|^|\()0(\.0+)? \* (\([^)(]+\))/g, '$10'], + [/(\s|^|\()0(\.0+)? \* ([^)(+-]+)/g, '$10'], + [/(\([^)(]\)) \* 0(\.0+)?(\s|$|\))/g, '0$3'], + [/([^)(+-]) \* 0(\.0+)?(\s|$|\))/g, '0$3'], + [/(\s|^|\()1(\.0+)? (\*|\/) /g, '$1'], + [/(\s|^|\()0(\.0+)? (\+|\-) /g, '$1'], + [/ (\*|\/) 1(\.0+)?(\s|$|\))/g, '$3'], + [/ (\+|\-) 0(\.0+)?(\s|$|\))/g, '$3'], + [/(^| |\() /g, '$1'], + [/ ($|\))/g, '$1'], + ] + + // Replacements + var found + do { + found = false + for(var replacement of replacements) + while(replacement[0].test(str)) { + found = true + str = str.replace(replacement[0], replacement[1]) + } + } while(found) + return str +} + + +function makeExpressionReadable(str) { + var replacements = [ + // variables + [/pi/g, 'π'], + [/Infinity/g, '∞'], + [/inf/g, '∞'], + // Other + [/ \* /g, '×'], + [/ \^ /g, '^'], + [/\^\(([^\^]+)\)/g, function(match, p1) { return textsup(p1) }], + [/\^([^ "]+)/g, function(match, p1) { return textsup(p1) }], + [/_\(([^_]+)\)/g, function(match, p1) { return textsub(p1) }], + [/_([^ "]+)/g, function(match, p1) { return textsub(p1) }], + [/\[([^\[\]]+)\]/g, function(match, p1) { return textsub(p1) }], + [/(\d|\))×/g, '$1'], + //[/×(\d|\()/g, '$1'], + [/\(([^)(+.\/-]+)\)/g, "$1"], + [/integral\((.+),\s?(.+),\s?("|')(.+)("|'),\s?("|')(.+)("|')\)/g, function(match, a, b, p1, body, p2, p3, by, p4) { + if(a.length < b.length) { + return `∫${textsub(a)}${textsup(b)} ${body} d${by}` + } else { + return `∫${textsup(b)}${textsub(a)} ${body} d${by}` + } + }], + [/derivative\(?("|')(.+)("|'), ?("|')(.+)("|'), ?(.+)\)?/g, function(match, p1, body, p2, p3, by, p4, x) { + return `d(${body.replace(new RegExp(by, 'g'), 'x')})/dx` + }] + ] + + str = simplifyExpression(str) + // Replacements + for(var replacement of replacements) + while(replacement[0].test(str)) + str = str.replace(replacement[0], replacement[1]) + return str +} + +function parseName(str, removeUnallowed = true) { + var replacements = [ + // Greek letters + [/([^a-z]|^)al(pha)?([^a-z]|$)/g, '$1α$3'], + [/([^a-z]|^)be(ta)?([^a-z]|$)/g, '$1β$3'], + [/([^a-z]|^)ga(mma)?([^a-z]|$)/g, '$1γ$3'], + [/([^a-z]|^)de(lta)?([^a-z]|$)/g, '$1δ$3'], + [/([^a-z]|^)ep(silon)?([^a-z]|$)/g, '$1ε$3'], + [/([^a-z]|^)ze(ta)?([^a-z]|$)/g, '$1ζ$3'], + [/([^a-z]|^)et(a)?([^a-z]|$)/g, '$1η$3'], + [/([^a-z]|^)th(eta)?([^a-z]|$)/g, '$1θ$3'], + [/([^a-z]|^)io(ta)?([^a-z]|$)/g, '$1ι$3'], + [/([^a-z]|^)ka(ppa)([^a-z]|$)?/g, '$1κ$3'], + [/([^a-z]|^)la(mbda)?([^a-z]|$)/g, '$1λ$3'], + [/([^a-z]|^)mu([^a-z]|$)/g, '$1μ$2'], + [/([^a-z]|^)nu([^a-z]|$)/g, '$1ν$2'], + [/([^a-z]|^)xi([^a-z]|$)/g, '$1ξ$2'], + [/([^a-z]|^)rh(o)?([^a-z]|$)/g, '$1ρ$3'], + [/([^a-z]|^)si(gma)?([^a-z]|$)/g, '$1σ$3'], + [/([^a-z]|^)ta(u)?([^a-z]|$)/g, '$1τ$3'], + [/([^a-z]|^)up(silon)?([^a-z]|$)/g, '$1υ$3'], + [/([^a-z]|^)ph(i)?([^a-z]|$)/g, '$1φ$3'], + [/([^a-z]|^)ch(i)?([^a-z]|$)/g, '$1χ$3'], + [/([^a-z]|^)ps(i)?([^a-z]|$)/g, '$1ψ$3'], + [/([^a-z]|^)om(ega)?([^a-z]|$)/g, '$1ω$3'], + // Capital greek letters + [/([^a-z]|^)gga(mma)?([^a-z]|$)/g, '$1Γ$3'], + [/([^a-z]|^)gde(lta)?([^a-z]|$)/g, '$1Δ$3'], + [/([^a-z]|^)gth(eta)?([^a-z]|$)/g, '$1Θ$3'], + [/([^a-z]|^)gla(mbda)?([^a-z]|$)/g, '$1Λ$3'], + [/([^a-z]|^)gxi([^a-z]|$)/g, '$1Ξ$2'], + [/([^a-z]|^)gpi([^a-z]|$)/g, '$1Π$2'], + [/([^a-z]|^)gsi(gma)([^a-z]|$)?/g, '$1Σ$3'], + [/([^a-z]|^)gph(i)?([^a-z]|$)/g, '$1Φ$3'], + [/([^a-z]|^)gps(i)?([^a-z]|$)/g, '$1Ψ$3'], + [/([^a-z]|^)gom(ega)?([^a-z]|$)/g, '$1Ω$3'], + // Underscores + [/_\(([^_]+)\)/g, function(match, p1) { return textsub(p1) }], + [/_([^" ]+)/g, function(match, p1) { return textsub(p1) }], + // Array elements + [/\[([^\]\[]+)\]/g, function(match, p1) { return textsub(p1) }], + // Removing + [/[xπℝℕ\\∪∩\]\[ ()^/÷*×+=\d-]/g , ''], + ] + if(!removeUnallowed) replacements.pop() + // Replacements + for(var replacement of replacements) + str = str.replace(replacement[0], replacement[1]) + return str +} + +String.prototype.toLatinUppercase = function() { + return this.replace(/[a-z]/g, function(match){return match.toUpperCase()}) +} + +function camelCase2readable(label) { + var parsed = parseName(label, false) + return parsed.charAt(0).toLatinUppercase() + parsed.slice(1).replace(/([A-Z])/g," $1") +} + +function getRandomColor() { + var clrs = '0123456789ABCDEF'; + var color = '#'; + for(var i = 0; i < 6; i++) { + color += clrs[Math.floor(Math.random() * (16-5*(i%2==0)))]; + } + return color; +} diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/qmldir b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/qmldir new file mode 100644 index 0000000..ac68e47 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/qmldir @@ -0,0 +1,4 @@ +module eu.ad5001.LogarithmPlotter + +Settings 1.0 Settings.qml +Alert 1.0 Alert.qml diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/MixedMenu b/LogarithmPlotter/qml/eu/ad5001/MixedMenu similarity index 100% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/MixedMenu rename to LogarithmPlotter/qml/eu/ad5001/MixedMenu diff --git a/runtime-pyside6/LogarithmPlotter/util/__init__.py b/LogarithmPlotter/util/__init__.py similarity index 100% rename from runtime-pyside6/LogarithmPlotter/util/__init__.py rename to LogarithmPlotter/util/__init__.py diff --git a/runtime-pyside6/LogarithmPlotter/util/config.py b/LogarithmPlotter/util/config.py similarity index 58% rename from runtime-pyside6/LogarithmPlotter/util/config.py rename to LogarithmPlotter/util/config.py index 2cce4dc..ecaa214 100644 --- a/runtime-pyside6/LogarithmPlotter/util/config.py +++ b/LogarithmPlotter/util/config.py @@ -1,6 +1,6 @@ """ * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -19,85 +19,48 @@ from os import path, environ, makedirs from platform import system from json import load, dumps -from shutil import which +from PySide2.QtCore import QLocale, QTranslator -from PySide6.QtCore import QLocale, QTranslator DEFAULT_SETTINGS = { "check_for_updates": True, "reset_redo_stack": True, "last_install_greet": "0", - "enable_latex": which("latex") is not None and which("dvipng") is not None, - "enable_latex_threaded": False, - "expression_editor": { - "autoclose": True, - "colorize": True, - "color_scheme": 0, - }, - "autocompletion": { - "enabled": True - }, - "default_graph": { - "xzoom": 100, - "yzoom": 10, - "xmin": 5 / 10, - "ymax": 25, - "xaxisstep": "4", - "yaxisstep": "4", - "xlabel": "", - "ylabel": "", - "linewidth": 1, - "textsize": 18, - "logscalex": True, - "showxgrad": True, - "showygrad": True - } + "enable_latex": True } # Create config directory CONFIG_PATH = { - "Linux": path.join(environ["XDG_CONFIG_HOME"], "LogarithmPlotter") - if "XDG_CONFIG_HOME" in environ else - path.join(path.expanduser("~"), ".config", "LogarithmPlotter"), + "Linux": path.join(environ["XDG_CONFIG_HOME"], "LogarithmPlotter") if "XDG_CONFIG_HOME" in environ else path.join(path.expanduser("~"), ".config", "LogarithmPlotter"), "Windows": path.join(path.expandvars('%APPDATA%'), "LogarithmPlotter", "config"), "Darwin": path.join(path.expanduser("~"), "Library", "Application Support", "LogarithmPlotter"), }[system()] CONFIG_FILE = path.join(CONFIG_PATH, "config.json") -current_config = DEFAULT_SETTINGS - - -class UnknownNamespaceError(Exception): pass +initialized = False +current_config= DEFAULT_SETTINGS def init(): """ Initializes the config and loads all possible settings from the file if needs be. """ - global current_config - current_config = DEFAULT_SETTINGS makedirs(CONFIG_PATH, exist_ok=True) if path.exists(CONFIG_FILE): - cfg_data = load(open(CONFIG_FILE, 'r', -1, 'utf8')) + cfg_data = load(open(CONFIG_FILE, 'r', -1, 'utf8')) for setting_name in cfg_data: - if type(cfg_data[setting_name]) == dict: - for setting_name2 in cfg_data[setting_name]: - setSetting(setting_name + "." + setting_name2, cfg_data[setting_name][setting_name2]) - else: - setSetting(setting_name, cfg_data[setting_name]) - - -def save(file=CONFIG_FILE): + setSetting(setting_name, cfg_data[setting_name]) + +def save(): """ Saves the config to the path. """ - write_file = open(file, 'w', -1, 'utf8') + write_file = open(CONFIG_FILE, 'w', -1, 'utf8') write_file.write(dumps(current_config)) write_file.close() - def getSetting(namespace): """ Returns a setting from a namespace. @@ -111,10 +74,9 @@ def getSetting(namespace): setting = setting[name] else: # return namespace # Return original name - raise UnknownNamespaceError(f"Setting {namespace} doesn't exist. Debug: {setting}, {name}") + raise ValueError('Setting ' + namespace + ' doesn\'t exist. Debug: ', setting, name) return setting - def setSetting(namespace, data): """ Sets a setting at a namespace with data. @@ -123,9 +85,11 @@ def setSetting(namespace, data): """ names = namespace.split(".") setting = current_config - for name in names[:-1]: - if name in setting: - setting = setting[name] + for name in names: + if name != names[-1]: + if name in setting: + setting = setting[name] + else: + raise ValueError('Setting {} doesn\'t exist. Debug: {}, {}'.format(namespace, setting, name)) else: - raise UnknownNamespaceError(f"Setting {namespace} doesn't exist. Debug: {setting}, {name}") - setting[names[-1]] = data + setting[name] = data diff --git a/LogarithmPlotter/util/helper.py b/LogarithmPlotter/util/helper.py new file mode 100644 index 0000000..4f2a7da --- /dev/null +++ b/LogarithmPlotter/util/helper.py @@ -0,0 +1,161 @@ +""" + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . +""" + +from PySide2.QtWidgets import QMessageBox, QApplication +from PySide2.QtCore import QRunnable, QThreadPool, QThread, QObject, Signal, Slot, QCoreApplication +from PySide2.QtQml import QQmlApplicationEngine +from PySide2.QtGui import QImage +from PySide2 import __version__ as PySide2_version + +from os import chdir, path +from json import loads +from sys import version as sys_version +from urllib.request import urlopen +from urllib.error import HTTPError, URLError + +from LogarithmPlotter import __VERSION__ +from LogarithmPlotter.util import config + +class ChangelogFetcher(QRunnable): + def __init__(self, helper): + QRunnable.__init__(self) + self.helper = helper + + def run(self): + msg_text = "Unknown changelog error." + try: + # Fetching version + r = urlopen("https://api.ad5001.eu/changelog/logarithmplotter/") + lines = r.readlines() + r.close() + msg_text = "".join(map(lambda x: x.decode('utf-8'), lines)).strip() + except HTTPError as e: + msg_text = QCoreApplication.translate("changelog","Could not fetch changelog: Server error {}.").format(str(e.code)) + except URLError as e: + msg_text = QCoreApplication.translate("changelog","Could not fetch update: {}.").format(str(e.reason)) + self.helper.gotChangelog.emit(msg_text) + +class Helper(QObject): + changelogFetched = Signal(str) + gotChangelog = Signal(str) + + def __init__(self, cwd: str, tmpfile: str): + QObject.__init__(self) + self.cwd = cwd + self.tmpfile = tmpfile + self.gotChangelog.connect(self.fetched) + + def fetched(self, changelog: str): + self.changelogFetched.emit(changelog) + + @Slot(str, str) + def write(self, filename, filedata): + chdir(self.cwd) + if path.exists(path.dirname(path.realpath(filename))): + if filename.split(".")[-1] == "lpf": + # Add header to file + filedata = "LPFv1" + filedata + f = open(path.realpath(filename), 'w', -1, 'utf8') + f.write(filedata) + f.close() + chdir(path.dirname(path.realpath(__file__))) + + @Slot(str, result=str) + def load(self, filename): + chdir(self.cwd) + data = '{}' + if path.exists(path.realpath(filename)): + f = open(path.realpath(filename), 'r', -1, 'utf8') + data = f.read() + f.close() + try: + if data[:5] == "LPFv1": + # V1 version of the file + data = data[5:] + elif data[0] == "{" and "type" in loads(data) and loads(data)["type"] == "logplotv1": + pass + elif data[:3] == "LPF": + # More recent version of LogarithmPlotter file, but incompatible with the current format + raise Exception(QCoreApplication.translate("This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}.\nPlease update LogarithmPlotter to open this file.".format(__VERSION__))) + else: + raise Exception("Invalid LogarithmPlotter file.") + except Exception as e: # If file can't be loaded + QMessageBox.warning(None, 'LogarithmPlotter', QCoreApplication.translate('main','Could not open file "{}":\n{}').format(filename, e), QMessageBox.Ok) # Cannot parse file + else: + QMessageBox.warning(None, 'LogarithmPlotter', QCoreApplication.translate('main','Could not open file: "{}"\nFile does not exist.').format(filename), QMessageBox.Ok) # Cannot parse file + try: + chdir(path.dirname(path.realpath(__file__))) + except NotADirectoryError as e: + # Triggered on bundled versions of MacOS when it shouldn't. Prevents opening files. + # See more at https://git.ad5001.eu/Ad5001/LogarithmPlotter/issues/1 + pass + return data + + @Slot(result=str) + def gettmpfile(self): + return self.tmpfile + + @Slot() + def copyImageToClipboard(self): + clipboard = QApplication.clipboard() + clipboard.setImage(QImage(self.tmpfile)) + + @Slot(result=str) + def getVersion(self): + return __VERSION__ + + @Slot(str, result=str) + def getSetting(self, namespace): + return config.getSetting(namespace) + + @Slot(str, result=bool) + def getSettingBool(self, namespace): + return config.getSetting(namespace) + + @Slot(str, str) + def setSetting(self, namespace, value): + return config.setSetting(namespace, value) + + @Slot(str, bool) + def setSettingBool(self, namespace, value): + return config.setSetting(namespace, value) + + @Slot(str) + def setLanguage(self, new_lang): + config.setSetting("language", new_lang) + + @Slot(result=str) + def getDebugInfos(self): + """ + Returns the version info about Qt, PySide2 & Python + """ + return QCoreApplication.translate('main',"Built with PySide2 (Qt) v{} and python v{}").format(PySide2_version, sys_version.split("\n")[0]) + + @Slot() + def fetchChangelog(self): + changelog_cache_path = path.join(path.dirname(path.realpath(__file__)), "CHANGELOG.md") + if path.exists(changelog_cache_path): + # We have a cached version of the changelog, for env that don't have access to the internet. + f = open(changelog_cache_path); + self.changelogFetched.emit("".join(f.readlines()).strip()) + f.close() + else: + # Fetch it from the internet. + runnable = ChangelogFetcher(self) + QThreadPool.globalInstance().start(runnable) + diff --git a/LogarithmPlotter/util/latex.py b/LogarithmPlotter/util/latex.py new file mode 100644 index 0000000..0ce5b91 --- /dev/null +++ b/LogarithmPlotter/util/latex.py @@ -0,0 +1,192 @@ +""" + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . +""" + +from PySide2.QtCore import QObject, Slot, Property, QCoreApplication +from PySide2.QtGui import QImage, QColor +from PySide2.QtWidgets import QApplication, QMessageBox + +from os import path, remove +from string import Template +from tempfile import TemporaryDirectory +from subprocess import Popen, TimeoutExpired, PIPE +from platform import system +from shutil import which +from sys import argv + +""" +Searches for a valid Latex and DVIPNG (http://savannah.nongnu.org/projects/dvipng/) +installation and collects the binary path in the DVIPNG_PATH variable. +If not found, it will send an alert to the user. +""" +LATEX_PATH = which('latex') +DVIPNG_PATH = which('dvipng') +#subprocess.run(["ls", "-l", "/dev/null"], capture_output=True) + +DEFAULT_LATEX_DOC = Template(r""" +\documentclass[]{minimal} +\usepackage[utf8]{inputenc} +\usepackage{calligra} +\usepackage{amsfonts} + +\title{} +\author{} + +\begin{document} + +$$$$ $markup $$$$ + +\end{document} +""") + +class Latex(QObject): + """ + Base class to convert Latex equations into PNG images with custom font color and size. + It doesn't have any python dependency, but requires a working latex installation and + dvipng to be installed on the system. + """ + def __init__(self, tempdir: TemporaryDirectory): + QObject.__init__(self) + self.tempdir = tempdir + + def check_latex_install(self): + """ + Checks if the current latex installation is valid. + """ + if LATEX_PATH is None: + print("No Latex installation found.") + if "--test-build" not in argv: + QMessageBox.warning(None, "LogarithmPlotter - Latex setup", QCoreApplication.translate("latex", "No Latex installation found.\nIf you already have a latex distribution installed, make sure it's installed on your path.\nOtherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/.")) + elif DVIPNG_PATH is None: + print("DVIPNG not found.") + if "--test-build" not in argv: + QMessageBox.warning(None, "LogarithmPlotter - Latex setup", QCoreApplication.translate("latex", "DVIPNG was not found. Make sure you include it from your Latex distribution.")) + + @Property(bool) + def latexSupported(self): + return LATEX_PATH is not None and DVIPNG_PATH is not None + + @Slot(str, float, QColor, result=str) + def render(self, latex_markup: str, font_size: float, color: QColor = True) -> str: + """ + Renders a latex string into a png file. + """ + markup_hash = hash(latex_markup) + export_path = path.join(self.tempdir.name, f'{markup_hash}_{font_size}_{color.rgb()}') + if self.latexSupported and not path.exists(export_path + ".png"): + print("Rendering", latex_markup, export_path) + # Generating file + try: + latex_path = path.join(self.tempdir.name, str(markup_hash)) + # If the formula is just recolored or the font is just changed, no need to recreate the DVI. + if not path.exists(latex_path + ".dvi"): + self.create_latex_doc(latex_path, latex_markup) + self.convert_latex_to_dvi(latex_path) + self.cleanup(latex_path) + self.convert_dvi_to_png(latex_path, export_path, font_size, color) + except Exception as e: # One of the processes failed. A message will be sent every time. + raise e + img = QImage(export_path + ".png"); + # Small hack, not very optimized since we load the image twice, but you can't pass a QImage to QML and expect it to be loaded + return f'{export_path}.png,{img.width()},{img.height()}' + + def create_latex_doc(self, export_path: str, latex_markup: str): + """ + Creates a temporary latex document with base file_hash as file name and a given expression markup latex_markup. + """ + ltx_path = export_path + ".tex" + f = open(export_path + ".tex", 'w') + f.write(DEFAULT_LATEX_DOC.substitute(markup = latex_markup)) + f.close() + + def convert_latex_to_dvi(self, export_path: str): + """ + Converts a DVI file to a PNG file. + """ + self.run([ + LATEX_PATH, + export_path + ".tex" + ]) + + + def convert_dvi_to_png(self, dvi_path: str, export_path: str, font_size: float, color: QColor): + """ + Converts a DVI file to a PNG file. + Documentation: https://linux.die.net/man/1/dvipng + """ + fg = color.convertTo(QColor.Rgb) + fg = f'rgb {fg.redF()} {fg.greenF()} {fg.blueF()}' + depth = int(font_size * 72.27 / 100) * 10 + self.run([ + DVIPNG_PATH, + '-T', 'tight', # Make sure image borders are as tight around the equation as possible to avoid blank space. + '--truecolor', # Make sure it's rendered in 24 bit colors. + '-D',f'{depth}', # Depth of the image + '-bg', 'Transparent', # Transparent background + '-fg',f'{fg}', # Foreground of the wanted color. + f'{dvi_path}.dvi', # Input file + '-o',f'{export_path}.png', # Output file + ]) + + def run(self, process: list): + """ + Runs a subprocess and handles exceptions and messages them to the user. + """ + proc = Popen(process, stdout=PIPE, stderr=PIPE, cwd=self.tempdir.name) + try: + out, err = proc.communicate(timeout=2) # 2 seconds is already FAR too long. + if proc.returncode != 0: + # Process errored + QMessageBox.warning(None, "LogarithmPlotter - Latex", + QCoreApplication.translate("latex", "An exception occured within the creation of the latex formula.\nProcess '{}' ended with a non-zero return code {}:\n\n{}\nPlease make sure your latex installation is correct and report a bug if so.") + .format(" ".join(process), proc.returncode, str(out, 'utf8')+"\n"+str(err,'utf8'))) + raise Exception(" ".join(process) + " process exited with return code " + str(proc.returncode) + ":\n" + str(out, 'utf8')+"\n"+str(err,'utf8')) + except TimeoutExpired as e: + # Process timed out + proc.kill() + out, err = proc.communicate() + QMessageBox.warning(None, "LogarithmPlotter - Latex", + QCoreApplication.translate("latex", "An exception occured within the creation of the latex formula.\nProcess '{}' took too long to finish:\n{}\nPlease make sure your latex installation is correct and report a bug if so.") + .format(" ".join(process), str(out, 'utf8')+"\n"+str(err,'utf8'))) + raise Exception(" ".join(process) + " process timed out:\n" + str(out, 'utf8')+"\n"+str(err,'utf8')) + + def cleanup(self, export_path): + """ + Removes auxiliary, logs and Tex temporary files. + """ + for i in [".tex", ".aux", ".log"]: + remove(export_path + i) + + + @Slot(str, float, QColor, result=str) + def render_legacy(self, latexstring, font_size, color = True): + exprpath = path.join(self.tempdir.name, f'{hash(latexstring)}_{font_size}_{color.rgb()}.png') + print("Rendering", latexstring, exprpath) + if not path.exists(exprpath): + fg = color.convertTo(QColor.Rgb) + fg = f'rgb {fg.redF()} {fg.greenF()} {fg.blueF()}' + preview('$${' + latexstring + '}$$', viewer='file', filename=exprpath, dvioptions=[ + "-T", "tight", + "-z", "0", + "--truecolor", + f"-D {int(font_size * 72.27 / 100) * 10}", # See https://linux.die.net/man/1/dvipng#-D for convertion + "-bg", "Transparent", + "-fg", fg], + euler=False) + img = QImage(exprpath); + # Small hack, not very optimized since we load the image twice, but you can't pass a QImage to QML and expect it to be loaded + return f'{exprpath},{img.width()},{img.height()}' diff --git a/runtime-pyside6/LogarithmPlotter/util/native.py b/LogarithmPlotter/util/native.py similarity index 74% rename from runtime-pyside6/LogarithmPlotter/util/native.py rename to LogarithmPlotter/util/native.py index 3adf153..2ac1b1b 100644 --- a/runtime-pyside6/LogarithmPlotter/util/native.py +++ b/LogarithmPlotter/util/native.py @@ -1,6 +1,6 @@ """ * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -18,38 +18,33 @@ # This file contains stuff for native interactions with each OS. -from PySide6.QtCore import QObject, QEvent - +from PySide2.QtCore import QObject, QEvent # On macOS, opening a file through finder can only be fetched through the -# QFileOpenEvent and NOT through command line parameters. +# QFileOpenEvent and NOT throught command line parameters. class MacOSFileOpenHandler(QObject): def __init__(self): - self.initialized = False - self.io_module = None + self.initilized = False + self.mainwindow = None self.opened_file = "" QObject.__init__(self) - - def init_io(self, io_modules): - self.io_module = io_modules - self.initialized = True + + def init_graphics(self, mainwindow): + self.mainwindow = mainwindow + self.initilized = True if self.opened_file != "": self.open_file() - + def open_file(self): - self.io_module.loadDiagram(self.opened_file) - + self.mainwindow.loadDiagram(self.opened_file) + def eventFilter(self, obj, event): if event.type() == QEvent.FileOpen: - print("Got file", event.file(), self.initialized) + print("Got file", event.file(), self.initilized) self.opened_file = event.file() - if self.initialized: + if self.initilized: self.open_file() return True else: # standard event processing return QObject.eventFilter(self, obj, event) - - - - diff --git a/runtime-pyside6/LogarithmPlotter/util/update.py b/LogarithmPlotter/util/update.py similarity index 73% rename from runtime-pyside6/LogarithmPlotter/util/update.py rename to LogarithmPlotter/util/update.py index e18b93e..5d8917e 100644 --- a/runtime-pyside6/LogarithmPlotter/util/update.py +++ b/LogarithmPlotter/util/update.py @@ -1,6 +1,6 @@ """ * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -16,16 +16,14 @@ * along with this program. If not, see . """ -from PySide6.QtCore import QRunnable, QThreadPool, QThread, QObject, Signal, QCoreApplication +from PySide2.QtCore import QRunnable, QThreadPool, QThread, QObject, Signal, QCoreApplication from urllib.request import urlopen from urllib.error import HTTPError, URLError from sys import argv - class UpdateInformation(QObject): got_update_info = Signal(bool, str, bool) - class UpdateCheckerRunnable(QRunnable): def __init__(self, current_version, callback): QRunnable.__init__(self) @@ -42,50 +40,44 @@ class UpdateCheckerRunnable(QRunnable): lines = r.readlines() r.close() # Parsing version - version = "".join(map(chr, lines[0])).strip() # Converts byte to string. + version = "".join(map(chr, lines[0])).strip() # Converts byte to string. version_tuple = version.split(".") is_version_newer = False if "dev" in self.current_version: # We're on a dev version - current_version_tuple = self.current_version.split(".")[:-1] # Removing the dev0+git bit. - is_version_newer = version_tuple >= current_version_tuple # If equals, that means we got out of testing phase. + current_version_tuple = self.current_version.split(".")[:-1] # Removing the dev0+git bit. + is_version_newer = version_tuple >= current_version_tuple # If equals, that means we got out of testing phase. else: current_version_tuple = self.current_version.split(".") is_version_newer = version_tuple > current_version_tuple if is_version_newer: - msg_text = QCoreApplication.translate("update", "An update for LogarithmPlotter (v{}) is available.") - msg_text = msg_text.format(version) + msg_text = QCoreApplication.translate("update","An update for LogarithPlotter (v{}) is available.").format(version) update_available = True else: show_alert = False - msg_text = QCoreApplication.translate("update", "No update available.") - + msg_text = QCoreApplication.translate("update","No update available.") + except HTTPError as e: - msg_text = QCoreApplication.translate("update", - "Could not fetch update information: Server error {}.") - msg_text = msg_text.format(str(e.code)) + msg_text = QCoreApplication.translate("update","Could not fetch update information: Server error {}.").format(str(e.code)) except URLError as e: - msg_text = QCoreApplication.translate("update", "Could not fetch update information: {}.") - msg_text = msg_text.format(str(e.reason)) - self.callback.got_update_info.emit(show_alert, msg_text, update_available) - + msg_text = QCoreApplication.translate("update","Could not fetch update information: {}.").format(str(e.reason)) + self.callback.got_update_info.emit(show_alert, msg_text,update_available) def check_for_updates(current_version, window): """ Checks for updates in the background, and sends an alert with information. """ if "--no-check-for-updates" in argv: - return - + return # def cb(show_alert, msg_text, update_available): + pass if show_alert: window.showAlert(msg_text) if update_available: window.showUpdateMenu() - + update_info = UpdateInformation() update_info.got_update_info.connect(cb) - + runnable = UpdateCheckerRunnable(current_version, update_info) QThreadPool.globalInstance().start(runnable) - return update_info diff --git a/runtime-pyside6/MANIFEST.in b/MANIFEST.in similarity index 100% rename from runtime-pyside6/MANIFEST.in rename to MANIFEST.in diff --git a/README.md b/README.md index f06d009..7c9e14f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -# ![icon](https://apps.ad5001.eu/icons/apps/svg/logarithmplotter.svg) LogarithmPlotter - +# ![icon](https://git.ad5001.eu/Ad5001/LogarithmPlotter/raw/branch/master/logplotter.svg) LogarithmPlotter [![Build Status](https://ci.ad5001.eu/api/badges/Ad5001/LogarithmPlotter/status.svg)](https://ci.ad5001.eu/Ad5001/LogarithmPlotter) [![Translation status](https://hosted.weblate.org/widgets/logarithmplotter/-/logarithmplotter/svg-badge.svg)](https://hosted.weblate.org/engage/logarithmplotter/) [![On flathub](https://img.shields.io/flathub/v/eu.ad5001.LogarithmPlotter?label=on%20flathub&logo=Flathub&logoColor=white&color=4A86CF)](https://flathub.org/apps/details/eu.ad5001.LogarithmPlotter) @@ -8,79 +7,56 @@ 2D plotter software to make Bode plots, sequences and distribution functions. ## Screenshots - ![Magnitude example](https://apps.ad5001.eu/img/full/logarithmplotter.png) ![Phase example](https://apps.ad5001.eu/img/en/logarithmplotter/phase.png) ![Object settings](https://apps.ad5001.eu/img/en/logarithmplotter/object-settings.webp) -You can find more screenshots on the [app's website](https://apps.ad5001.eu/logarithmplotter/). +You can find more screenshots on the [app website](https://apps.ad5001.eu/logarithmplotter/). -## Build & Run +## Run -First, you'll need to install all the required dependencies: +You can simply run LogarithmPlotter using `python3 run.py`. -- [Python 3](https://python.org) with [poetry](https://python-poetry.org/), setup a virtual environment, go to the `runtime-pyside6` directory, and call - `poetry install`. -- [npm](https://npmjs.com) (or [yarn](https://yarnpkg.com/)), go to the `common` directory, and run `npm install` (or `yarn install`). - -You can simply run LogarithmPlotter using `python3 run.py`. It automatically compiles the language files (requires -`pyside6-lrelease` to be installed and in path), and the JavaScript modules. - -If you do not wish do recompile the files again on every run, you can use the build script (`scripts/build.sh`) and run -`python3 build/runtime-pyside6/LogarithmPlotter/logarithmplotter.py`. - -In order to test translations, you can use the `--lang=` commandline option to force the locale. +In order to test translations, you can use the `--lang=` command line option to force the detected locale of LogarithmPlotter. ## Install ### Generate installers: - All scripts noted here can be found in the `scripts` directory. -You can generate installers for LogarithmPlotter after installing all the dependencies. - -- Windows installer (crosscompiling from Linux): - - Run `build-wine.sh` (requires wine) to build an exe for LogarithmPlotter in build/runtime-pyside6/dist. - - You also need [NSIS](https://nsis.sourceforge.io/Main_Page) (the [nsis](https://pkgs.org/download/nsis) package is available on linux). - - Run the `package-wine.sh` script. You will find a logarithmplotter-setup.exe installer in the build/runtime-pyside6/dist/logarithmplotter/ folder. -- MacOS Archive creator installer: - - Run the `build-macosx.sh` script to build an .app for LogarithmPlotter which can be found in the build/runtime-pyside6/dist directory. - - Run the `package-macosx.sh` script. You will find a LogarithmPlotter-v<version>-setup.dmg installer in the - build/runtime-pyside6/build/pysdist/ folder. +You can generate installers from LogarithmPlotter after installing all the dependencies: +For all builds, you need [Python 3](https://python.org) with [PySide2](https://pypi.org/project/PySide2/) installable with `pip install PySide2`. +- Windows installer: + - You need `pyinstaller`. You can install it using `pip install pyinstaller`. + - Run the `build-windows.bat` script (or `build-wine.sh` if you're cross-compiling with wine on Linux) to build an exe for LogarithmPlotter. + - You also need [NSIS](https://nsis.sourceforge.io/Main_Page) (Linux users can install the [nsis](https://pkgs.org/download/nsis) package). + - Run the `package-windows.bat` script (or `package-wine.sh`if you're cross-compiling on Linux). You will find a logarithmplotter-setup.exe installer in the dist/accountfree/ folder. +- MacOS Archive creator installer: + - You need `pyinstaller`. You can install it using `pip install pyinstaller`. + - Run the `build-macosx.sh` script to build an .app for LogarithmPlotter which can be found in the dist directory. + - Run the `package-macosx.sh` script. You will find a LogarithmPlotter-v0.1-dev-setup.dmg installer in the dist/ folder. - Linux packages: - - Run `package-deb.sh`. It will create an DSC and a DEB in build/runtime-pyside6/deb_dist/ - - Run `scripts/build.sh` followed by `snapcraft`. It .snap file in the root directory. - - See [the flatpak repo](https://github.com/Ad5001/eu.ad5001.LogarithmPlotter) for instrutions on how to build the flatpak. + - To build a DEB, you need DPKG and stdeb. You can install the later by using `pip install stdeb`. + - To build and install the flatpak, you need [flatpak-builder](https://docs.flatpak.org/en/latest/flatpak-builder.html) installed. + - To build the snap, you need [snapcraft](https://snapcraft.io) installed. + - Run `package-linux.sh`. + + +### Linux + +Run `bash linux/install_local.sh` ## Contribute -There are several ways you can contribute to LogarithmPlotter. - +There are several ways to contribute to LogarithmPlotter. - You can help to translate [the project on Hosted Weblate](https://hosted.weblate.org/engage/logarithmplotter/): - [![Translation status](https://hosted.weblate.org/widgets/logarithmplotter/-/logarithmplotter/multi-auto.svg)](https://hosted.weblate.org/engage/logarithmplotter/) +[![Translation status](https://hosted.weblate.org/widgets/logarithmplotter/-/logarithmplotter/multi-auto.svg)](https://hosted.weblate.org/engage/logarithmplotter/) -- You can help the development of LogarithmPlotter. In order to get started, take a look at - the [wiki](https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/_pages). - -## Tests - -To run LogarithmPlotter's tests, follow these steps: - -- Python - - Install python3 and [poetry](https://python-poetry.org/) - - Create and activate virtual env (recommended) - - Go into `runtime-pyside6` and run `poetry install --with test` -- ECMAScript - - Install node with npm - - Go into `common` and run `npm install -D` - -Finally, to actually run the tests: - - Run `scripts/run-tests.sh` +- You can help the development of LogarithmPlotter. In order to get started, take a look at the [wiki](https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/_pages). ## Legal notice - - LogarithmPlotter - 2D plotter software to make Bode plots, sequences and repartition functions. - Copyright (C) 2021-2025 Ad5001 + LogarithmPlotter - 2D plotter software to make BODE plots, sequences and repartition functions. + Copyright (C) 2022 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 @@ -95,19 +71,6 @@ Finally, to actually run the tests: You should have received a copy of the GNU General Public License along with this program. If not, see . -See LICENSE.md for more details. Language files translations located at assets/i18n are licensed under GNU GPL3.0+ and -are copyrighted by their original authors: - -- 🇭🇺 Hungarian translation by [Óvári](https://github.com/ovari) +Language files translations located at LogarithmPlotter/i18n are licensed under GNU GPL3.0+ and are copyrighted by their original authors. See LICENSE.md for more details: - 🇳🇴 Norwegian translation by [Allan Nordhøy](https://github.com/comradekingu) -- 🇪🇸 Spanish translation by gallegonovato and [IngrownMink4](https://github.com/IngrownMink4) - -### Libraries used - -LogarithmPlotter includes [expr-eval](https://github.com/silentmatt/expr-eval) a port -of [ndef.parser](https://web.archive.org/web/20111023001618/http://www.undefined.ch/mparser/index.html) by Raphael Graf -<r@undefined.ch>, ported to javascript by Matthew Crumley -<email@matthewcrumley.com> (http://silentmatt.com/), and then to QMLJS by Ad5001. - -All files in (common/src/lib/expr-eval/) except integration.mjs are licensed -under the [MIT License](https://raw.githubusercontent.com/silentmatt/expr-eval/master/LICENSE.txt). +- 🇭🇺 Hungarian translation by [Óvári](https://github.com/ovari) diff --git a/assets/i18n/lp_de.ts b/assets/i18n/lp_de.ts deleted file mode 100644 index 9ed0e8c..0000000 --- a/assets/i18n/lp_de.ts +++ /dev/null @@ -1,1996 +0,0 @@ - - - - - About - - - About LogarithmPlotter - Über LogarithmPlotter - - - - LogarithmPlotter v%1 - LogarithmPlotter v%1 - - - - 2D plotter software to make BODE plots, sequences and repartition functions. - 2D-Grafiksoftware zur Erstellung von Bode-Diagramms, Folgen und Verteilungsfunktionen. - - - - Report a bug - Bug melden - - - - Official website - Offizielle Website - - - - AppMenuBar - - - &File - &Datei - - - - &Load... - &Laden… - - - - &Save - &Speichern - - - - Save &As... - Speichern &Unter… - - - - &Quit - &Ausfahrt - - - - &Edit - &Bearbeiten - - - - &Undo - &Lösen - - - - &Redo - &Wiederherstellen - - - - &Copy plot - Grafik &Kopieren - - - - &Preferences - &Einstellung - - - - &Create - &Erstellen - - - &Settings - &Einstellungen - - - Check for updates on startup - Beim Starten auf Updates prüfen - - - Reset redo stack automaticly - Wiederherstellen-Stapel automatisch zurücksetzen - - - Enable LaTeX rendering - LaTeX-Rendering aktivieren - - - Expression editor - Ausdruckseditor - - - Automatically close parenthesises and brackets - Klammern automatisch schließen - - - Enable syntax highlighting - Syntaxhervorhebung einschalten - - - Enable autocompletion - Automatische Vervollständigung einschalten - - - Color Scheme - Syntaktische Färbung - - - - &Help - &Hilfe - - - - &Source code - &Quellcode - - - - &Report a bug - Fehler &Melden - - - - &User manual - &Benutzerhandbuch - - - - &Changelog - &Versionshinweise - - - - &Help translating! - &Hilfe beim Übersetzen! - - - - &Thanks - &Danksagungen - - - - &About - &Übrigens - - - - Save unsaved changes? - Änderungen speichern? - - - - This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? - Diese Grafik enthält ungespeicherte Änderungen. Dadurch gehen alle ungespeicherten Daten verloren. Fortfahren? - - - - BaseDialog - - - Close - Schließen - - - - BoolSetting - - Check for updates on startup - Beim Starten auf Updates prüfen - - - - Browser - - - Filter... - Filtern… - - - - Redo > - Wiederherstellen > - - - - > Now - > Aktueller Stand - - - - < Undo - < Rückgängig - - - - Changelog - - - Fetching changelog... - Changelog abrufen… - - - - Close - Schließen - - - - CustomPropertyList - - - - + Create new %1 - + Neues %1objekt erstellen - - - - Pick on graph - Aufnehmen auf Graph - - - - Dialog - - - Edit properties of %1 %2 - Eigenschaften von %1 %2 bearbeiten - - - - LogarithmPlotter - Invalid object name - LogarithmPlotter - Ungültiger Objektname - - - - An object with the name '%1' already exists. - Ein Objekt mit dem Namen '%1' existiert bereits. - - - - Name - Name - - - Label content - Etikett - - - - null - leer - - - - name - Name - - - - name + value - Name + Wert - - - - EditorDialog - - Edit properties of %1 %2 - Eigenschaften von %1 %2 bearbeiten - - - Name - Name - - - Label content - Etikett - - - null - leer - - - name - Name - - - name + value - Name + Wert - - - + Create new %1 - + Neues %1objekt erstellen - - - - ExpressionEditor - - - Object Properties - Objekteigenschaften - - - - Variables - Variablen - - - - Constants - Konstanten - - - - Functions - Funktionen - - - - Executable Objects - Funktionsobjekte - - - - Objects - Objekte - - - - FileDialog - - - Export Logarithm Plot file - Logarithmusgrafik exportieren - - - - Import Logarithm Plot file - Logarithmusgrafik importieren - - - - GreetScreen - - - Welcome to LogarithmPlotter - Willkommen bei LogarithmPlotter - - - - Version %1 - Version %1 - - - Take a few seconds to configure LogarithmPlotter. -These settings can be changed at any time from the "Settings" menu. - Nehmen Sie sich ein paar Sekunden Zeit, um LogarithmPlotter zu konfigurieren. -Diese Einstellungen können jederzeit über das Menü "Einstellungen" geändert werden. - - - Check for updates on startup (requires online connectivity) - Beim Start nach Updates suchen (Online-Verbindung erforderlich) - - - Reset redo stack when a new action is added to history - Redo-Stapel zurücksetzen, wenn eine neue Aktion zur Historie hinzugefügt wird - - - Enable LaTeX rendering - LaTeX-Rendering aktivieren - - - Automatically close parenthesises and brackets in expressions - Automatisches Schließen von Klammern in Ausdrücken - - - Enable syntax highlighting for expressions - Syntaxhervorhebung für Ausdrücke einschalten - - - Enable autocompletion interface in expression editor - Schnittstelle zur automatischen Vervollständigung im Ausdruckseditor aktivieren - - - Color scheme: - Syntaktische Färbung Thema: - - - - User manual - Benutzerhandbuch - - - - Changelog - Versionshinweise - - - - Preferences - Einstellung - - - - Close - Schließen - - - - HistoryBrowser - - Filter... - Filtern… - - - Redo > - Wiederherstellen > - - - > Now - > Aktueller Stand - - - < Undo - < Rückgängig - - - - ListSetting - - - + Add Entry - + Neuer Eintrag - - - - Loading - - - Loading... - Laden… - - - - Finished rendering of %1 - Beendetes Rendering von %1 - - - - LogarithmPlotter - - - untitled - unbetitelt - - - - Objects - Objekte - - - - Settings - Einstellungen - - - - History - Verlauf - - - Saved plot to '%1'. - Gespeicherte Grafik auf '%1'. - - - Loading file '%1'. - Laden der Datei '%1'. - - - Unknown object type: %1. - Unbekannter Objekttyp: %1. - - - Invalid file provided. - Ungültige Datei angegeben. - - - Could not save file: - Die Datei konnte nicht gespeichert werden: - - - Loaded file '%1'. - Geladene Datei '%1'. - - - - Copied plot screenshot to clipboard! - Grafik in die Zwischenablage kopiert! - - - - &Update - &Aktualisieren - - - - &Update LogarithmPlotter - LogarithmPlotter &aktualisieren - - - - ObjectCreationGrid - - - + Create new: - + Neu erstellen: - - - - ObjectLists - - - Hide all %1 - Alle %1 ausblenden - - - - Show all %1 - Alle %1 anzeigen - - - Hide %1 %2 - Ausblenden %1 %2 - - - Show %1 %2 - Anzeigen %1 %2 - - - Set %1 %2 position - Position von %1 %2 einstellen - - - Delete %1 %2 - %1 %2 löschen - - - Pick new color for %1 %2 - Neue Farbe für %1 %2 auswählen - - - - ObjectRow - - - Hide %1 %2 - Ausblenden %1 %2 - - - - Show %1 %2 - Anzeigen %1 %2 - - - - Set %1 %2 position - Position von %1 %2 einstellen - - - - Delete %1 %2 - %1 %2 löschen - - - - Pick new color for %1 %2 - Neue Farbe für %1 %2 auswählen - - - - PickLocation - - - Pointer precision: - Genauigkeit des Zeigers: - - - - Snap to grid: - Am Raster einrasten: - - - - Pick X - X nehmen - - - - Pick Y - Y nehmen - - - - Open picker settings - Zeigereinstellungen öffnen - - - - Hide picker settings - Zeigereinstellungen ausblenden - - - - (no pick selected) - (keine Auswahl ausgewählt) - - - - PickLocationOverlay - - Pointer precision: - Genauigkeit des Zeigers: - - - Snap to grid - Am Gitter einrasten - - - Snap to grid: - Am Raster einrasten: - - - Pick X - X nehmen - - - Pick Y - Y nehmen - - - Open picker settings - Zeigereinstellungen öffnen - - - Hide picker settings - Zeigereinstellungen ausblenden - - - (no pick selected) - (keine Auswahl ausgewählt) - - - - Preferences - - - Close - Schließen - - - - Settings - - - - X Zoom - Zoom auf X - - - - - Y Zoom - Zoom auf Y - - - - - Min X - Minimum X - - - - - Max Y - Maximum Y - - - - Max X - Maximum X - - - - Min Y - Minimum Y - - - - - X Axis Step - X-Achsen-Schritt - - - - - Y Axis Step - Y-Achsen-Schritt - - - - - Line width - Linienbreite - - - - - Text size (px) - Textgröße (px) - - - - - X Label - Etikett der X-Achse - - - - - Y Label - Etikett der Y-Achse - - - - - X Log scale - Logarithmische Skala in X - - - - - Show X graduation - X-Teilung anzeigen - - - - - Show Y graduation - Y-Teilung anzeigen - - - - Copy to clipboard - Kopieren in die Zwischenablage - - - - Save plot - Grafik speichern… - - - - Save plot as - Grafik speichern unter… - - - - Load plot - Grafik laden… - - - Close - Schließen - - - - ThanksTo - - - Thanks and Contributions - LogarithmPlotter - Danksagungen und Beiträge - LogarithmPlotter - - - - Source code - Quellcode - - - - Original library by Raphael Graf - Originalbibliothek von Raphael Graf - - - - Source - Quelle - - - - Ported to Javascript by Matthew Crumley - Portiert auf JavaScript von Matthew Crumley - - - - - - Website - Website - - - - Ported to QMLJS by Ad5001 - Portiert auf QMLJS von Ad5001 - - - - Libraries included - Einschließlich Bibliotheken - - - - Email - E-Mail - - - - English - Englisch - - - - French - Französisch - - - - German - Deutsch - - - - Hungarian - Ungarisch - - - - - - - Github - GitHub - - - - Norwegian - Norwegisch - - - - Spanish - Spanisch - - - - Tamil - Tamil - - - - Translations included - Einschließlich Übersetzungen - - - - Improve - Verbessern - - - - bodemagnitude - - - Bode Magnitude - Bode-Magnitude - - - - Bode Magnitudes - Bode-Magnituden - - - - - low-pass - Tiefpass - - - - - high-pass - Hochpass - - - - bodemagnitudesum - - - - Bode Magnitudes Sum - Bode-Magnituden Summe - - - - bodephase - - - Bode Phase - Bode-Phase - - - - Bode Phases - Bode-Phasen - - - - bodephasesum - - - - Bode Phases Sum - Bode-Phasen Summe - - - - changelog - - - Could not fetch changelog: Server error {}. - Changelog konnte nicht geholt werden: Server-Fehler {}. - - - - - Could not fetch update: {}. - Changelog konnte nicht geholt werden: {}. - - - - color - - - - %1 %2's color changed from %3 to %4. - %1 %2 wurde von %3 bis %4 umgefärbt. - - - - comment - - - Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} - Beispiel: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ-*), ]0;1[, {3;4;5} - - - - The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) - Die folgenden Parameter werden verwendet, wenn der Definitionsbereich eine nicht kontinuierliche Menge ist. (Beispiel: ℕ, ℤ, Mengen wie {0;3}...) - - - - Note: Specify the probability for each value. - Hinweis: Geben Sie die Wahrscheinlichkeit für jeden Wert an. - - - - Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... - Hinweis: Verwenden Sie %1[n], um sich auf %1ₙ zu beziehen, %1[n+1] für %1ₙ₊₁… - - - - If you have latex enabled, you can use use latex markup in between $$ to create equations. - Wenn Sie Latex aktiviert haben, können Sie Latex-Auszeichnungen zwischen $$ verwenden, um Gleichungen zu erstellen. - - - - control - - - - - - - %1: - %1: - - - - create - - - - New %1 %2 created. - Neu %1 %2 erstellt. - - - - delete - - - - %1 %2 deleted. - %1 %2 gelöscht. - - - - distribution - - - Repartition - Verteilungsfunktion - - - - Repartition functions - Verteilungsfunktionen - - - - editproperty - - - %1 of %2 %3 changed from "%4" to "%5". - %1 von %2 %3 wurde von "%4" auf "%5" geändert. - - - - %1 of %2 changed from %3 to %4. - %1 von %2 wurde von %3 auf %4 geändert. - - - - error - - - - Cannot find property %1 of object %2. - Eigenschaft %1 von Objekt %2 kann nicht gefunden werden. - - - - Undefined variable %1. - Die Variable %1 ist nicht definiert. - - - - In order to be executed, object %1 must have at least one argument. - Um als Funktion verwendet zu werden, benötigt das Objekt %1 mindestens ein Parameter. - - - - %1 cannot be executed. - %1 ist keine Formel. - - - - - - Invalid expression. - Ungültiger Ausdruck. - - - - Invalid expression (parity). - Ungültiger Ausdruck (Parität). - - - - Unknown character "%1". - Unbekanntes Schriftzeichen "%1". - - - - - Illegal escape sequence: %1. - Unzulässige Escapesequenz: %1. - - - Parse error [%1:%2]: %3 - Analysefehler [%1:%2]: %3 - - - - Expected %1 - Erwartet %1 - - - - Unexpected %1 - Unerwartetes %1 - - - Function definition is not permitted. - Funktionsdefinition ist nicht erlaubt. - - - Expected variable for assignment. - Erwartete Variable für Zuweisung. - - - - - Parse error [position %1]: %2 - Analysefehler [Posten %1]: %2 - - - - Unexpected ".": member access is not permitted - Unerwartetes ".": Mitgliederzugriff ist nicht erlaubt - - - - Unexpected "[]": arrays are disabled. - Unerwartetes "[]": Arrays sind deaktiviert. - - - - Unexpected symbol: %1. - Unerwartetes Symbol: %1. - - - - - Function %1 must have at least one argument. - Die Funktion %1 benötigt mindestens ein Parameter. - - - First argument to map is not a function. - Der erste Parameter von map ist keine Formel. - - - Second argument to map is not an array. - Der zweite Parameter von map ist kein Array. - - - First argument to fold is not a function. - Der erste Parameter für fold ist keine Formel. - - - Second argument to fold is not an array. - Der zweite Parameter für fold ist kein Array. - - - First argument to filter is not a function. - Der erste Parameter für filter ist keine Formel. - - - Second argument to filter is not an array. - Der zweite Parameter von filter ist kein Array. - - - Second argument to indexOf is not a string or array. - Der zweite Parameter von indexOf ist kein String oder Array. - - - Second argument to join is not an array. - Der zweite Parameter von join ist kein Array. - - - - EOF - Ende des Ausdrucks - - - - No object found with names %1. - Kein Objekt mit Namen %1 gefunden. - - - - No object found with name %1. - Kein Objekt mit dem Namen %1 gefunden. - - - - Object cannot be dependent on itself. - Ein Objekt kann nicht von sich selbst abhängen. - - - - Circular dependency detected. Object %1 depends on %2. - Zirkuläre Abhängigkeit entdeckt. Objekt %1 hängt von %2 ab. - - - - Circular dependency detected. Objects %1 depend on %2. - Zirkuläre Abhängigkeit entdeckt. Objekte %1 hängen von %2 ab. - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - Fehler beim Analysieren des Ausdrucks für die Eigenschaft %1: -%2 - -Ausdruck analysiert: %3 - - - - Error while attempting to draw %1 %2: -%3 - -Undoing last change. - Fehler beim Versuch, das %1 %2 zu zeichnen: -%3 - -Die letzte Änderung wurde rückgängig gemacht. - - - - expression - - - - LogarithmPlotter - Parsing error - LogarithmPlotter - Analysefehler - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - Fehler beim Analysieren des Ausdrucks für die Eigenschaft %1: -%2 - -Ausdruck analysiert: %3 - - - - LogarithmPlotter - Drawing error - LogarithmPlotter - Fehler - - - - Automatically close parenthesises and brackets - Klammern automatisch schließen - - - - Enable syntax highlighting - Syntaxhervorhebung einschalten - - - - Enable autocompletion - Automatische Vervollständigung einschalten - - - - Color Scheme - Syntaktische Färbung - - - - function - - - Function - Funktion - - - - Functions - Funktionen - - - - gainbode - - Bode Magnitude - Bode-Magnitude - - - Bode Magnitudes - Bode-Magnituden - - - low-pass - Tiefpass - - - high-pass - Hochpass - - - - general - - - Check for updates on startup - Beim Starten auf Updates prüfen - - - - Reset redo stack automaticly - Wiederherstellen-Stapel automatisch zurücksetzen - - - - Enable LaTeX rendering - LaTeX-Rendering aktivieren - - - - Enable threaded LaTeX renderer (experimental) - LaTeX-Renderer mit Threads aktivieren (experimentell) - - - - historylib - - New %1 %2 created. - Neu %1 %2 erstellt. - - - %1 %2 deleted. - %1 %2 gelöscht. - - - %1 of %2 %3 changed from "%4" to "%5". - %1 von %2 %3 wurde von "%4" auf "%5" geändert. - - - %1 %2 shown. - %1 %2 angezeigt. - - - %1 %2 hidden. - %1 %2 ausgeblendet. - - - Name of %1 %2 changed to %3. - Der Name von %1 %2 wurde in %3 geändert. - - - - io - - Objects - Objekte - - - Settings - Einstellungen - - - History - Verlauf - - - Saved plot to '%1'. - Gespeicherte Grafik auf '%1'. - - - Loading file '%1'. - Laden der Datei '%1'. - - - Unknown object type: %1. - Unbekannter Objekttyp: %1. - - - Invalid file provided. - Ungültige Datei angegeben. - - - Could not load file: - Datei konnte nicht geladen werden: - - - Could not save file: - Die Datei konnte nicht gespeichert werden: - - - Loaded file '%1'. - Geladene Datei '%1'. - - - Copied plot screenshot to clipboard! - Grafik in die Zwischenablage kopiert! - - - &Update - &Aktualisieren - - - &Update LogarithmPlotter - LogarithmPlotter &aktualisieren - - - - latex - - - No Latex installation found. -If you already have a latex distribution installed, make sure it's installed on your path. -Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. - Keine LaTeX-Installation gefunden. -Wenn Sie bereits eine LaTeX-Distribution installiert haben, vergewissern Sie sich, dass sie in Ihrem Pfad installiert ist. -Andernfalls können Sie eine LaTeX-Distribution wie TeX Live unter https://tug.org/texlive/ herunterladen. - - - - DVIPNG was not found. Make sure you include it from your Latex distribution. - DVIPNG wurde nicht gefunden. Stellen Sie sicher, dass Sie es aus Ihrer LaTeX-Distribution einbinden. - - - - An exception occured within the creation of the latex formula. -Process '{}' ended with a non-zero return code {}: - -{} -Please make sure your latex installation is correct and report a bug if so. - Bei der Erstellung der LaTeX-Formel ist eine Exception aufgetreten. -Der Prozess '{}' wurde mit einem Rückgabecode ungleich Null beendet {}: - -{} -Bitte vergewissern Sie sich, dass Ihre LaTeX-Installation korrekt ist, und melden Sie einen Fehler, falls dies der Fall ist. - - - - Your LaTeX installation does not include some required packages: - -- {} (https://ctan.org/pkg/{}) - -Make sure said package is installed, or disable the LaTeX rendering in LogarithmPlotter. - Ihre LaTeX-Installation enthält einige erforderliche Pakete nicht: - -- {} (https://ctan.org/pkg/{}) - -Stellen Sie sicher, dass diese Pakete installiert sind, oder deaktivieren Sie das LaTeX-Rendering in LogarithmPlotter. - - - - An exception occured within the creation of the latex formula. -Process '{}' took too long to finish: -{} -Please make sure your latex installation is correct and report a bug if so. - Bei der Erstellung der LaTeX-Formel ist eine Exception aufgetreten. -Der Prozess '{}' brauchte zu lange, um beendet zu werden: -{} -Bitte vergewissern Sie sich, dass Ihre LaTeX-Installation korrekt ist, und melden Sie einen Fehler, falls dies der Fall ist. - - - - main - - - This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}. -Please update LogarithmPlotter to open this file. - Diese Datei wurde mit einer neueren Version von LogarithmPlotter erstellt und kann nicht in LogarithmPlotter v{} zurückgeladen werden. -Bitte aktualisieren Sie LogarithmPlotter, um diese Datei zu öffnen. - - - - Could not open file "{}": -{} - Die Datei „{}“ konnte nicht geöffnet werden: -{} - - - - Could not open file: "{}" -File does not exist. - Die Datei „{}“ konnte nicht geöffnet werden: -Die Datei existiert nicht. - - - - Built with PySide6 (Qt) v{} and python v{} - Kompiliert mit PySide6 (Qt) v{} und python v{} - - - - name - - - - %1 %2 renamed to %3. - %1 %2 umbenannt in %3. - - - - parameters - - - above - ↑ Über - - - - below - ↓ Unter - - - - - left - ← Links - - - - - right - → Rechts - - - - above-left - ↖ Oben links - - - - above-right - ↗ Oben rechts - - - - below-left - ↙ Unten links - - - - below-right - ↘ Unten rechts - - - - center - >|< Zentrum - - - - top - ↑ Über - - - - bottom - ↓ Unter - - - - top-left - ↖ Oben links - - - - top-right - ↗ Oben rechts - - - - bottom-left - ↙ Unten links - - - - bottom-right - ↘ Unten rechts - - - - application - Anwendung - - - - function - Funktion - - - - high - Hoch - - - - low - Tief - - - - Next to target - Neben dem Ziel - - - - With label - Mit Etikett - - - - Hidden - Versteckt - - - - phasebode - - Bode Phase - Bode-Phase - - - Bode Phases - Bode-Phasen - - - - point - - - Point - Punkt - - - - Points - Punkte - - - - position - - - Position of %1 %2 set from "%3" to "%4". - %1 %2 wurde von "%3" nach "%4" verschoben. - - - - Position of %1 set from %2 to %3. - %1 wurde von %2 nach %3 verschoben. - - - - prop - - - expression - Ausdruck - - - - definitionDomain - Definitionsbereich - - - - destinationDomain - Reichweite - - - - - - - - - - - - - labelPosition - Position des Etiketts - - - - displayMode - Anzeigemodus - - - - - - - - - - labelX - X-Position des Etiketts - - - - - drawPoints - Unentschiedene Punkte - - - - - drawDashedLines - Gestrichelte Linien anzeigen - - - - - om_0 - ω₀ - - - - pass - Pass - - - - gain - Größenordnung - - - - omGraduation - Teilung auf ω zeigen - - - - phase - Phase - - - - unit - Einheit - - - - - - x - X - - - - - y - Y - - - - pointStyle - Punkt-Stil - - - - probabilities - Wahrscheinlichkeiten - - - - text - Inhalt - - - - disableLatex - LaTeX-Rendering für diesen Text deaktivieren - - - - targetElement - Zielobjekt - - - - approximate - Berechneten Wert gerundet anzeigen - - - - rounding - Rundung - - - - displayStyle - Stil - - - - targetValuePosition - Wertposition des Ziels - - - - defaultExpression - Standardausdruck - - - - baseValues - Initialisierungswerte - - - color - Farbe - - - - labelContent - Etikett - - - - repartition - - Repartition - Verteilungsfunktion - - - Repartition functions - Verteilungsfunktionen - - - - sequence - - - Sequence - Folge - - - - Sequences - Folgen - - - - settingCategory - - - default - Standardeinstellungen - - - - general - Allgemeine - - - - editor - Ausdruckseditor - - - - sommegainsbode - - Bode Magnitudes Sum - Bode-Magnituden Summe - - - - sommephasesbode - - Bode Phases Sum - Bode-Phasen Summe - - - - text - - - Text - Text - - - - Texts - Texte - - - - update - - - An update for LogarithmPlotter (v{}) is available. - Ein Update für LogarithmPlotter (v{}) ist verfügbar. - - - - No update available. - Keine Aktualisierung verfügbar. - - - - Could not fetch update information: Server error {}. - Es konnten keine Aktualisierungsinformationen abgerufen werden: Server-Fehler {}. - - - - Could not fetch update information: {}. - Es konnten keine Aktualisierungsinformationen abgerufen werden:{}. - - - - usage - - - integral(<from: number>, <to: number>, <f: ExecutableObject>) - integral(<von: Zahl>, <bis: Zahl>, <f: Funktionsähnliches Objekt>) - - - - - Usage: -%1 - Verwendung: -%1 - - - - - - Usage: -%1 -%2 - Verwendung: -%1 -%2 - - - - integral(<from: number>, <to: number>, <f: string>, <variable: string>) - integral(<von: Zahl>, <bis: Zahl>, <f: String>, <Variablen: String>) - - - - derivative(<f: ExecutableObject>, <x: number>) - derivative(<f: Funktionsähnliches Objekt>, <x: Zahl>) - - - - derivative(<f: string>, <variable: string>, <x: number>) - derivative(<f: String>, <Variablen: String>, <x: Zahl>) - - - - visibility - - - - %1 %2 shown. - %1 %2 angezeigt. - - - - - %1 %2 hidden. - %1 %2 ausgeblendet. - - - - xcursor - - - X Cursor - X Zeiger - - - - X Cursors - X Zeiger - - - diff --git a/assets/i18n/lp_en.ts b/assets/i18n/lp_en.ts deleted file mode 100644 index 68590af..0000000 --- a/assets/i18n/lp_en.ts +++ /dev/null @@ -1,1996 +0,0 @@ - - - - - About - - - About LogarithmPlotter - About LogarithmPlotter - - - - LogarithmPlotter v%1 - LogarithmPlotter v%1 - - - - 2D plotter software to make BODE plots, sequences and repartition functions. - 2D plotter software to make Bode plots, sequences and distribution functions. - - - - Report a bug - Report a bug - - - - Official website - Official website - - - - AppMenuBar - - - &File - &File - - - - &Load... - &Open… - - - - &Save - &Save - - - - Save &As... - Save &As… - - - - &Quit - &Quit - - - - &Edit - &Edit - - - - &Undo - &Undo - - - - &Redo - &Redo - - - - &Copy plot - &Copy plot - - - - &Preferences - &Preferences - - - - &Create - &Create - - - &Settings - &Settings - - - Check for updates on startup - Check for updates on startup - - - Reset redo stack automaticly - Reset redo stack automatically - - - Enable LaTeX rendering - Enable LaTeX rendering - - - Expression editor - Expression editor - - - Automatically close parenthesises and brackets - Automatically close parentheses and brackets - - - Enable syntax highlighting - Enable syntax highlighting - - - Enable autocompletion - Enable autocompletion - - - Color Scheme - Color Scheme - - - - &Help - &Help - - - - &Source code - &Source code - - - - &Report a bug - &Report a bug - - - - &User manual - &User manual - - - - &Changelog - &Changelog - - - - &Help translating! - &Help translating! - - - - &Thanks - &Thanks - - - - &About - &About - - - - Save unsaved changes? - Save unsaved changes? - - - - This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? - This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? - - - - BaseDialog - - - Close - Close - - - - BoolSetting - - Check for updates on startup - Check for updates on startup - - - - Browser - - - Filter... - Filter… - - - - Redo > - Redo > - - - - > Now - > Now - - - - < Undo - < Undo - - - - Changelog - - - Fetching changelog... - Fetching changelog… - - - - Close - Close - - - - CustomPropertyList - - - - + Create new %1 - + Create new %1 - - - - Pick on graph - Pick on graph - - - - Dialog - - - Edit properties of %1 %2 - Edit properties of %1 %2 - - - - LogarithmPlotter - Invalid object name - LogarithmPlotter - Invalid object name - - - - An object with the name '%1' already exists. - An object with the name '%1' already exists. - - - - Name - Name - - - Label content - Label content - - - - null - null - - - - name - name - - - - name + value - name + value - - - - EditorDialog - - Edit properties of %1 %2 - Edit properties of %1 %2 - - - Name - Name - - - Label content - Label content - - - null - null - - - name - name - - - name + value - name + value - - - + Create new %1 - + Create new %1 - - - - ExpressionEditor - - - Object Properties - Object Properties - - - - Variables - Variables - - - - Constants - Constants - - - - Functions - Functions - - - - Executable Objects - Function Objects - - - - Objects - Objects - - - - FileDialog - - - Export Logarithm Plot file - Export Logarithm Plot file - - - - Import Logarithm Plot file - Import Logarithm Plot file - - - - GreetScreen - - - Welcome to LogarithmPlotter - Welcome to LogarithmPlotter - - - - Version %1 - Version %1 - - - Take a few seconds to configure LogarithmPlotter. -These settings can be changed at any time from the "Settings" menu. - Take a few seconds to configure LogarithmPlotter. -These settings can be changed at any time from the "Settings" menu. - - - Check for updates on startup (requires online connectivity) - Check for updates on startup (requires online connectivity) - - - Reset redo stack when a new action is added to history - Reset redo stack when a new action is added to history - - - Enable LaTeX rendering - Enable LaTeX rendering - - - Automatically close parenthesises and brackets in expressions - Automatically close parentheses and brackets in expressions - - - Enable syntax highlighting for expressions - Enable syntax highlighting for expressions - - - Enable autocompletion interface in expression editor - Enable autocompletion interface in expression editor - - - Color scheme: - Color scheme: - - - - User manual - User manual - - - - Changelog - Changelog - - - - Preferences - Preferences - - - - Close - Close - - - - HistoryBrowser - - Filter... - Filter… - - - Redo > - Redo > - - - > Now - > Now - - - < Undo - < Undo - - - - ListSetting - - - + Add Entry - + Add Entry - - - - Loading - - - Loading... - Loading… - - - - Finished rendering of %1 - Finished rendering of %1 - - - - LogarithmPlotter - - - untitled - untitled - - - - Objects - Objects - - - - Settings - Settings - - - - History - History - - - Saved plot to '%1'. - Saved plot to '%1'. - - - Loading file '%1'. - Loading file '%1'. - - - Unknown object type: %1. - Unknown object type: %1. - - - Invalid file provided. - Invalid file provided. - - - Could not save file: - Could not save file: - - - Loaded file '%1'. - Loaded file '%1'. - - - - Copied plot screenshot to clipboard! - Copied plot screenshot to clipboard! - - - - &Update - &Update - - - - &Update LogarithmPlotter - &Update LogarithmPlotter - - - - ObjectCreationGrid - - - + Create new: - + Create new: - - - - ObjectLists - - - Hide all %1 - Hide all %1 - - - - Show all %1 - Show all %1 - - - Hide %1 %2 - Hide %1 %2 - - - Show %1 %2 - Show %1 %2 - - - Set %1 %2 position - Set %1 %2 position - - - Delete %1 %2 - Delete %1 %2 - - - Pick new color for %1 %2 - Pick new color for %1 %2 - - - - ObjectRow - - - Hide %1 %2 - Hide %1 %2 - - - - Show %1 %2 - Show %1 %2 - - - - Set %1 %2 position - Set %1 %2 position - - - - Delete %1 %2 - Delete %1 %2 - - - - Pick new color for %1 %2 - Pick new color for %1 %2 - - - - PickLocation - - - Pointer precision: - Pointer precision: - - - - Snap to grid: - Snap to grid: - - - - Pick X - Pick X - - - - Pick Y - Pick Y - - - - Open picker settings - Open picker settings - - - - Hide picker settings - Hide picker settings - - - - (no pick selected) - (no pick selected) - - - - PickLocationOverlay - - Pointer precision: - Pointer precision: - - - Snap to grid - Snap to grid - - - Snap to grid: - Snap to grid: - - - Pick X - Pick X - - - Pick Y - Pick Y - - - Open picker settings - Open picker settings - - - Hide picker settings - Hide picker settings - - - (no pick selected) - (no pick selected) - - - - Preferences - - - Close - Close - - - - Settings - - - - X Zoom - X Zoom - - - - - Y Zoom - Y Zoom - - - - - Min X - Min X - - - - - Max Y - Max Y - - - - Max X - Max X - - - - Min Y - Min Y - - - - - X Axis Step - X Axis Step - - - - - Y Axis Step - Y Axis Step - - - - - Line width - Line width - - - - - Text size (px) - Text size (px) - - - - - X Label - X Label - - - - - Y Label - Y Label - - - - - X Log scale - X Log scale - - - - - Show X graduation - Show X graduation - - - - - Show Y graduation - Show Y graduation - - - - Copy to clipboard - Copy to clipboard - - - - Save plot - Save plot… - - - - Save plot as - Save plot as… - - - - Load plot - Open plot… - - - Close - Close - - - - ThanksTo - - - Thanks and Contributions - LogarithmPlotter - Thanks and Contributions - LogarithmPlotter - - - - Source code - Source code - - - - Original library by Raphael Graf - Original library by Raphael Graf - - - - Source - Source - - - - Ported to Javascript by Matthew Crumley - Ported to JavaScript by Matthew Crumley - - - - - - Website - Website - - - - Ported to QMLJS by Ad5001 - Ported to QMLJS by Ad5001 - - - - Libraries included - Libraries included - - - - Email - Email - - - - English - English - - - - French - French - - - - German - German - - - - Hungarian - Hungarian - - - - - - - Github - GitHub - - - - Norwegian - Norwegian - - - - Spanish - Spanish - - - - Tamil - Tamil - - - - Translations included - Translations included - - - - Improve - Improve - - - - bodemagnitude - - - Bode Magnitude - Bode Magnitude - - - - Bode Magnitudes - Bode Magnitudes - - - - - low-pass - low-pass - - - - - high-pass - high-pass - - - - bodemagnitudesum - - - - Bode Magnitudes Sum - Bode Magnitudes Sum - - - - bodephase - - - Bode Phase - Bode Phase - - - - Bode Phases - Bode Phases - - - - bodephasesum - - - - Bode Phases Sum - Bode Phases Sum - - - - changelog - - - Could not fetch changelog: Server error {}. - Could not fetch changelog: Server error {}. - - - - - Could not fetch update: {}. - Could not fetch changelog: {}. - - - - color - - - - %1 %2's color changed from %3 to %4. - %1 %2's color changed from %3 to %4. - - - - comment - - - Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} - Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} - - - - The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) - The following parameters are used when the domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}…) - - - - Note: Specify the probability for each value. - Note: Specify the probability for each value. - - - - Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... - Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁… - - - - 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. - - - - control - - - - - - - %1: - %1: - - - - create - - - - New %1 %2 created. - New %1 %2 created. - - - - delete - - - - %1 %2 deleted. - %1 %2 deleted. - - - - distribution - - - Repartition - Distribution - - - - Repartition functions - Distribution functions - - - - editproperty - - - %1 of %2 %3 changed from "%4" to "%5". - %1 of %2 %3 changed from "%4" to "%5". - - - - %1 of %2 changed from %3 to %4. - %1 of %2 changed from %3 to %4. - - - - error - - - - Cannot find property %1 of object %2. - Cannot find property %1 of object %2. - - - - Undefined variable %1. - Undefined variable %1. - - - - In order to be executed, object %1 must have at least one argument. - In order to be executed, object %1 must have at least one argument. - - - - %1 cannot be executed. - %1 is not a function. - - - - - - Invalid expression. - Invalid expression. - - - - Invalid expression (parity). - Invalid expression (parity). - - - - Unknown character "%1". - Unknown character "%1". - - - - - Illegal escape sequence: %1. - Illegal escape sequence: %1. - - - Parse error [%1:%2]: %3 - Parse error [%1:%2]: %3 - - - - Expected %1 - Expected %1 - - - - Unexpected %1 - Unexpected %1 - - - Function definition is not permitted. - Function definition is not permitted. - - - Expected variable for assignment. - Expected variable for assignment. - - - - - Parse error [position %1]: %2 - Parse error [position %1]: %2 - - - - Unexpected ".": member access is not permitted - Unexpected ".": member access is not permitted - - - - Unexpected "[]": arrays are disabled. - Unexpected "[]": arrays are disabled. - - - - Unexpected symbol: %1. - Unexpected symbol: %1. - - - - - Function %1 must have at least one argument. - Function %1 must have at least one argument. - - - First argument to map is not a function. - First argument to map is not a function. - - - Second argument to map is not an array. - Second argument to map is not an array. - - - First argument to fold is not a function. - First argument to fold is not a function. - - - Second argument to fold is not an array. - Second argument to fold is not an array. - - - First argument to filter is not a function. - First argument to filter is not a function. - - - Second argument to filter is not an array. - Second argument to filter is not an array. - - - Second argument to indexOf is not a string or array. - Second argument to indexOf is not a string or array. - - - Second argument to join is not an array. - Second argument to join is not an array. - - - - EOF - End of expression - - - - No object found with names %1. - No object found with names %1. - - - - No object found with name %1. - No object found with name %1. - - - - Object cannot be dependent on itself. - Object cannot be dependent on itself. - - - - Circular dependency detected. Object %1 depends on %2. - Circular dependency detected. Object %1 depends on %2. - - - - Circular dependency detected. Objects %1 depend on %2. - Circular dependency detected. Objects %1 depend on %2. - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - - - - Error while attempting to draw %1 %2: -%3 - -Undoing last change. - Error while attempting to draw %1 %2: -%3 - -Undoing last change. - - - - expression - - - - LogarithmPlotter - Parsing error - LogarithmPlotter - Parsing error - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - - - - LogarithmPlotter - Drawing error - LogarithmPlotter - Drawing error - - - - Automatically close parenthesises and brackets - Automatically close parentheses and brackets - - - - Enable syntax highlighting - Enable syntax highlighting - - - - Enable autocompletion - Enable autocompletion - - - - Color Scheme - Color Scheme - - - - function - - - Function - Function - - - - Functions - Functions - - - - gainbode - - Bode Magnitude - Bode Magnitude - - - Bode Magnitudes - Bode Magnitudes - - - low-pass - low-pass - - - high-pass - high-pass - - - - general - - - Check for updates on startup - Check for updates on startup - - - - Reset redo stack automaticly - Reset redo stack automatically - - - - Enable LaTeX rendering - Enable LaTeX rendering - - - - Enable threaded LaTeX renderer (experimental) - Enable threaded LaTeX renderer (experimental) - - - - historylib - - New %1 %2 created. - New %1 %2 created. - - - %1 %2 deleted. - %1 %2 deleted. - - - %1 of %2 %3 changed from "%4" to "%5". - %1 of %2 %3 changed from "%4" to "%5". - - - %1 %2 shown. - %1 %2 shown. - - - %1 %2 hidden. - %1 %2 hidden. - - - Name of %1 %2 changed to %3. - Name of %1 %2 changed to %3. - - - - io - - Objects - Objects - - - Settings - Settings - - - History - History - - - Saved plot to '%1'. - Saved plot to '%1'. - - - Loading file '%1'. - Loading file '%1'. - - - Unknown object type: %1. - Unknown object type: %1. - - - Invalid file provided. - Invalid file provided. - - - Could not load file: - Could not load file: - - - Could not save file: - Could not save file: - - - Loaded file '%1'. - Loaded file '%1'. - - - Copied plot screenshot to clipboard! - Copied plot screenshot to clipboard! - - - &Update - &Update - - - &Update LogarithmPlotter - &Update LogarithmPlotter - - - - latex - - - No Latex installation found. -If you already have a latex distribution installed, make sure it's installed on your path. -Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. - No LaTeX installation found. -If you already have a LaTeX distribution installed, make sure it's installed on your path. -Otherwise, you can download a LaTeX distribution like TeX Live at https://tug.org/texlive/. - - - - DVIPNG was not found. Make sure you include it from your Latex distribution. - DVIPNG was not found. Make sure you include it from your LaTeX distribution. - - - - An exception occured within the creation of the latex formula. -Process '{}' ended with a non-zero return code {}: - -{} -Please make sure your latex installation is correct and report a bug if so. - An exception occurred within the creation of the LaTeX formula. -Process '{}' ended with a non-zero return code {}: - -{} -Please make sure your LaTeX installation is correct and report a bug if so. - - - - Your LaTeX installation does not include some required packages: - -- {} (https://ctan.org/pkg/{}) - -Make sure said package is installed, or disable the LaTeX rendering in LogarithmPlotter. - Your LaTeX installation does not include some required packages: - -- {} (https://ctan.org/pkg/{}) - -Make sure said package is installed, or disable the LaTeX rendering in LogarithmPlotter. - - - - An exception occured within the creation of the latex formula. -Process '{}' took too long to finish: -{} -Please make sure your latex installation is correct and report a bug if so. - An exception occurred within the creation of the LaTeX formula. -Process '{}' took too long to finish: -{} -Please make sure your LaTeX installation is correct and report a bug if so. - - - - main - - - This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}. -Please update LogarithmPlotter to open this file. - This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}. -Please update LogarithmPlotter to open this file. - - - - Could not open file "{}": -{} - Could not open file "{}": -{} - - - - Could not open file: "{}" -File does not exist. - Could not open file: "{}" -File does not exist. - - - - Built with PySide6 (Qt) v{} and python v{} - Built with PySide6 (Qt) v{} and python v{} - - - - name - - - - %1 %2 renamed to %3. - %1 %2 renamed to %3. - - - - parameters - - - above - ↑ Above - - - - below - ↓ Below - - - - - left - ← Left - - - - - right - → Right - - - - above-left - ↖ Above left - - - - above-right - ↗ Above right - - - - below-left - ↙ Below left - - - - below-right - ↘ Below right - - - - center - >|< Center - - - - top - ↑ Top - - - - bottom - ↓ Bottom - - - - top-left - ↖ Top left - - - - top-right - ↗ Top right - - - - bottom-left - ↙ Bottom left - - - - bottom-right - ↘ Bottom right - - - - application - Application - - - - function - Function - - - - high - High - - - - low - Low - - - - Next to target - Next to target - - - - With label - With label - - - - Hidden - Hidden - - - - phasebode - - Bode Phase - Bode Phase - - - Bode Phases - Bode Phases - - - - point - - - Point - Point - - - - Points - Points - - - - position - - - Position of %1 %2 set from "%3" to "%4". - %1 %2 moved from "%3" to "%4". - - - - Position of %1 set from %2 to %3. - %1 moved from %2 to %3. - - - - prop - - - expression - Expression - - - - definitionDomain - Domain - - - - destinationDomain - Range - - - - - - - - - - - - - labelPosition - Label position - - - - displayMode - Display mode - - - - - - - - - - labelX - Label's X position - - - - - drawPoints - Show points - - - - - drawDashedLines - Show dashed lines - - - - - om_0 - ω₀ - - - - pass - Pass - - - - gain - Magnitude gain - - - - omGraduation - Show graduation on ω₀ - - - - phase - Phase - - - - unit - Unit to use - - - - - - x - X - - - - - y - Y - - - - pointStyle - Point style - - - - probabilities - Probabilities list - - - - text - Content - - - - disableLatex - Disable LaTeX rendering for this text - - - - targetElement - Object to target - - - - approximate - Show rounded calculated value - - - - rounding - Rounding - - - - displayStyle - Display style - - - - targetValuePosition - Target's value position - - - - defaultExpression - Default expression - - - - baseValues - Initialization values - - - color - Color - - - - labelContent - Label content - - - - repartition - - Repartition - Distribution - - - Repartition functions - Distribution functions - - - - sequence - - - Sequence - Sequence - - - - Sequences - Sequences - - - - settingCategory - - - general - General - - - - editor - Expression Editor - - - - default - Default settings - - - - sommegainsbode - - Bode Magnitudes Sum - Bode Magnitudes Sum - - - - sommephasesbode - - Bode Phases Sum - Bode Phases Sum - - - - text - - - Text - Text - - - - Texts - Texts - - - - update - - - An update for LogarithmPlotter (v{}) is available. - An update for LogarithmPlotter (v{}) is available. - - - - No update available. - No update available. - - - - Could not fetch update information: Server error {}. - Could not fetch update information: Server error {}. - - - - Could not fetch update information: {}. - Could not fetch update information: {}. - - - - usage - - - integral(<from: number>, <to: number>, <f: ExecutableObject>) - integral(<from: number>, <to: number>, <f: Function-like object>) - - - - - Usage: -%1 - Usage: -%1 - - - - - - Usage: -%1 -%2 - Usage: -%1 -%2 - - - - integral(<from: number>, <to: number>, <f: string>, <variable: string>) - integral(<from: number>, <to: number>, <f: string>, <variable: string>) - - - - derivative(<f: ExecutableObject>, <x: number>) - derivative(<f: Function-like object>, <x: number>) - - - - derivative(<f: string>, <variable: string>, <x: number>) - derivative(<f: string>, <variable: string>, <x: number>) - - - - visibility - - - - %1 %2 shown. - %1 %2 shown. - - - - - %1 %2 hidden. - %1 %2 hidden. - - - - xcursor - - - X Cursor - X Cursor - - - - X Cursors - X Cursors - - - diff --git a/assets/i18n/lp_es.ts b/assets/i18n/lp_es.ts deleted file mode 100644 index b1e5d0a..0000000 --- a/assets/i18n/lp_es.ts +++ /dev/null @@ -1,1985 +0,0 @@ - - - - - About - - - About LogarithmPlotter - Sobre LogarithmPlotter - - - - LogarithmPlotter v%1 - LogarithmPlotter v%1 - - - - 2D plotter software to make BODE plots, sequences and repartition functions. - Software de trazado 2D para realizar gráficos de Bode, secuencias y funciones de distribución. - - - - Report a bug - Informar de un error - - - - Official website - Sitio web oficial - - - - AppMenuBar - - - &File - &Archivo - - - - &Load... - &Abrir… - - - - &Save - &Guardar - - - - Save &As... - Guardar &como… - - - - &Quit - &Salida - - - - &Edit - &Editar - - - - &Undo - &Cancelar - - - - &Redo - &Reiniciar - - - - &Copy plot - &Copiar el gráfico - - - - &Preferences - &Preferencias - - - - &Create - &Crear - - - &Settings - &Ajustes - - - Check for updates on startup - Comprobación de las actualizaciones al arrancar - - - Reset redo stack automaticly - Restablecer la pila de rehacer automáticamente - - - Enable LaTeX rendering - Activar el renderizado de LaTeX - - - Automatically close parenthesises and brackets - Cerrar automáticamente paréntesis y corchetes - - - Enable syntax highlighting - Activar el resaltado sintáctico - - - Enable autocompletion - Activar autocompletar - - - Color Scheme - Esquema de colores - - - - &Help - &Ayuda - - - - &Source code - &Código fuente - - - - &Report a bug - &Informar de un error - - - - &User manual - &Manual del usuario - - - - &Changelog - &Registro de cambios - - - - &Help translating! - &¡Ayuda a la traducción! - - - - &Thanks - &Agradecimientos - - - - &About - &Acerca de - - - - Save unsaved changes? - ¿Guardar los cambios no guardados? - - - - This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? - Este gráfico contiene cambios sin guardar. Al hacer esto, se perderán todos los datos no guardados. ¿Continuar? - - - Expression editor - Editor de expresiones - - - - BaseDialog - - - Close - Cerrar - - - - BoolSetting - - Check for updates on startup - Comprobación de las actualizaciones al arrancar - - - - Browser - - - Filter... - - - - - Redo > - Rehacer > - - - - > Now - > Ahora - - - - < Undo - < Deshacer - - - - Changelog - - - Fetching changelog... - Obteniendo el registro de cambios… - - - - Close - Cerrar - - - - CustomPropertyList - - - - + Create new %1 - + Crear nuevo %1 - - - - Pick on graph - Elegir en el gráfico - - - - Dialog - - - Edit properties of %1 %2 - Editar las propiedades de %1 %2 - - - - LogarithmPlotter - Invalid object name - LogarithmPlotter - Nombre de objeto no válido - - - - An object with the name '%1' already exists. - Ya existe un objeto con el nombre '%1'. - - - - Name - - - - - null - - - - - name - - - - - name + value - nombre + valor - - - - EditorDialog - - Label content - Contenido de la etiqueta - - - null - nulo - - - name - nombre - - - name + value - nombre + valor - - - + Create new %1 - + Crear nuevo %1 - - - Edit properties of %1 %2 - Editar propiedades de %1 %2 - - - Name - Nombre - - - - ExpressionEditor - - - Object Properties - Propiedades de los objetos - - - - Variables - - - - - Constants - Constantes - - - - Functions - Funciones - - - - Executable Objects - Objetos de función - - - - Objects - Objetos - - - - FileDialog - - - Export Logarithm Plot file - Exportar archivo de gráfico de logaritmos - - - - Import Logarithm Plot file - Importar archivo de gráfico de logaritmos - - - - GreetScreen - - - Welcome to LogarithmPlotter - Bienvenid@ a LogarithmPlotter - - - - Version %1 - - - - Enable LaTeX rendering - Activar el renderizado de LaTeX - - - - User manual - - - - - Changelog - Registro de cambios - - - - Preferences - Preferencias - - - - Close - Cerrar - - - Color scheme: - Esquema de colores: - - - Take a few seconds to configure LogarithmPlotter. -These settings can be changed at any time from the "Settings" menu. - Tómate unos segundos para configurar LogarithmPlotter. -Estos ajustes se pueden cambiar en cualquier momento desde el menú “Ajustes”. - - - Check for updates on startup (requires online connectivity) - Buscar actualizaciones al iniciar (requiere conexión a Internet) - - - Reset redo stack when a new action is added to history - Restablecer el historial de deshacer cuando se agrega una nueva acción - - - Automatically close parenthesises and brackets in expressions - Cerrar automáticamente paréntesis y corchetes en expresiones - - - Enable autocompletion interface in expression editor - Habilitar el autocompletado en el editor de expresiones - - - Enable syntax highlighting for expressions - Habilitar el resaltado de sintaxis para expresiones - - - - HistoryBrowser - - Redo > - Rehacer > - - - > Now - > Ahora - - - < Undo - < Deshacer - - - - ListSetting - - - + Add Entry - + Añadir entrada - - - - Loading - - - Loading... - - - - - Finished rendering of %1 - - - - - LogarithmPlotter - - - untitled - - - - - Objects - Objetos - - - - Settings - Ajustes - - - - History - Historial - - - - Copied plot screenshot to clipboard! - ¡Captura de pantalla del gráfico copiada al portapapeles! - - - - &Update - &Actualizar - - - - &Update LogarithmPlotter - &Actualizar LogarithmPlotter - - - Unknown object type: %1. - Tipo de objeto desconocido: %1 . - - - Saved plot to '%1'. - Gráfico guardado en '%1'. - - - Loading file '%1'. - Cargando el archivo '%1'. - - - Invalid file provided. - Se ha proporcionado un archivo no válido. - - - Could not save file: - No se ha podido guardar el archivo: - - - Loaded file '%1'. - Archivo cargado '%1'. - - - - ObjectCreationGrid - - - + Create new: - + Crear nuevo: - - - - ObjectLists - - - Hide all %1 - Ocultar todo %1 - - - - Show all %1 - Mostrar todo %1 - - - Delete %1 %2 - Borrar %1 %2 - - - Hide %1 %2 - Ocultar %1 %2 - - - Show %1 %2 - Mostrar %1 %2 - - - Set %1 %2 position - Fijar la posición %1 %2 - - - Pick new color for %1 %2 - Elegir nuevo color para %1 %2 - - - - ObjectRow - - - Hide %1 %2 - Ocultar %1 %2 - - - - Show %1 %2 - Mostrar %1 %2 - - - - Set %1 %2 position - - - - - Delete %1 %2 - - - - - Pick new color for %1 %2 - Elegir nuevo color para %1 %2 - - - - PickLocation - - - Pointer precision: - Precisión del puntero: - - - - Snap to grid: - Ajustar a la cuadrícula: - - - - Pick X - Elige X - - - - Pick Y - Elige Y - - - - Open picker settings - Abrir los ajustes del selector - - - - Hide picker settings - Ocultar ajustes del selector - - - - (no pick selected) - (sin selección) - - - - PickLocationOverlay - - Pointer precision: - Precisión del puntero: - - - Snap to grid: - Ajustar a la cuadrícula: - - - Pick X - Elige X - - - Pick Y - Elige Y - - - Open picker settings - Abrir los ajustes del selector - - - Hide picker settings - Ocultar ajustes del selector - - - (no pick selected) - (sin selección) - - - Snap to grid - Ajustar a la cuadrícula - - - - Preferences - - - Close - Cerrar - - - - Settings - - - - X Zoom - - - - - - Y Zoom - - - - - - Min X - - - - - - Max Y - - - - - Max X - - - - - Min Y - - - - - - X Axis Step - Paso por eje X - - - - - Y Axis Step - Paso por eje Y - - - - - Line width - Anchura de la línea - - - - - Text size (px) - Tamaño del texto (px) - - - - - X Label - - - - - - Y Label - - - - - - X Log scale - Escala logarítmica en X - - - - - Show X graduation - Mostrar graduación del eje X - - - - - Show Y graduation - Mostrar graduación del eje Y - - - - Copy to clipboard - Copiar al portapapeles - - - - Save plot - Guardar gráfico… - - - - Save plot as - Guardar gráfico como… - - - - Load plot - Abrir gráfico… - - - Close - Cerrar - - - - ThanksTo - - - Thanks and Contributions - LogarithmPlotter - Agradecimientos y colaboraciones - LogarithmPlotter - - - - Source code - - - - - Original library by Raphael Graf - Biblioteca original de Raphael Graf - - - - Source - - - - - Ported to Javascript by Matthew Crumley - Portado a JavaScript por Matthew Crumley - - - - - - Website - - - - - Ported to QMLJS by Ad5001 - Portado a QMLJS por Ad5001 - - - - Libraries included - Bibliotecas incluidas - - - - Email - - - - - English - - - - - French - - - - - German - - - - - Hungarian - - - - - - - - Github - GitHub - - - - Norwegian - - - - - Spanish - Español - - - - Tamil - - - - - Translations included - Traducciones incluidas - - - - Improve - Mejorar - - - - bodemagnitude - - - Bode Magnitude - Magnitud de Bode - - - - Bode Magnitudes - Magnitudes de Bode - - - - - low-pass - Filtro paso bajo - - - - - high-pass - Filtro paso alto - - - - bodemagnitudesum - - - - Bode Magnitudes Sum - Suma de las Magnitudes de Bode - - - - bodephase - - - Bode Phase - Fase de Bode - - - - Bode Phases - Fases de Bode - - - - bodephasesum - - - - Bode Phases Sum - Suma de las fases de Bode - - - - changelog - - - Could not fetch changelog: Server error {}. - No se ha podido recuperar el registro de cambios: Error del servidor {}. - - - - - Could not fetch update: {}. - No se pudo obtener el registro de cambios: {}. - - - - color - - - - %1 %2's color changed from %3 to %4. - El color de %1 %2 cambió de %3 a %4. - - - - comment - - - Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} - Ej: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ-*), ]0;1[, {3;4;5} - - - - The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) - Los siguientes parámetros se utilizan cuando el dominio es un conjunto no continuo. (Ej: ℕ, ℤ, conjuntos como {0;3}...) - - - - Note: Specify the probability for each value. - Nota: Especifique la probabilidad para cada valor. - - - - Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... - Nota: Utilice %1[n] para referirse a %1ₙ, %1[n+1] para %1ₙ₊₁… - - - - If you have latex enabled, you can use use latex markup in between $$ to create equations. - Si tiene habilitado el latex, puede utilizar el marcado de latex entre $$ para crear ecuaciones. - - - - control - - - - - - - %1: - - - - - create - - - - New %1 %2 created. - Se ha creado un nuevo %1 %2. - - - - delete - - - - %1 %2 deleted. - %1 %2 borrados. - - - - distribution - - - Repartition - Distribución - - - - Repartition functions - Funciones de distribución - - - - editproperty - - - %1 of %2 %3 changed from "%4" to "%5". - %1 de %2 %3 cambió de "%4" a "%5". - - - - %1 of %2 changed from %3 to %4. - %1 de %2 ha cambiado de %3 a %4. - - - - error - - - - Cannot find property %1 of object %2. - No se puede encontrar la propiedad %1 del objeto %2. - - - - Undefined variable %1. - Variable %1 no definida. - - - - In order to be executed, object %1 must have at least one argument. - Para ser ejecutado, el objeto %1 debe tener al menos un argumento. - - - - %1 cannot be executed. - %1 no es una función. - - - - - - Invalid expression. - Expresión incorrecta. - - - - Invalid expression (parity). - Expresión no válida (paridad). - - - - Unknown character "%1". - Carácter "%1" desconocido. - - - - - Illegal escape sequence: %1. - Secuencia de salida no válida: %1 . - - - Parse error [%1:%2]: %3 - Error de análisis [%1:%2]: %3 - - - - - Parse error [position %1]: %2 - Error en el análisis [posición%1 ]:%2 - - - - Expected %1 - Previsto %1 - - - - Unexpected %1 - Inesperado %1 - - - - Unexpected ".": member access is not permitted - "." Inesperado: el acceso de miembros no está permitido - - - - Unexpected "[]": arrays are disabled. - "[]" inesperado: las matrices están desactivadas. - - - - Unexpected symbol: %1. - Símbolo inesperado: %1. - - - - - Function %1 must have at least one argument. - La función %1 debe tener al menos un argumento. - - - First argument to map is not a function. - El primer argumento de map no es una función. - - - Second argument to map is not an array. - El segundo argumento de map no es una matriz. - - - First argument to fold is not a function. - El primer argumento de fold no es una función. - - - Second argument to fold is not an array. - El segundo argumento de fold no es una matriz. - - - First argument to filter is not a function. - El primer argumento del filtro no es una función. - - - Second argument to filter is not an array. - El segundo argumento del filtro no es una matriz. - - - Second argument to indexOf is not a string or array. - El segundo argumento de indexOf no es una cadena ni una matriz. - - - Second argument to join is not an array. - El segundo argumento para unirse no es una matriz. - - - - No object found with names %1. - No se ha encontrado ningún objeto con el nombre %1. - - - - No object found with name %1. - Ningún objeto con el nombre %1 encontrado. - - - - Object cannot be dependent on itself. - El objeto no puede depender de sí mismo. - - - - Circular dependency detected. Object %1 depends on %2. - Dependencia circular detectada. El objeto %1 depende de %2. - - - - Circular dependency detected. Objects %1 depend on %2. - Dependencia circular detectada. Los objetos %1 dependen de %2. - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - Error al analizar la expresión de la propiedad %1: -%2 - -Expresión evaluada: %3 - - - - Error while attempting to draw %1 %2: -%3 - -Undoing last change. - Error al intentar dibujar %1 %2: -%3 - -Deshaciendo el último cambio. - - - Function definition is not permitted. - No se permite la definición de las funciones. - - - Expected variable for assignment. - Variable de asignación esperada. - - - - EOF - Fin de la expresión - - - - expression - - - - LogarithmPlotter - Parsing error - LogarithmPlotter - Error de procesamiento - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - Error al analizar la expresión de la propiedad %1: -%2 - -Expresión evaluada: %3 - - - - LogarithmPlotter - Drawing error - - - - - Automatically close parenthesises and brackets - Cerrar automáticamente paréntesis y corchetes - - - - Enable syntax highlighting - Activar el resaltado sintáctico - - - - Enable autocompletion - Activar autocompletar - - - - Color Scheme - Esquema de colores - - - - function - - - Function - Función - - - - Functions - Funciones - - - - gainbode - - high-pass - Filtro paso alto - - - low-pass - Filtro paso bajo - - - Bode Magnitude - Magnitud de Bode - - - Bode Magnitudes - Magnitudes de Bode - - - - general - - - Check for updates on startup - Comprobación de las actualizaciones al arrancar - - - - Reset redo stack automaticly - Restablecer la pila de rehacer automáticamente - - - - Enable LaTeX rendering - Activar el renderizado de LaTeX - - - - Enable threaded LaTeX renderer (experimental) - - - - - historylib - - %1 %2 deleted. - %1 %2 borrados. - - - %1 %2 shown. - Se muestra %1 %2. - - - %1 %2 hidden. - Se oculta %1 %2. - - - New %1 %2 created. - Se ha creado un nuevo %1 %2. - - - Name of %1 %2 changed to %3. - El nombre de %1 %2 se ha cambiado por %3. - - - %1 of %2 %3 changed from "%4" to "%5". - %1 de %2 %3 cambió de "%4" a "%5". - - - - io - - History - Historial - - - Copied plot screenshot to clipboard! - ¡Captura de pantalla del gráfico copiada al portapapeles! - - - &Update - &Actualizar - - - &Update LogarithmPlotter - &Actualizar LogarithmPlotter - - - Settings - Ajustes - - - Objects - Objetos - - - Saved plot to '%1'. - Gráfico guardado en '%1'. - - - Loading file '%1'. - Cargando el archivo '%1'. - - - Unknown object type: %1. - Tipo de objeto desconocido: %1 . - - - Invalid file provided. - Se ha proporcionado un archivo no válido. - - - Could not load file: - No se pudo cargar el archivo: - - - Could not save file: - No se ha podido guardar el archivo: - - - Loaded file '%1'. - Archivo cargado '%1'. - - - - latex - - - No Latex installation found. -If you already have a latex distribution installed, make sure it's installed on your path. -Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. - No se ha encontrado ninguna instalación de LaTeX. -Si ya tiene instalada una distribución de LaTeX, asegúrese de que está instalada en su ruta. -De lo contrario, puede descargar una distribución de LaTeX como TeX Live en https://tug.org/texlive/. - - - - DVIPNG was not found. Make sure you include it from your Latex distribution. - No se ha encontrado DVIPNG. Asegúrese de incluirlo en tu distribución LaTeX. - - - - An exception occured within the creation of the latex formula. -Process '{}' ended with a non-zero return code {}: - -{} -Please make sure your latex installation is correct and report a bug if so. - Se ha producido una excepción durante la creación de la fórmula LaTeX. -El proceso '{}' terminó con un código de retorno distinto de cero {}: - -{} -Por favor, asegúrate de que tu instalación de LaTeX es correcta e informe de un error si es así. - - - - Your LaTeX installation does not include some required packages: - -- {} (https://ctan.org/pkg/{}) - -Make sure said package is installed, or disable the LaTeX rendering in LogarithmPlotter. - Tu instalación de LaTeX no incluye algunos paquetes necesarios: - -- {} (https://ctan.org/pkg/{}) - -Asegúrate de que dicho paquete está instalado, o desactive el renderizado LaTeX en LogarithmPlotter. - - - - An exception occured within the creation of the latex formula. -Process '{}' took too long to finish: -{} -Please make sure your latex installation is correct and report a bug if so. - Se ha producido una excepción durante la creación de la fórmula LaTeX. -El proceso '{}' tardó demasiado en finalizar: -{} -Por favor, asegúrese de que su instalación de LaTeX es correcta e informe de un error si es así. - - - - main - - - This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}. -Please update LogarithmPlotter to open this file. - - - - - Could not open file "{}": -{} - - - - - Could not open file: "{}" -File does not exist. - - - - - Built with PySide6 (Qt) v{} and python v{} - - - - - name - - - - %1 %2 renamed to %3. - %1 %2 ha sido renombrado a %3. - - - - parameters - - - below - ↓ Abajo - - - - - left - ← Izquierda - - - - above-left - ↖ Arriba a la izquierda - - - - below-left - ↙ Abajo a la izquierda - - - - below-right - ↘ Arriba a la derecha - - - - center - >|< Centro - - - - top - ↑ Arriba - - - - above - ↑ Arriba - - - - - right - → Derecha - - - - above-right - ↗ Arriba a la derecha - - - - bottom - ↓ Bajar - - - - top-right - ↗ Arriba a la derecha - - - - application - Aplicación - - - - Next to target - Próximo al objetivo - - - - top-left - ↖ Arriba a la izquierda - - - - bottom-left - ↙ Abajo a la izquierda - - - - bottom-right - ↘ Abajo a la derecha - - - - function - Función - - - - high - Alto - - - - low - Bajo - - - - With label - Con etiqueta - - - - Hidden - Oculto - - - - phasebode - - Bode Phase - Fase de Bode - - - Bode Phases - Fases de Bode - - - - point - - - Point - Punto - - - - Points - Puntos - - - - position - - - Position of %1 %2 set from "%3" to "%4". - %1 %2 movido de "%3" a "%4". - - - - Position of %1 set from %2 to %3. - %1 movido de %2 a %3. - - - - prop - - - expression - Expresión - - - - definitionDomain - Dominio - - - - - om_0 - ω₀ - - - - disableLatex - Desactivar el renderizado LaTeX para este texto - - - - rounding - Redondeo - - - - - - - - - - labelX - Posición de la etiqueta en X - - - - - drawPoints - Mostrar puntos - - - - - drawDashedLines - Mostrar líneas discontinuas - - - - destinationDomain - Rango - - - - - - - - - - - - - labelPosition - Posición de la etiqueta - - - - displayMode - Modo de visualización - - - - pass - Pasar - - - - gain - Incremento de magnitud - - - - unit - Unidad a usar - - - - - y - Y - - - - omGraduation - Mostrar la graduación en ω₀ - - - - phase - Fase - - - - - - x - X - - - - pointStyle - Estilo de puntos - - - - text - Contenido - - - - probabilities - Lista de probabilidades - - - - targetElement - Objeto de destino - - - - approximate - Mostrar el resultado redondeado - - - - displayStyle - Estilo de visualización - - - - targetValuePosition - Posición del valor del objetivo - - - - defaultExpression - Expresión predeterminada - - - color - Color - - - - baseValues - Valores de inicialización - - - - labelContent - Contenido de la etiqueta - - - - repartition - - Repartition - Distribución - - - Repartition functions - Funciones de distribución - - - - sequence - - - Sequences - Secuencias - - - - Sequence - Secuencia - - - - settingCategory - - - general - General - - - - editor - Editor de expresiones - - - - default - Ajustes por defecto - - - - sommegainsbode - - Bode Magnitudes Sum - Suma de las Magnitudes de Bode - - - - sommephasesbode - - Bode Phases Sum - Suma de las fases de Bode - - - - text - - - Texts - Textos - - - - Text - Texto - - - - update - - - An update for LogarithmPlotter (v{}) is available. - Una actualización para LogarithmPlotter (v{}) está disponible. - - - - No update available. - No hay ninguna actualización disponible. - - - - Could not fetch update information: Server error {}. - No se ha podido obtener la información de la actualización: Error del servidor {}. - - - - Could not fetch update information: {}. - No se pudo obtener información de la actualización: {}. - - - - usage - - - integral(<from: number>, <to: number>, <f: ExecutableObject>) - integral(<desde: número>, <hasta: número>, <f: Objeto similar a una función>) - - - - - Usage: -%1 - Uso: -%1 - - - - - - Usage: -%1 -%2 - Uso: -%1 -%2 - - - - integral(<from: number>, <to: number>, <f: string>, <variable: string>) - integral(<desde: número>, <hasta: número>, <f: cadena>, <variable: cadena>) - - - - derivative(<f: ExecutableObject>, <x: number>) - derivative(<f: objeto similar a una función>, <x: número>) - - - - derivative(<f: string>, <variable: string>, <x: number>) - derivative(<f: cadena>, <variable: cadena>, <x: número>) - - - - visibility - - - - %1 %2 shown. - Se muestra %1 %2. - - - - - %1 %2 hidden. - Se oculta %1 %2. - - - - xcursor - - - X Cursor - Cursor X - - - - X Cursors - Cursores X - - - diff --git a/assets/i18n/lp_fr.ts b/assets/i18n/lp_fr.ts deleted file mode 100644 index d2e6380..0000000 --- a/assets/i18n/lp_fr.ts +++ /dev/null @@ -1,1999 +0,0 @@ - - - - - About - - - About LogarithmPlotter - À propos de LogarithmPlotter - - - - LogarithmPlotter v%1 - LogarithmPlotter v%1 - - - - 2D plotter software to make BODE plots, sequences and repartition functions. - Logiciel de traçage 2D pour les diagrammes de Bode, les suites et les fonctions de répartition. - - - - Report a bug - Rapport de bug - - - - Official website - Site officiel - - - - AppMenuBar - - - &File - &Fichier - - - - &Load... - &Ouvrir… - - - - &Save - &Sauvegarder - - - - Save &As... - Sauvegarde &Sous… - - - - &Quit - &Quitter - - - - &Edit - &Édition - - - - &Undo - &Annuler - - - - &Redo - &Rétablir - - - - &Copy plot - &Copier le graphe - - - - &Preferences - &Préférences - - - - &Create - &Créer - - - &Settings - &Paramètres - - - Check for updates on startup - Vérifier la présence de mise à jour au démarrage - - - Reset redo stack automaticly - Légèrement long, et pas forcément très compréhensible. - Réinitialiser la pile d'action "Rétablir" automatiquement - - - Enable LaTeX rendering - Activer le rendu LaTeX - - - Expression editor - Éditeur de formule - - - Automatically close parenthesises and brackets - Fermer automatiquement les parenthèses et les crochets - - - Enable syntax highlighting - Activer la coloration syntaxique - - - Enable autocompletion - Activer l'autocomplétion - - - Color Scheme - Coloration Syntaxique - - - - &Help - &Aide - - - - &Source code - &Code source - - - - &Report a bug - &Rapport de bug - - - - &User manual - Manuel d'&utilisation - - - - &Changelog - &Notes de version - - - - &Help translating! - &Aider à la traduction ! - - - - &Thanks - &Remerciements - - - - &About - &À propos - - - - Save unsaved changes? - Sauvegarder les modifications ? - - - - This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? - Ce graphe contient des modifications non sauvegardées. En faisant cela, toutes les données non sauvegardées seront perdues. Continuer ? - - - - BaseDialog - - - Close - Fermer - - - - BoolSetting - - Check for updates on startup - Vérifier la présence de mise à jour au démarrage - - - - Browser - - - Filter... - Filtrer… - - - - Redo > - Rétablir > - - - - > Now - > État actuel - - - - < Undo - < Annuler - - - - Changelog - - - Fetching changelog... - Récupération des notes de version… - - - - Close - Fermer - - - - CustomPropertyList - - - - + Create new %1 - + Créer un nouvel objet %1 - - - - Pick on graph - Prendre la position sur le graphe - - - - Dialog - - - Edit properties of %1 %2 - Changer les propriétés de %1 %2 - - - - LogarithmPlotter - Invalid object name - LogarithmPlotter - Nom d'objet invalide - - - - An object with the name '%1' already exists. - Un objet portant le nom '%1' existe déjà. - - - - Name - Nom - - - Label content - Étiquette - - - - null - vide - - - - name - nom - - - - name + value - nom + valeur - - - - EditorDialog - - Edit properties of %1 %2 - Changer les propriétés de %1 %2 - - - Name - Nom - - - Label content - Étiquette - - - null - vide - - - name - nom - - - name + value - nom + valeur - - - + Create new %1 - Traduction non litéralle pour éviter les problèmes de genre. - + Créer un nouvel objet %1 - - - - ExpressionEditor - - - Object Properties - Propriétés de l'objet - - - - Variables - Variables - - - - Constants - Constantes - - - - Functions - Fonctions - - - - Executable Objects - Objets fonction - - - - Objects - Objets - - - - FileDialog - - - Export Logarithm Plot file - Exporter le graphe Logarithmique - - - - Import Logarithm Plot file - Importer un graphe Logarithmique - - - - GreetScreen - - - Welcome to LogarithmPlotter - Bienvenu·e sur LogarithmPlotter - - - - Version %1 - Version %1 - - - Take a few seconds to configure LogarithmPlotter. -These settings can be changed at any time from the "Settings" menu. - Prenez quelques secondes pour configurer LogarithmPlotter. -Ces paramètres peuvent être modifiés à tout moment à partir du menu "Paramètres". - - - Enable LaTeX rendering - Activer le rendu LaTeX - - - Automatically close parenthesises and brackets in expressions - Fermer automatiquement les parenthèses et les crochets dans les formules - - - Enable syntax highlighting for expressions - Activer la coloration syntaxique des formules - - - Enable autocompletion interface in expression editor - Activer l'interface d'autocomplétion dans l'éditeur de formules - - - Color scheme: - Thème de coloration syntaxique : - - - - User manual - Manuel d'utilisation - - - - Changelog - Notes de version - - - - Preferences - Préférences - - - - Close - Fermer - - - Check for updates on startup (requires online connectivity) - Vérifier les mises à jour au démarrage (nécessite d'être connecté à internet) - - - Reset redo stack when a new action is added to history - Réinitialiser la pile d'action "Rétablir" lorsqu'une nouvelle action est ajoutée à l'historique - - - - HistoryBrowser - - Filter... - Filtrer… - - - Redo > - Rétablir > - - - > Now - > État actuel - - - < Undo - < Annuler - - - - ListSetting - - - + Add Entry - + Nouvelle entrée - - - - Loading - - - Loading... - Chargement… - - - - Finished rendering of %1 - Rendu de %1 terminé - - - - LogarithmPlotter - - - untitled - sans titre - - - - Objects - Objets - - - - Settings - Paramètres - - - - History - Historique - - - Saved plot to '%1'. - Graphe sauvegardé dans '%1'. - - - Loading file '%1'. - Chargement du fichier '%1'. - - - Unknown object type: %1. - Type d'objet inconnu : %1. - - - Invalid file provided. - Fichier fourni invalide. - - - Could not save file: - Impossible de sauvegarder le fichier : - - - Loaded file '%1'. - Fichier '%1' chargé. - - - - Copied plot screenshot to clipboard! - Image du graphe copiée dans le presse-papiers ! - - - - &Update - &Mise à jour - - - - &Update LogarithmPlotter - &Mettre à jour LogarithmPlotter - - - - ObjectCreationGrid - - - + Create new: - + Créer : - - - - ObjectLists - - - Hide all %1 - Cacher tous les %1 - - - - Show all %1 - Montrer tous les %1 - - - Hide %1 %2 - Cacher l'objet %1 %2 - - - Show %1 %2 - Montrer l'objet %1 %2 - - - Set %1 %2 position - Définir la position de l'objet %1 %2 - - - Delete %1 %2 - Supprimer l'objet %1 %2 - - - Pick new color for %1 %2 - Choisissez une nouvelle couleur pour %1 %2 - - - - ObjectRow - - - Hide %1 %2 - Cacher l'objet %1 %2 - - - - Show %1 %2 - Montrer l'objet %1 %2 - - - - Set %1 %2 position - Définir la position de l'objet %1 %2 - - - - Delete %1 %2 - Supprimer l'objet %1 %2 - - - - Pick new color for %1 %2 - Choisissez une nouvelle couleur pour %1 %2 - - - - PickLocation - - - Pointer precision: - Précision du pointeur : - - - - Snap to grid: - Placer sur la grille : - - - - Pick X - Prendre la position X - - - - Pick Y - Prendre la position Y - - - - Open picker settings - Ouvrir les paramètres du pointeur - - - - Hide picker settings - Cacher les paramètres du pointeur - - - - (no pick selected) - (aucun axe sélectionné) - - - - PickLocationOverlay - - Pointer precision: - Précision du pointeur : - - - Snap to grid - Placement sur la grille - - - Snap to grid: - Placer sur la grille : - - - Pick X - Prendre la position X - - - Pick Y - Prendre la position Y - - - Open picker settings - Ouvrir les paramètres du pointeur - - - Hide picker settings - Cacher les paramètres du pointeur - - - (no pick selected) - (aucun axe sélectionné) - - - - Preferences - - - Close - Fermer - - - - Settings - - - - X Zoom - Zoom en X - - - - - Y Zoom - Zoom en Y - - - - - Min X - Min X - - - - - Max Y - Max Y - - - - Max X - Max X - - - - Min Y - Min Y - - - - - X Axis Step - Pas de l'axe X - - - - - Y Axis Step - Pas de l'axe Y - - - - - Line width - Taille des lignes - - - - - Text size (px) - Taille du texte (px) - - - - - X Label - Label de l'axe X - - - - - Y Label - Label de l'axe Y - - - - - X Log scale - Échelle logarithmique en X - - - - - Show X graduation - Montrer la graduation de l'axe X - - - - - Show Y graduation - Montrer la graduation de l'axe Y - - - - Copy to clipboard - Copier vers le presse-papiers - - - - Save plot - Sauvegarder le graphe… - - - - Save plot as - Sauvegarder le graphe sous… - - - - Load plot - Ouvrir un graphe… - - - Close - Fermer - - - - ThanksTo - - - Thanks and Contributions - LogarithmPlotter - Remerciements et contributions - LogarithmPlotter - - - - Source code - Code source - - - - Original library by Raphael Graf - Bibliothèque originale de Raphael Graf - - - - Source - Source - - - - Ported to Javascript by Matthew Crumley - Porté en JavaScript par Matthew Crumley - - - - - - Website - Site web - - - - Ported to QMLJS by Ad5001 - Porté à QMLJS par Ad5001 - - - - Libraries included - Bibliothèques incluses - - - - Email - Email - - - - English - Anglais - - - - French - Français - - - - German - Allemand - - - - Hungarian - Hongrois - - - - - - - Github - GitHub - - - - Norwegian - Norvégien - - - - Spanish - Espagnol - - - - Tamil - Tamoul - - - - Translations included - Traductions incluses - - - - Improve - Améliorer - - - - bodemagnitude - - - Bode Magnitude - Gain de Bode - - - - Bode Magnitudes - Gains de Bode - - - - - low-pass - passe-bas - - - - - high-pass - passe-haut - - - - bodemagnitudesum - - - - Bode Magnitudes Sum - Sommes des gains de Bode - - - - bodephase - - - Bode Phase - Phase de Bode - - - - Bode Phases - Phases de Bode - - - - bodephasesum - - - - Bode Phases Sum - Somme des phases de Bode - - - - changelog - - - Could not fetch changelog: Server error {}. - Impossible de récupérer les notes de version : Erreur de serveur {}. - - - - - Could not fetch update: {}. - Impossible de récupérer les notes de version : {}. - - - - color - - - - %1 %2's color changed from %3 to %4. - La couleur du %1 %2 a été changée du %3 au %4. - - - - comment - - - Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} - Par exemple : R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} - - - - The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) - Les paramètres suivants sont utilisés lorsque le domaine de définition est un ensemble non-continu. (Ex : ℕ, ℤ, des ensembles comme {0;3}…) - - - - Note: Specify the probability for each value. - Note : Spécifiez la probabilité pour chaque valeur. - - - - Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... - Note : Utilisez %1[n] pour faire référence à %1ₙ, %1[n+1] pour %1ₙ₊₁... - Note : Utilisez %1[n] pour faire référence à %1ₙ, %1[n+1] pour %1ₙ₊₁… - - - - If you have latex enabled, you can use use latex markup in between $$ to create equations. - Si vous avez activé le rendu latex, vous pouvez utiliser les balises latex entre $$ pour créer des équations. - - - - control - - - - - - - %1: - %1 : - - - - create - - - - New %1 %2 created. - Nouvel objet %1 %2 créé. - - - - delete - - - - %1 %2 deleted. - %1 %2 supprimé(e). - - - - distribution - - - Repartition - Répartition - - - - Repartition functions - Fonctions de répartition - - - - editproperty - - - %1 of %2 %3 changed from "%4" to "%5". - %1 de %2 %3 modifiée de "%4" à "%5". - - - - %1 of %2 changed from %3 to %4. - %1 de %2 modifiée de %3 à %4. - - - - error - - - - Cannot find property %1 of object %2. - Impossible de trouver la propriété %1 de l'objet %2. - - - - Undefined variable %1. - La variable %1 n'est pas définie. - - - - In order to be executed, object %1 must have at least one argument. - Pour être utilisé comme fonction, l'objet %1 nécessite au moins un argument. - - - - %1 cannot be executed. - %1 n'est pas une fonction. - - - - - - Invalid expression. - Formule invalide. - - - - Invalid expression (parity). - Formule invalide (parité). - - - - Unknown character "%1". - Le caractère "%1" est inconnu. - - - - - Illegal escape sequence: %1. - Séquence d'échappement illégale : %1. - - - Parse error [%1:%2]: %3 - Erreur de syntaxe [%1:%2] : %3 - - - - Expected %1 - %1 attendu - - - - Unexpected %1 - %1 inattendu - - - Function definition is not permitted. - La définition de fonctions n'est pas autorisée. - - - Expected variable for assignment. - Une variable est attendue pour l'affectation. - - - - - Parse error [position %1]: %2 - Erreur de syntaxe [position %1] : %2 - - - - Unexpected ".": member access is not permitted - "." inattendu : l'accès aux propriétés n'est pas autorisé - - - - Unexpected "[]": arrays are disabled. - "[]" inattendu : les tableaux sont désactivés. - - - - Unexpected symbol: %1. - Symbole inconnu : %1. - - - - - Function %1 must have at least one argument. - La fonction %1 nécessite au moins un argument. - - - First argument to map is not a function. - Le premier argument de map n'est pas une fonction. - - - Second argument to map is not an array. - Le deuxième argument de map n'est pas un tableau. - - - First argument to fold is not a function. - Le premier argument de fold n'est pas une fonction. - - - Second argument to fold is not an array. - Le deuxième argument de fold n'est pas un tableau. - - - First argument to filter is not a function. - Le premier argument de filter n'est pas une fonction. - - - Second argument to filter is not an array. - Le deuxième argument de filter n'est pas un tableau. - - - Second argument to indexOf is not a string or array. - Le deuxième argument de indexOf n'est ni chaîne de caractères ni un tableau. - - - Second argument to join is not an array. - Le deuxième argument de join n'est pas un tableau. - - - - EOF - Fin de la formule - - - - No object found with names %1. - Aucun objet trouvé ayant pour noms %1. - - - - No object found with name %1. - Aucun objet avec le nom %1 n'a été trouvé. - - - - Object cannot be dependent on itself. - Un objet ne peut pas dépendre de lui-même. - - - - Circular dependency detected. Object %1 depends on %2. - Dépendance circulaire détectée. L'objet %1 dépend de %2. - - - - Circular dependency detected. Objects %1 depend on %2. - Dépendance circulaire détectée. Les objets %1 dépendent de %2. - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - Erreur lors de l'analyse de la formule pour la propriété %1 : -%2 - -Formule analysée : %3 - - - - Error while attempting to draw %1 %2: -%3 - -Undoing last change. - Erreur lors de la tentative de dessin du %1 %2 : -%3 - -La dernière modification a été annulée. - - - - expression - - - - LogarithmPlotter - Parsing error - LogarithmPlotter - Erreur de syntaxe - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - Erreur lors de l'analyse de la formule pour la propriété %1 : -%2 - -Formule analysée : %3 - - - - LogarithmPlotter - Drawing error - LogarithmPlotter - Erreur - - - - Automatically close parenthesises and brackets - Fermer automatiquement les parenthèses et les crochets - - - - Enable syntax highlighting - Activer la coloration syntaxique - - - - Enable autocompletion - Activer l'autocomplétion - - - - Color Scheme - Coloration Syntaxique - - - - function - - - Function - Fonction - - - - Functions - Fonctions - - - - gainbode - - Bode Magnitude - Gain de Bode - - - Bode Magnitudes - Gains de Bode - - - low-pass - passe-bas - - - high-pass - passe-haut - - - - general - - - Check for updates on startup - Vérifier la présence de mise à jour au démarrage - - - - Reset redo stack automaticly - Réinitialiser la pile d'action "Rétablir" automatiquement - - - - Enable LaTeX rendering - Activer le rendu LaTeX - - - - Enable threaded LaTeX renderer (experimental) - Activer le moteur de rendu LaTeX asynchrone (expérimental) - - - - historylib - - New %1 %2 created. - Nouvel objet %1 %2 créé. - - - %1 %2 deleted. - %1 %2 supprimé(e). - - - %1 of %2 %3 changed from "%4" to "%5". - %1 de %2 %3 modifiée de "%4" à "%5". - - - %1 %2 shown. - %1 %2 affiché(e). - - - %1 %2 hidden. - %1 %2 cachée(e). - - - Name of %1 %2 changed to %3. - Le nom de %1 %2 a été changé en %3. - - - - io - - Objects - Objets - - - Settings - Paramètres - - - History - Historique - - - Copied plot screenshot to clipboard! - Image du graphe copiée dans le presse-papiers ! - - - &Update - &Mise à jour - - - &Update LogarithmPlotter - &Mettre à jour LogarithmPlotter - - - Saved plot to '%1'. - Graphe sauvegardé dans '%1'. - - - Loading file '%1'. - Chargement du fichier '%1'. - - - Unknown object type: %1. - Type d'objet inconnu : %1. - - - Invalid file provided. - Fichier fourni invalide. - - - Could not load file: - Impossible de charger le fichier : - - - Could not save file: - Impossible de sauvegarder le fichier : - - - Loaded file '%1'. - Fichier '%1' chargé. - - - - latex - - - No Latex installation found. -If you already have a latex distribution installed, make sure it's installed on your path. -Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. - Aucune installation de LaTeX trouvée. -Si vous avez déjà installé une distribution LaTeX, assurez-vous qu'elle est installée sur votre PATH. -Sinon, vous pouvez télécharger une distribution LaTeX comme TeX Live à l'adresse https://tug.org/texlive/. - - - - DVIPNG was not found. Make sure you include it from your Latex distribution. - DVIPNG n'a pas été trouvé. Assurez-vous de l'inclure dans votre distribution LaTeX. - - - - An exception occured within the creation of the latex formula. -Process '{}' ended with a non-zero return code {}: - -{} -Please make sure your latex installation is correct and report a bug if so. - Une exception s'est produite lors de la création de la formule LaTeX. -Le processus '{}' s'est terminé par un code de retour non nul {} : - -{} -Vérifiez que votre installation de LaTeX est correcte et signalez un bogue si c'est le cas. - - - - Your LaTeX installation does not include some required packages: - -- {} (https://ctan.org/pkg/{}) - -Make sure said package is installed, or disable the LaTeX rendering in LogarithmPlotter. - Votre installation de LaTeX n'inclut pas certains paquets nécessaires : - -- {} (https://ctan.org/pkg/{}) - -Assurez-vous que ce paquetage est installé, ou désactivez le rendu LaTeX dans LogarithmPlotter. - - - - An exception occured within the creation of the latex formula. -Process '{}' took too long to finish: -{} -Please make sure your latex installation is correct and report a bug if so. - Une exception s'est produite lors de la création de la formule LaTeX. -Le processus '{}' a mis trop de temps à se terminer : -{} -Vérifiez que votre installation de LaTeX est correcte et signalez un bogue si c'est le cas. - - - - main - - - This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}. -Please update LogarithmPlotter to open this file. - Ce fichier a été créé par une version plus récente de LogarithmPlotter et ne peut pas être rechargé dans LogarithmPlotter v{}. -Veuillez mettre à jour LogarithmPlotter pour ouvrir ce fichier. - - - - Could not open file "{}": -{} - Impossible d'ouvrir le fichier "{}": -{} - - - - Could not open file: "{}" -File does not exist. - Impossible d'ouvrir le fichier "{}": -Le fichier n'existe pas. - - - - Built with PySide6 (Qt) v{} and python v{} - Compilé avec PySide6 (Qt) v{} et python v{} - - - - name - - - - %1 %2 renamed to %3. - %1 %2 renommé(e) en %3. - - - - parameters - - - above - ↑ Au dessus - - - - below - ↓ En dessous - - - - - left - ← À gauche - - - - - right - → À droite - - - - above-left - ↖ Au dessus à gauche - - - - above-right - ↗ Au dessus à droite - - - - below-left - ↙ En dessous à gauche - - - - below-right - ↘ En dessous à droite - - - - center - >|< Centré - - - - top - ↑ Au dessus - - - - bottom - ↓ En dessous - - - - top-left - ↖ Au dessus à gauche - - - - top-right - ↗ Au dessus à droite - - - - bottom-left - ↙ En dessous à gauche - - - - bottom-right - ↘ En dessous à droite - - - - application - Application - - - - function - Fonction - - - - high - Haut - - - - low - Bas - - - - Next to target - A côté de la cible - - - - With label - Avec l'étiquette - - - - Hidden - Caché - - - - phasebode - - Bode Phase - Phase de Bode - - - Bode Phases - Phases de Bode - - - - point - - - Point - Point - - - - Points - Points - - - - position - - - Position of %1 %2 set from "%3" to "%4". - %1 %2 a été déplacé depuis "%3" vers "%4". - - - - Position of %1 set from %2 to %3. - %1 a été déplacé depuis %2 vers %3. - - - - prop - - - expression - Formule - - - - definitionDomain - Domaine de définition - - - - destinationDomain - Portée - - - - - - - - - - - - - labelPosition - Position de l'étiquette - - - - displayMode - Mode d'affichage - - - - - - - - - - labelX - Position en X de l'étiquette - - - - - drawPoints - Afficher les points - - - - - drawDashedLines - Afficher les pointillés - - - - - om_0 - ω₀ - - - - pass - Passe - - - - gain - Gain - - - - omGraduation - Afficher la graduation sur ω₀ - - - - phase - Phase - - - - unit - Unité de la phase - - - - - - x - X - - - - - y - Y - - - - pointStyle - Style du point - - - - probabilities - Liste de probabilités - - - - text - Contenu - - - - disableLatex - Désactiver le rendu LaTeX pour ce texte - - - - targetElement - Objet à cibler - - - - approximate - Afficher la valeur arrondie calculée - - - - rounding - Arrondi - - - - displayStyle - Style d'affichage - - - - targetValuePosition - Position de la valeur de la cible - - - - defaultExpression - Formule par défaut - - - - baseValues - Valeurs d'initialisation - - - color - Couleur - - - - labelContent - Étiquette - - - - repartition - - Repartition - Répartition - - - Repartition functions - Fonctions de répartition - - - - sequence - - - Sequence - Suite - - - - Sequences - Suites - - - - settingCategory - - - general - Général - - - - editor - Éditeur de formule - - - - default - Paramètres par défaut - - - - sommegainsbode - - Bode Magnitudes Sum - Sommes des gains de Bode - - - - sommephasesbode - - Bode Phases Sum - Somme des phases de Bode - - - - text - - - Text - Texte - - - - Texts - Textes - - - - update - - - An update for LogarithmPlotter (v{}) is available. - Une mise à jour de LogarithmPlotter (v{}) est disponible. - - - - No update available. - À jour. - - - - Could not fetch update information: Server error {}. - Impossible de récupérer les informations de mise à jour. Erreur du serveur {}. - - - - Could not fetch update information: {}. - Impossible de récupérer les informations de mise à jour. {}. - - - - usage - - - integral(<from: number>, <to: number>, <f: ExecutableObject>) - integral(<de : nombre>, <à : nombre>, <f : Objet fonction>) - - - - - Usage: -%1 - Emploi : -%1 - - - - - - Usage: -%1 -%2 - Emploi : -%1 -%2 - - - - integral(<from: number>, <to: number>, <f: string>, <variable: string>) - integral(<de : nombre>, <à : nombre>, <f : fonction chaîne>, <variable>) - - - - derivative(<f: ExecutableObject>, <x: number>) - derivative(<f : Objet fonction>, <x : nombre>) - - - - derivative(<f: string>, <variable: string>, <x: number>) - derivative(<f : fonction chaîne>, <variable>, <x : nombre>) - - - - visibility - - - - %1 %2 shown. - %1 %2 affiché(e). - - - - - %1 %2 hidden. - %1 %2 cachée(e). - - - - xcursor - - - X Cursor - Curseur X - - - - X Cursors - Curseurs X - - - diff --git a/assets/i18n/lp_hu.ts b/assets/i18n/lp_hu.ts deleted file mode 100644 index 7e2856d..0000000 --- a/assets/i18n/lp_hu.ts +++ /dev/null @@ -1,1996 +0,0 @@ - - - - - About - - - About LogarithmPlotter - LogarithmPlotter névjegye - - - - LogarithmPlotter v%1 - LogarithmPlotter %1 verzió - - - - 2D plotter software to make BODE plots, sequences and repartition functions. - Síkbeli ábrázolásszoftver Bode-ábrák, sorozatok és eloszlási funkciók készítéséhez. - - - - Report a bug - Hiba bejelentése - - - - Official website - Hivatalos honlap - - - - AppMenuBar - - - &File - &Fájl - - - - &Load... - &Megnyitás… - - - - &Save - &Mentés - - - - Save &As... - Me&ntés másként… - - - - &Quit - &Kilépés - - - - &Edit - S&zerkesztés - - - - &Undo - &Visszavonás - - - - &Redo - &Ismétlés - - - - &Copy plot - Ábra má&solása - - - - &Preferences - &Beállítások - - - - &Create - &Létrehozás - - - &Settings - &Beállítások - - - Check for updates on startup - Frissítések keresése indításkor - - - Reset redo stack automaticly - Ismétlési verem önműködő visszaállítása - - - Enable LaTeX rendering - LaTeX-megjelenítés engedélyezése - - - Expression editor - Kifejezésszerkesztő - - - Automatically close parenthesises and brackets - Zárójelek automatikus bezárása - - - Enable syntax highlighting - Mondattani kiemelés engedélyezése - - - Enable autocompletion - Automatikus befejezés engedélyezése - - - Color Scheme - Színséma - - - - &Help - &Súgó - - - - &Source code - &Forráskód - - - - &Report a bug - &Hiba bejelentése - - - - &User manual - &Használati utasítás - - - - &Changelog - &Változásnapló - - - - &Help translating! - &Segítség a fordításban! - - - - &Thanks - &Köszönjük - - - - &About - &Névjegy - - - - Save unsaved changes? - Menti a változtatásokat? - - - - This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? - Ez az ábra nem mentett változtatásokat tartalmaz. Ezzel az összes nem mentett adat elveszik. Folytatja? - - - - BaseDialog - - - Close - Bezárás - - - - BoolSetting - - Check for updates on startup - Frissítések keresése indításkor - - - - Browser - - - Filter... - Szűrő… - - - - Redo > - Ismétlés > - - - - > Now - > Most - - - - < Undo - < Visszavonás - - - - Changelog - - - Fetching changelog... - Változásnapló lekérése… - - - - Close - Kész - - - - CustomPropertyList - - - - + Create new %1 - + Új %1 létrehozása - - - - Pick on graph - Ábra kijelölése - - - - Dialog - - - Edit properties of %1 %2 - %1 %2 tulajdonságainak szerkesztése - - - - LogarithmPlotter - Invalid object name - LogarithmPlotter - Érvénytelen objektumnév - - - - An object with the name '%1' already exists. - A(z) „%1” nevű objektum már létezik. - - - - Name - Név - - - Label content - Címketartalom - - - - null - üres - - - - name - név - - - - name + value - név + érték - - - - EditorDialog - - Edit properties of %1 %2 - %1 %2 tulajdonságainak szerkesztése - - - Name - Név - - - Label content - Címke tartalom - - - null - üres - - - name - név - - - name + value - név + érték - - - + Create new %1 - + Új %1 létrehozása - - - - ExpressionEditor - - - Object Properties - Objektumtulajdonságok - - - - Variables - Változók - - - - Constants - Állandók - - - - Functions - Függvények - - - - Executable Objects - Függvényobjektumok - - - - Objects - Objektumok - - - - FileDialog - - - Export Logarithm Plot file - Logaritmus-ábra-fájl exportálása - - - - Import Logarithm Plot file - Logaritmus-ábra-fájl importálása - - - - GreetScreen - - - Welcome to LogarithmPlotter - Isten hozott a LogarithmPlotter! - - - - Version %1 - %1 verzió - - - Take a few seconds to configure LogarithmPlotter. -These settings can be changed at any time from the "Settings" menu. - Szánjon néhány másodpercet a LogarithmPlotter beállításához. -Ezek a beállítások bármikor módosíthatók a „Beállítások” menüben. - - - Check for updates on startup (requires online connectivity) - Frissítések keresése indításkor (online kapcsolat szükséges) - - - Reset redo stack when a new action is added to history - Ismétlési verem alaphelyzet visszaállítása, ha új műveletet adnak az előzményekhez - - - Enable LaTeX rendering - LaTeX-megjelenítés engedélyezése - - - Automatically close parenthesises and brackets in expressions - Zárójelek automatikus bezárása a kifejezésekben - - - Enable syntax highlighting for expressions - Mondattani kiemelés engedélyezése a kifejezésekhez - - - Enable autocompletion interface in expression editor - Automatikus befejezési felület engedélyezése a kifejezésszerkesztőben - - - Color scheme: - Színséma: - - - - User manual - Használati utasítás - - - - Changelog - Változásnapló - - - - Preferences - Beállítások - - - - Close - Kész - - - - HistoryBrowser - - Filter... - Szűrő… - - - Redo > - Ismétlés > - - - > Now - > Most - - - < Undo - < Visszavonás - - - - ListSetting - - - + Add Entry - + Bejegyzés hozzáadása - - - - Loading - - - Loading... - Betöltés… - - - - Finished rendering of %1 - %1 renderelése befejeződött - - - - LogarithmPlotter - - - untitled - névtelen - - - - Objects - Tárgyak - - - - Settings - Beállítások - - - - History - Előzmények - - - Saved plot to '%1'. - Ábra mentve ide: „%1”. - - - Loading file '%1'. - A(z) „%1” fájl betöltése folyamatban van. - - - Unknown object type: %1. - Ismeretlen objektumtípus: %1. - - - Invalid file provided. - A megadott fájl érvénytelen. - - - Could not save file: - A fájl mentése nem sikerült: - - - Loaded file '%1'. - A(z) „%1” fájl betöltve. - - - - Copied plot screenshot to clipboard! - Ábra képernyőkép vágólapra másolva! - - - - &Update - &Frissítés - - - - &Update LogarithmPlotter - A LogarithmPlotter &frissítése - - - - ObjectCreationGrid - - - + Create new: - + Új létrehozása: - - - - ObjectLists - - - Hide all %1 - Összes %1 elrejtése - - - - Show all %1 - Összes %1 megjelenítése - - - Hide %1 %2 - %1 %2 elrejtése - - - Show %1 %2 - %1 %2 megjelenítése - - - Set %1 %2 position - %1 %2 helye beállítása - - - Delete %1 %2 - %1 %2 törlése - - - Pick new color for %1 %2 - Válasszon új színt a következőhöz: %1 %2 - - - - ObjectRow - - - Hide %1 %2 - %1 %2 elrejtése - - - - Show %1 %2 - %1 %2 megjelenítése - - - - Set %1 %2 position - %1 %2 helye beállítása - - - - Delete %1 %2 - %1 %2 törlése - - - - Pick new color for %1 %2 - Válasszon új színt a következőhöz: %1 %2 - - - - PickLocation - - - Pointer precision: - Mutató pontossága: - - - - Snap to grid: - Rácshoz igazítás: - - - - Pick X - X kijelölése - - - - Pick Y - Y kijelölése - - - - Open picker settings - Kijelölési beállítások megnyitása - - - - Hide picker settings - Kijelölési beállítások elrejtése - - - - (no pick selected) - (nincs kijelölés kiválasztva) - - - - PickLocationOverlay - - Pointer precision: - Mutató pontossága: - - - Snap to grid - Rácshoz illesztés - - - Snap to grid: - Rácshoz igazítás: - - - Pick X - X kijelölése - - - Pick Y - Y kijelölése - - - Open picker settings - Kijelölési beállítások megnyitása - - - Hide picker settings - Kijelölési beállítások elrejtése - - - (no pick selected) - (nincs kijelölés kiválasztva) - - - - Preferences - - - Close - Bezárás - - - - Settings - - - - X Zoom - X-nagyítás - - - - - Y Zoom - Y-nagyítás - - - - - Min X - Legkisebb X - - - - - Max Y - Legnagyobb Y - - - - Max X - Legnagyobb X - - - - Min Y - Legkisebb Y - - - - - X Axis Step - X tengely lépésköze - - - - - Y Axis Step - Y tengely lépésköze - - - - - Line width - Vonalvastagság - - - - - Text size (px) - Szövegméret (képpont) - - - - - X Label - X címke - - - - - Y Label - Y címke - - - - - X Log scale - X tengely logaritmikus skálával - - - - - Show X graduation - X érettségi megjelenítése - - - - - Show Y graduation - Y érettségi megjelenítése - - - - Copy to clipboard - Másolás a vágólapra - - - - Save plot - Ábra mentése… - - - - Save plot as - Ábra mentése másként… - - - - Load plot - Ábra megnyitása… - - - Close - Kész - - - - ThanksTo - - - Thanks and Contributions - LogarithmPlotter - Köszönet és hozzájárulás - LogarithmPlotter - - - - Source code - Forráskód - - - - Original library by Raphael Graf - Eredeti könyvtár: Graf Raphael - - - - Source - Forrás - - - - Ported to Javascript by Matthew Crumley - JavaScript-átalakítás: Crumley Máté - - - - - - Website - Honlap - - - - Ported to QMLJS by Ad5001 - QMLJS-átalakítás: Ad5001 - - - - Libraries included - Tartalmazott könyvtárak - - - - Email - E-mail - - - - English - angol - - - - French - francia - - - - German - német - - - - Hungarian - magyar - - - - - - - Github - GitHub - - - - Norwegian - norvég - - - - Spanish - spanyol - - - - Tamil - Tamil - - - - Translations included - A felhasználói felület nyelvei - - - - Improve - Fejlesztés - - - - bodemagnitude - - - Bode Magnitude - Bode-nagyságrend - - - - Bode Magnitudes - Bode-nagyságrendek - - - - - low-pass - aluláteresztő - - - - - high-pass - felüláteresztő - - - - bodemagnitudesum - - - - Bode Magnitudes Sum - Bode-nagyságrendek összege - - - - bodephase - - - Bode Phase - Bode-fázis - - - - Bode Phases - Bode-fázisok - - - - bodephasesum - - - - Bode Phases Sum - Bode-fázisok összege - - - - changelog - - - Could not fetch changelog: Server error {}. - Nem sikerült lekérni a változásnaplót: Kiszolgálóhiba: {}. - - - - - Could not fetch update: {}. - Nem sikerült lekérni a változásnaplót: {}. - - - - color - - - - %1 %2's color changed from %3 to %4. - %1 %2 színe %3-ról %4-re változott. - - - - comment - - - Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} - Példák: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} - - - - The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) - A következő paraméterek használatosak, ha a tartomány nem folytonos halmaz. (Példák: ℕ, ℤ, olyan halmazok, mint a {0;3}…) - - - - Note: Specify the probability for each value. - Megjegyzés: Adja meg az egyes értékek valószínűségét. - - - - Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... - Megjegyzés: A(z) %1[n] használatával hivatkozhat erre: %1ₙ, a(z) %1[n+1] használatával hivatkozhat erre: %1ₙ₊₁, … - - - - If you have latex enabled, you can use use latex markup in between $$ to create equations. - Ha a LaTeX engedélyezve van, a LaTeX-jelölés használható egyenletek létrehozására $$ között. - - - - control - - - - - - - %1: - %1: - - - - create - - - - New %1 %2 created. - Új %1 %2 létrehozva. - - - - delete - - - - %1 %2 deleted. - %1 %2 törölve. - - - - distribution - - - Repartition - Elosztás - - - - Repartition functions - Elosztási függvények - - - - editproperty - - - %1 of %2 %3 changed from "%4" to "%5". - %1/%2 %3 megváltozott. Régi érték: %4, új érték: %5. - - - - %1 of %2 changed from %3 to %4. - %1/%2 megváltozott. Régi érték: %3, új érték: %4. - - - - error - - - - Cannot find property %1 of object %2. - A(z) %2 objektum %1 tulajdonsága nem található. - - - - Undefined variable %1. - A(z) %1 változó nincs meghatározva. - - - - In order to be executed, object %1 must have at least one argument. - A végrehajtáshoz a(z) %1 objektumnak legalább egy argumentummal kell rendelkeznie. - - - - %1 cannot be executed. - A(z) %1 nem függvény. - - - - - - Invalid expression. - Érvénytelen kifejezés. - - - - Invalid expression (parity). - Érvénytelen kifejezés (paritás). - - - - Unknown character "%1". - Ismeretlen karakter „%1”. - - - - - Illegal escape sequence: %1. - Érvénytelen kilépési sorozat: %1. - - - Parse error [%1:%2]: %3 - Elemzési hiba [%1:%2]: %3 - - - - Expected %1 - Várható %1 - - - - Unexpected %1 - Váratlan %1 - - - Function definition is not permitted. - A függvény meghatározása nem engedélyezett. - - - Expected variable for assignment. - A hozzárendeléshez várt változó. - - - - - Parse error [position %1]: %2 - Elemzési hiba [hely %1]: %2 - - - - Unexpected ".": member access is not permitted - Váratlan „.”: a tagok hozzáférése nem engedélyezett - - - - Unexpected "[]": arrays are disabled. - Váratlan „[]”: a tömbök le vannak tiltva. - - - - Unexpected symbol: %1. - Váratlan szimbólum: %1. - - - - - Function %1 must have at least one argument. - A(z) %1 függvénynek legalább egy argumentumnak kell lennie. - - - First argument to map is not a function. - Az első leképezési argumentum nem függvény. - - - Second argument to map is not an array. - A második leképezési argumentum nem tömb. - - - First argument to fold is not a function. - Az első behajtási argumentum nem függvény. - - - Second argument to fold is not an array. - A második behajtási argumentum nem tömb. - - - First argument to filter is not a function. - Az első szűrési argumentum nem függvény. - - - Second argument to filter is not an array. - A második szűrési argumentum nem tömb. - - - Second argument to indexOf is not a string or array. - Az indexOf második argumentuma nem karakterlánc vagy tömb. - - - Second argument to join is not an array. - A második csatlakozási argumentum nem tömb. - - - - EOF - Kifejezés vége - - - - No object found with names %1. - A(z) %1 nevű objektum nem található. - - - - No object found with name %1. - A(z) %1 nevű objektum nem található. - - - - Object cannot be dependent on itself. - Az objektum nem függhet önmagától. - - - - Circular dependency detected. Object %1 depends on %2. - Körkörös függőség észlelve. A(z) %1-objektum a(z) %2-objektumtól függ. - - - - Circular dependency detected. Objects %1 depend on %2. - Körkörös függőség észlelve. A(z) %1-objektumok a(z) %2-objektumtól függenek. - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - Hiba a(z) %1 tulajdonság kifejezésének elemzésekor: -%2 - -Kiértékelt kifejezés: %3 - - - - Error while attempting to draw %1 %2: -%3 - -Undoing last change. - Hiba történt a(z) %1 %2 rajzolása közben: -%3 - -Az utolsó módosítás visszavonása. - - - - expression - - - - LogarithmPlotter - Parsing error - LogarithmPlotter - Elemzési hiba - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - Hiba a(z) %1 tulajdonság kifejezésének elemzésekor: -%2 - -Kiértékelt kifejezés: %3 - - - - LogarithmPlotter - Drawing error - LogarithmPlotter - Rajzolási hiba - - - - Automatically close parenthesises and brackets - Zárójelek automatikus bezárása - - - - Enable syntax highlighting - Mondattani kiemelés engedélyezése - - - - Enable autocompletion - Automatikus befejezés engedélyezése - - - - Color Scheme - Színséma - - - - function - - - Function - Függvény - - - - Functions - Függvények - - - - gainbode - - Bode Magnitude - Bode-nagyságrend - - - Bode Magnitudes - Bode-nagyságrendek - - - low-pass - aluláteresztő - - - high-pass - felüláteresztő - - - - general - - - Check for updates on startup - Frissítések keresése indításkor - - - - Reset redo stack automaticly - Ismétlési verem önműködő visszaállítása - - - - Enable LaTeX rendering - LaTeX-megjelenítés engedélyezése - - - - Enable threaded LaTeX renderer (experimental) - A szálas LaTeX renderer engedélyezése (kísérleti) - - - - historylib - - New %1 %2 created. - Új %1 %2 létrehozva. - - - %1 %2 deleted. - %1 %2 törölve. - - - %1 of %2 %3 changed from "%4" to "%5". - %1/%2 %3 megváltozott. Régi érték: %4, új érték: %5. - - - %1 %2 shown. - %1 %2 megjelenítve. - - - %1 %2 hidden. - %1 %2 rejtve. - - - Name of %1 %2 changed to %3. - %1 %2 neve a következőre módosult: %3. - - - - io - - Settings - Beállítások - - - History - Előzmények - - - Saved plot to '%1'. - Ábra mentve ide: „%1”. - - - Loading file '%1'. - A(z) „%1” fájl betöltése folyamatban van. - - - Unknown object type: %1. - Ismeretlen objektumtípus: %1. - - - Invalid file provided. - A megadott fájl érvénytelen. - - - Could not load file: - Nem sikerült betölteni a fájlt: - - - Could not save file: - A fájl mentése nem sikerült: - - - Loaded file '%1'. - A(z) „%1” fájl betöltve. - - - Copied plot screenshot to clipboard! - Ábra képernyőkép vágólapra másolva! - - - &Update - &Frissítés - - - &Update LogarithmPlotter - A LogarithmPlotter &frissítése - - - Objects - Objektumok - - - - latex - - - No Latex installation found. -If you already have a latex distribution installed, make sure it's installed on your path. -Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. - Nem található LaTeX telepítés. -Ha már telepítve van egy LaTeX disztribúció, győződjön meg arról, hogy az telepítve van az elérési útján. -Egyébként letölthet egy LaTeX disztribúciót, például a TeX Live-t a https://tug.org/texlive/ címről. - - - - DVIPNG was not found. Make sure you include it from your Latex distribution. - DVIPNG nem található. Ügyeljen arra, hogy a LaTeX disztribúciójából tartalmazza. - - - - An exception occured within the creation of the latex formula. -Process '{}' ended with a non-zero return code {}: - -{} -Please make sure your latex installation is correct and report a bug if so. - Kivétel történt a LaTeX-képlet létrehozása során. -A(z) „{}” folyamat nullától eltérő visszatérési kóddal ({}) végződött: - -{} -Kérjük, ellenőrizze, hogy a LaTeX telepítése helyes-e, és ha igen, jelentse a hibát. - - - - Your LaTeX installation does not include some required packages: - -- {} (https://ctan.org/pkg/{}) - -Make sure said package is installed, or disable the LaTeX rendering in LogarithmPlotter. - A LaTeX telepítése nem tartalmaz néhány szükséges csomagot: - -- {} (https://ctan.org/pkg/{}) - -Győződjön meg arról, hogy az említett csomag telepítve van, vagy tiltsa le a LaTeX megjelenítést a LogarithmPlotterben. - - - - An exception occured within the creation of the latex formula. -Process '{}' took too long to finish: -{} -Please make sure your latex installation is correct and report a bug if so. - Kivétel történt a LaTeX-képlet létrehozása során. -A(z) „{}” folyamat túl sokáig tartott a befejezéshez: -{} -Kérjük, ellenőrizze, hogy a LaTeX telepítése helyes-e, és ha igen, jelentse a hibát. - - - - main - - - This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}. -Please update LogarithmPlotter to open this file. - Ezt a fájlt a LogarithmPlotter egy újabb verziója hozta létre, és nem tölthető vissza a LogarithmPlotter v{} alkalmazásban. -Kérjük, frissítse a LogarithmPlottert a fájl megnyitásához. - - - - Could not open file "{}": -{} - Nem sikerült megnyitni a(z) „{}”-fájlt: -{} - - - - Could not open file: "{}" -File does not exist. - Nem sikerült megnyitni a(z) „{}”-fájlt: -A fájl nem létezik. - - - - Built with PySide6 (Qt) v{} and python v{} - PySide6 (Qt) v{} és python v{} segítségével épült - - - - name - - - - %1 %2 renamed to %3. - %1 %2 átnevezve erre: %3. - - - - parameters - - - above - ↑ Felett - - - - below - ↓ Alatt - - - - - left - ← Balra - - - - - right - → Jobbra - - - - above-left - ↖ Felett, balra - - - - above-right - ↗ Felett, jobbra - - - - below-left - ↙ Alatt, balra - - - - below-right - ↘ Alatt, jobbra - - - - center - >|< Középre - - - - top - ↑ Felső - - - - bottom - ↓ Alsó - - - - top-left - ↖ Bal felső - - - - top-right - ↗ Jobb felső - - - - bottom-left - ↙ Bal alsó - - - - bottom-right - ↘ Jobb alsó - - - - application - Alkalmazás - - - - function - Függvény - - - - high - Magas - - - - low - Alul - - - - Next to target - Cél mellé - - - - With label - Címkével - - - - Hidden - Rejtett - - - - phasebode - - Bode Phase - Bode-fázis - - - Bode Phases - Bode-fázisok - - - - point - - - Point - Pont - - - - Points - Pontok - - - - position - - - Position of %1 %2 set from "%3" to "%4". - %1 %2 áthelyezve innen: „%3” ide: „%4”. - - - - Position of %1 set from %2 to %3. - %1 áthelyezve innen: %2 ide: %3. - - - - prop - - - expression - Kifejezés - - - - definitionDomain - Abszcissza tartomány - - - - destinationDomain - Ordináta tartomány - - - - - - - - - - - - - labelPosition - Címke helyzete - - - - displayMode - Megjelenítési mód - - - - - - - - - - labelX - Címke X helyzete - - - - - drawPoints - Pontok megjelenítése - - - - - drawDashedLines - Szaggatott vonalak megjelenítése - - - - - om_0 - ω₀ - - - - pass - Áteresztő - - - - gain - Nagyságrend nyeresége - - - - omGraduation - ω₀ érettségi megjelenítése - - - - phase - Fázis - - - - unit - Egység használata - - - - - - x - X - - - - - y - Y - - - - pointStyle - Pontstílus - - - - probabilities - Valószínűségek listája - - - - text - Tartalom - - - - disableLatex - LaTeX-megjelenítés letiltása ennél a szövegnél - - - - targetElement - Tárgycél - - - - approximate - Kerekített számított érték megjelenítése - - - - rounding - Kerekítés - - - - displayStyle - Megjelenítési stílus - - - - targetValuePosition - Cél értékpozíciója - - - - defaultExpression - Alapértelmezett kifejezés - - - - baseValues - Kezdeményezési értékek - - - color - Szín - - - - labelContent - Címketartalom - - - - repartition - - Repartition - Elosztás - - - Repartition functions - Elosztási függvények - - - - sequence - - - Sequence - Sorozat - - - - Sequences - Sorozatok - - - - settingCategory - - - general - Általános - - - - editor - Kifejezésszerkesztő - - - - default - Alapértelmezett ábra - - - - sommegainsbode - - Bode Magnitudes Sum - Bode-nagyságrendek összege - - - - sommephasesbode - - Bode Phases Sum - Bode-fázisok összege - - - - text - - - Text - Szöveg - - - - Texts - Szövegek - - - - update - - - An update for LogarithmPlotter (v{}) is available. - Elérhető a Logaritmus-ábrázoló ({} verzió) frissítése. - - - - No update available. - Nincs telepíthető frissítés. - - - - Could not fetch update information: Server error {}. - Nem sikerült lekérni a frissítési adatokat: Kiszolgálóhiba: {}. - - - - Could not fetch update information: {}. - Nem sikerült lekérni a frissítési adatokat: {}. - - - - usage - - - integral(<from: number>, <to: number>, <f: ExecutableObject>) - integral(<alsó korlát: szám>, <felső korlát: szám>, <f: függvényszerű objektum>) - - - - - Usage: -%1 - Használat: -%1 - - - - - - Usage: -%1 -%2 - Használat: -%1 -%2 - - - - integral(<from: number>, <to: number>, <f: string>, <variable: string>) - integral(<alsó korlát: szám>, <felső korlát: szám>, <függvény: karakterlánc>, <változó: karakterlánc>) - - - - derivative(<f: ExecutableObject>, <x: number>) - derivative(<f: függvényszerű objektum>, <x: szám>) - - - - derivative(<f: string>, <variable: string>, <x: number>) - derivált(<függvény: karakterlánc>, <változó: karakterlánc>, <x: szám>) - - - - visibility - - - - %1 %2 shown. - %1 %2 megjelenítve. - - - - - %1 %2 hidden. - %1 %2 rejtve. - - - - xcursor - - - X Cursor - X kurzor - - - - X Cursors - X kurzorok - - - diff --git a/assets/i18n/lp_nb_NO.ts b/assets/i18n/lp_nb_NO.ts deleted file mode 100644 index 97799c6..0000000 --- a/assets/i18n/lp_nb_NO.ts +++ /dev/null @@ -1,1836 +0,0 @@ - - - - - About - - - About LogarithmPlotter - Om - - - - LogarithmPlotter v%1 - LogarithmPlotter v%1 - - - - 2D plotter software to make BODE plots, sequences and repartition functions. - 2D-plotterprogramvare laget for opprettelse av Bode-diagram, sekvenser, og distribusjonsfunksjoner. - - - - Report a bug - Rapporter en feil - - - - Official website - - - - - AppMenuBar - - - &File - &Fil - - - - &Load... - &Last inn … - - - - &Save - &Lagre - - - - Save &As... - Lagre &som … - - - - &Quit - &Avslutt - - - - &Edit - &Rediger - - - - &Undo - &Angre - - - - &Redo - &Gjenta - - - - &Copy plot - &Kopier plott - - - - &Preferences - - - - - &Create - &Opprett - - - &Settings - &Innstillinger - - - Check for updates on startup - Se etter nye versjoner ved programstart - - - Reset redo stack automaticly - Tilbakestill angrehistorikk automatisk - - - - &Help - &Hjelp - - - - &Source code - - - - - &Report a bug - &Rapporter en feil - - - - &User manual - - - - - &Changelog - &Endringslogg - - - - &Help translating! - &Hjelp til å oversette! - - - - &Thanks - &Erkjennelser - - - - &About - &Om - - - - Save unsaved changes? - Lagre ikke-lagrede endringer? - - - - This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? - Dette plottet inneholder ikke-lagrede endringer. Hvis du gjør dette, vil alle ikke-lagrede data gå tapt. Fortsette? - - - - BaseDialog - - - Close - Lukk - - - - BoolSetting - - Check for updates on startup - Se etter nye versjoner ved programstart - - - - Browser - - - Filter... - - - - - Redo > - Angre > - - - - > Now - > Nå - - - - < Undo - < Angre - - - - Changelog - - - Fetching changelog... - Henter endringslogg… - - - - Close - Lukk - - - - CustomPropertyList - - - - + Create new %1 - + Opprett nytt %1 - - - - Pick on graph - - - - - Dialog - - - Edit properties of %1 %2 - Rediger egenskaper for %1 %2 - - - - LogarithmPlotter - Invalid object name - LogarithmPlotter - Ugyldig objektnavn - - - - An object with the name '%1' already exists. - Et objekt med navnet '%1' finnes allerede. - - - - Name - Navn - - - Label content - Etikett-innhold - - - - null - NULL - - - - name - navn - - - - name + value - navn + veri - - - - EditorDialog - - Edit properties of %1 %2 - Rediger egenskaper for %1 %2 - - - Name - Navn - - - Label content - Etikett-innhold - - - null - NULL - - - name - navn - - - name + value - navn + veri - - - + Create new %1 - + Opprett nytt %1 - - - - ExpressionEditor - - - Object Properties - - - - - Variables - - - - - Constants - - - - - Functions - Funksjoner - - - - Executable Objects - - - - - Objects - Objekter - - - - FileDialog - - - Export Logarithm Plot file - Eksporter logaritmeplott-fil - - - - Import Logarithm Plot file - Importer logaritmeplott-fil - - - - GreetScreen - - - Welcome to LogarithmPlotter - Velkommen til LogarithmPlotter - - - - Version %1 - Versjon %1 - - - Take a few seconds to configure LogarithmPlotter. -These settings can be changed at any time from the "Settings" menu. - Sett opp LogarithmPlotter. -Disse innstillingene kan endres når som helst fra «Innstillinger»-menyen. - - - Check for updates on startup (requires online connectivity) - Se etter nye versjoner ved programstart. (Krever tilkobling til Internett.) - - - Reset redo stack when a new action is added to history - Tilbakesitll angrehistorikk når en ny handling legges til - - - - User manual - - - - - Changelog - - - - - Preferences - - - - - Close - Lukk - - - - HistoryBrowser - - Redo > - Angre > - - - > Now - > Nå - - - < Undo - < Angre - - - - ListSetting - - - + Add Entry - - - - - Loading - - - Loading... - - - - - Finished rendering of %1 - - - - - LogarithmPlotter - - - untitled - - - - - Objects - Objekter - - - - Settings - Innstillinger - - - - History - Historikk - - - Saved plot to '%1'. - Lagret plott i «%1». - - - Loading file '%1'. - Laster inn «%1»-fil. - - - Unknown object type: %1. - Ukjent objekttype: %1. - - - Invalid file provided. - Ugyldig fil angitt. - - - Could not save file: - Kunne ikke lagre fil: - - - Loaded file '%1'. - Lastet inn filen «%1». - - - - Copied plot screenshot to clipboard! - Kopierte plott-skjermavbildning til utklippstavlen! - - - - &Update - &Oppdater - - - - &Update LogarithmPlotter - &Installer ny versjon av LogartimePlotter - - - - ObjectCreationGrid - - - + Create new: - + Opprett ny: - - - - ObjectLists - - - Hide all %1 - Skjul alle %1 - - - - Show all %1 - Vis alle %1 - - - Hide %1 %2 - Skjul %1 %2 - - - Show %1 %2 - Vis %1 %2 - - - Set %1 %2 position - Sett %1 %2 posisjon - - - Delete %1 %2 - Slett %1 %2 - - - Pick new color for %1 %2 - Velg ny farge for %1 %2 - - - - ObjectRow - - - Hide %1 %2 - Skjul %1 %2 - - - - Show %1 %2 - Vis %1 %2 - - - - Set %1 %2 position - Sett %1 %2 posisjon - - - - Delete %1 %2 - Slett %1 %2 - - - - Pick new color for %1 %2 - Velg ny farge for %1 %2 - - - - PickLocation - - - Pointer precision: - Peker-presisjon: - - - - Snap to grid: - - - - - Pick X - - - - - Pick Y - - - - - Open picker settings - - - - - Hide picker settings - - - - - (no pick selected) - - - - - PickLocationOverlay - - Pointer precision: - Peker-presisjon: - - - Snap to grid - Fest til rutenett - - - - Preferences - - - Close - Lukk - - - - Settings - - - - X Zoom - X-forstørrelse - - - - - Y Zoom - Y-forstørrelse - - - - - Min X - Min. X - - - - - Max Y - Maks. Y - - - - Max X - Maks. X - - - - Min Y - Min. Y - - - - - X Axis Step - X-aksesteg - - - - - Y Axis Step - Y-aksesteg - - - - - Line width - Linjebredde - - - - - Text size (px) - Tekststørrelse (piksler) - - - - - X Label - Navn på X-akse - - - - - Y Label - Navn på Y-akse - - - - - X Log scale - Logaritmisk skala i x - - - - - Show X graduation - Vis X-inndeling - - - - - Show Y graduation - Vis Y-inndeling - - - - Copy to clipboard - Kopier til utklippstavle - - - - Save plot - Lagre plott - - - - Save plot as - Lagre plott som - - - - Load plot - Last inn plott - - - - ThanksTo - - - Thanks and Contributions - LogarithmPlotter - - - - - Source code - - - - - Original library by Raphael Graf - - - - - Source - - - - - Ported to Javascript by Matthew Crumley - Overført til JavaScript av Matthew Crumley - - - - - - Website - - - - - Ported to QMLJS by Ad5001 - - - - - Libraries included - - - - - Email - - - - - English - - - - - French - - - - - German - - - - - Hungarian - - - - - - - - Github - GitHub - - - - Norwegian - - - - - Spanish - - - - - Tamil - - - - - Translations included - - - - - Improve - - - - - bodemagnitude - - - Bode Magnitude - Bode-magnitude - - - - Bode Magnitudes - Bode-magnituder - - - - - low-pass - lavpass - - - - - high-pass - høypass - - - - bodemagnitudesum - - - - Bode Magnitudes Sum - Bode-magnitudesum - - - - bodephase - - - Bode Phase - Bode-fase - - - - Bode Phases - Bode-faser - - - - bodephasesum - - - - Bode Phases Sum - Bode-fasesum - - - - changelog - - - Could not fetch changelog: Server error {}. - - - - - - Could not fetch update: {}. - - - - - color - - - - %1 %2's color changed from %3 to %4. - - - - - comment - - - Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} - - - - - The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) - - - - - Note: Specify the probability for each value. - - - - - Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... - - - - - If you have latex enabled, you can use use latex markup in between $$ to create equations. - - - - - control - - - - - - - %1: - - - - - create - - - - New %1 %2 created. - Ny %1 %2 opprettet. - - - - delete - - - - %1 %2 deleted. - %1 %2 slettet. - - - - distribution - - - Repartition - Distribusjon - - - - Repartition functions - Distribusjonsfunksjoner - - - - editproperty - - - %1 of %2 %3 changed from "%4" to "%5". - %1 av %2 %3 endret fra «%4» til «%5». - - - - %1 of %2 changed from %3 to %4. - - - - - error - - - No object found with names %1. - - - - - No object found with name %1. - - - - - Object cannot be dependent on itself. - - - - - Circular dependency detected. Object %1 depends on %2. - - - - - Circular dependency detected. Objects %1 depend on %2. - - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - - - - - Error while attempting to draw %1 %2: -%3 - -Undoing last change. - - - - - - Cannot find property %1 of object %2. - - - - - Undefined variable %1. - - - - - In order to be executed, object %1 must have at least one argument. - - - - - %1 cannot be executed. - - - - - - - Invalid expression. - - - - - Invalid expression (parity). - - - - - EOF - - - - - - Parse error [position %1]: %2 - - - - - Expected %1 - - - - - Unexpected %1 - - - - - Unexpected ".": member access is not permitted - - - - - Unexpected "[]": arrays are disabled. - - - - - Unexpected symbol: %1. - - - - - - Function %1 must have at least one argument. - - - - - Unknown character "%1". - - - - - - Illegal escape sequence: %1. - - - - - expression - - - - LogarithmPlotter - Parsing error - - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - - - - - LogarithmPlotter - Drawing error - - - - - Automatically close parenthesises and brackets - - - - - Enable syntax highlighting - - - - - Enable autocompletion - - - - - Color Scheme - - - - - function - - - Function - Funksjon - - - - Functions - Funksjoner - - - - gainbode - - Bode Magnitude - Bode-magnitude - - - Bode Magnitudes - Bode-magnituder - - - low-pass - lavpass - - - high-pass - høypass - - - - general - - - Check for updates on startup - Se etter nye versjoner ved programstart - - - - Reset redo stack automaticly - Tilbakestill angrehistorikk automatisk - - - - Enable LaTeX rendering - - - - - Enable threaded LaTeX renderer (experimental) - - - - - historylib - - New %1 %2 created. - Ny %1 %2 opprettet. - - - %1 %2 deleted. - %1 %2 slettet. - - - %1 of %2 %3 changed from "%4" to "%5". - %1 av %2 %3 endret fra «%4» til «%5». - - - %1 %2 shown. - %1 %2 vist. - - - %1 %2 hidden. - %1 %2 skjult. - - - - io - - Objects - Objekter - - - Settings - Innstillinger - - - History - Historikk - - - Saved plot to '%1'. - Lagret plott i «%1». - - - Loading file '%1'. - Laster inn «%1»-fil. - - - Unknown object type: %1. - Ukjent objekttype: %1. - - - Invalid file provided. - Ugyldig fil angitt. - - - Could not save file: - Kunne ikke lagre fil: - - - Loaded file '%1'. - Lastet inn filen «%1». - - - Copied plot screenshot to clipboard! - Kopierte plott-skjermavbildning til utklippstavlen! - - - &Update - &Oppdater - - - &Update LogarithmPlotter - &Installer ny versjon av LogartimePlotter - - - - latex - - - No Latex installation found. -If you already have a latex distribution installed, make sure it's installed on your path. -Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. - - - - - DVIPNG was not found. Make sure you include it from your Latex distribution. - - - - - An exception occured within the creation of the latex formula. -Process '{}' ended with a non-zero return code {}: - -{} -Please make sure your latex installation is correct and report a bug if so. - - - - - Your LaTeX installation does not include some required packages: - -- {} (https://ctan.org/pkg/{}) - -Make sure said package is installed, or disable the LaTeX rendering in LogarithmPlotter. - - - - - An exception occured within the creation of the latex formula. -Process '{}' took too long to finish: -{} -Please make sure your latex installation is correct and report a bug if so. - - - - - main - - - This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}. -Please update LogarithmPlotter to open this file. - - - - - Could not open file "{}": -{} - - - - - Could not open file: "{}" -File does not exist. - - - - - Built with PySide6 (Qt) v{} and python v{} - - - - - name - - - - %1 %2 renamed to %3. - - - - - parameters - - - above - - - - - below - - - - - - left - - - - - - right - - - - - above-left - - - - - above-right - - - - - below-left - - - - - below-right - - - - - center - - - - - top - - - - - bottom - - - - - top-left - - - - - top-right - - - - - bottom-left - - - - - bottom-right - - - - - application - - - - - function - - - - - high - - - - - low - - - - - Next to target - - - - - With label - - - - - Hidden - - - - - phasebode - - Bode Phase - Bode-fase - - - Bode Phases - Bode-faser - - - - point - - - Point - Punkt - - - - Points - Punkter - - - - position - - - Position of %1 %2 set from "%3" to "%4". - - - - - Position of %1 set from %2 to %3. - - - - - prop - - - expression - - - - - definitionDomain - - - - - destinationDomain - - - - - displayMode - - - - - - - - - - - - - - labelPosition - - - - - - - - - - - labelX - - - - - - drawPoints - - - - - - drawDashedLines - - - - - - om_0 - - - - - pass - - - - - gain - - - - - omGraduation - - - - - phase - - - - - unit - - - - - - - x - - - - - - y - - - - - pointStyle - - - - - probabilities - - - - - defaultExpression - - - - - baseValues - - - - - text - - - - - disableLatex - - - - - targetElement - - - - - approximate - - - - - rounding - - - - - displayStyle - - - - - targetValuePosition - - - - - labelContent - Etikett-innhold - - - - repartition - - Repartition - Distribusjon - - - Repartition functions - Distribusjonsfunksjoner - - - - sequence - - - Sequence - Følge - - - - Sequences - Følger - - - - settingCategory - - - general - - - - - editor - - - - - default - - - - - sommegainsbode - - Bode Magnitudes Sum - Bode-magnitudesum - - - - sommephasesbode - - Bode Phases Sum - Bode-fasesum - - - - text - - - Text - Tekst - - - - Texts - Tekster - - - - update - - - An update for LogarithmPlotter (v{}) is available. - En ny versjon av LogartimePlotter (v{}) er tilgjengelig. - - - - No update available. - Ingen nye versjoner. - - - - Could not fetch update information: Server error {}. - Fant ikke ut om det er noen nye versjoner. Tjenerfeil {}. - - - - Could not fetch update information: {}. - Kunne ikke hente info om hvorvidt det er nye versjoner: {}. - - - - usage - - - integral(<from: number>, <to: number>, <f: ExecutableObject>) - - - - - - Usage: -%1 - - - - - - - Usage: -%1 -%2 - - - - - integral(<from: number>, <to: number>, <f: string>, <variable: string>) - - - - - derivative(<f: ExecutableObject>, <x: number>) - - - - - derivative(<f: string>, <variable: string>, <x: number>) - - - - - visibility - - - - %1 %2 shown. - %1 %2 vist. - - - - - %1 %2 hidden. - %1 %2 skjult. - - - - xcursor - - - X Cursor - X-peker - - - - X Cursors - X-pekere - - - diff --git a/assets/i18n/lp_ta.ts b/assets/i18n/lp_ta.ts deleted file mode 100644 index 2f3048c..0000000 --- a/assets/i18n/lp_ta.ts +++ /dev/null @@ -1,1597 +0,0 @@ - - - - - About - - - About LogarithmPlotter - மடக்கை பற்றி - - - - LogarithmPlotter v%1 - மடக்கை சதித்திட்டம் வி 1 - - - - 2D plotter software to make BODE plots, sequences and repartition functions. - போட் அடுக்கு, காட்சிகள் மற்றும் விநியோக செயல்பாடுகளை உருவாக்க 2 டி ப்ளாட்டர் மென்பொருள். - - - - Report a bug - ஒரு பிழையைப் புகாரளிக்கவும் - - - - Official website - அதிகாரப்பூர்வ வலைத்தளம் - - - - AppMenuBar - - - &File - கோப்பு (&f) - - - - &Load... - & திறந்த… - - - - &Save - சேமி (&s) - - - - Save &As... - சேமிக்கவும்… - - - - &Quit - &வெளியேறு - - - - &Edit - திருத்து (&e) - - - - &Undo - செயல்தவிர் (&u) - - - - &Redo - மீண்டும்செய் (&r) - - - - &Copy plot - & சூழ்ச்சி நகலெடுக்கவும் - - - - &Preferences - &விருப்பத்தேர்வுகள் - - - - &Create - & உருவாக்கு - - - - &Help - உதவி (&h) - - - - &Source code - & மூலக் குறியீடு - - - - &Report a bug - ஒரு பிழையைப் புகாரளிக்கவும் - - - - &User manual - & பயனர் கையேடு - - - - &Changelog - & சேஞ்ச்லாக் - - - - &Help translating! - & மொழிபெயர்க்க உதவுங்கள்! - - - - &Thanks - & நன்றி - - - - &About - &பற்றி - - - - Save unsaved changes? - சேமிக்கப்படாத மாற்றங்களைச் சேமிக்கவா? - - - - This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? - இந்த சதித்திட்டத்தில் சேமிக்கப்படாத மாற்றங்கள் உள்ளன. இதைச் செய்வதன் மூலம், சேமிக்கப்படாத அனைத்து தரவும் இழக்கப்படும். தொடரவா? - - - - BaseDialog - - - Close - மூடு - - - - Browser - - - Filter... - வடிகட்டி… - - - - Redo > - மீண்டும்> - - - - > Now - > இப்போது - - - - < Undo - <செயல்தவிர்க்கவும் - - - - Changelog - - - Fetching changelog... - சேஞ்ச்லாக் பெறுதல்… - - - - Close - மூடு - - - - CustomPropertyList - - - - + Create new %1 - + புதிய %1 ஐ உருவாக்கவும் - - - - Pick on graph - வரைபடத்தைத் தேர்ந்தெடுங்கள் - - - - Dialog - - - Edit properties of %1 %2 - %1 %2 இன் பண்புகளைத் திருத்தவும் - - - - LogarithmPlotter - Invalid object name - மடக்கை - தவறான பொருள் பெயர் - - - - An object with the name '%1' already exists. - '%1' என்ற பெயரைக் கொண்ட ஒரு பொருள் ஏற்கனவே உள்ளது. - - - - Name - பெயர் - - - - null - சுழியம் - - - - name - பெயர் - - - - name + value - பெயர் + மதிப்பு - - - - ExpressionEditor - - - Object Properties - பொருள் பண்புகள் - - - - Variables - மாறிகள் - - - - Constants - மாறிலிகள் - - - - Functions - செயல்பாடுகள் - - - - Executable Objects - செயல்பாடு பொருள்கள் - - - - Objects - பொருள்கள் - - - - FileDialog - - - Export Logarithm Plot file - மடக்கை சூழ்ச்சி கோப்பை ஏற்றுமதி செய்யுங்கள் - - - - Import Logarithm Plot file - மடக்கை சூழ்ச்சி கோப்பை இறக்குமதி செய்க - - - - GreetScreen - - - Welcome to LogarithmPlotter - மடக்கை பிளாட்டருக்கு வருக - - - - Version %1 - பதிப்பு %1 - - - - User manual - பயனர் கையேடு - - - - Changelog - மாற்றபதிவு - - - - Preferences - விருப்பத்தேர்வுகள் - - - - Close - மூடு - - - - ListSetting - - - + Add Entry - + உள்ளீட்டைச் சேர்க்கவும் - - - - Loading - - - Loading... - ஏற்றுகிறது… - - - - Finished rendering of %1 - %1 இன் வழங்குதல் முடிந்தது - - - - LogarithmPlotter - - - untitled - தலைப்பிடப்படாத - - - - Objects - பொருள்கள் - - - - Settings - அமைப்புகள் - - - - History - வரலாறு - - - - Copied plot screenshot to clipboard! - இடைநிலைப்பலகைக்கு சூழ்ச்சி திரை சாட்டை நகலெடுத்தது! - - - - &Update - & புதுப்பிக்கவும் - - - - &Update LogarithmPlotter - & மடக்கை புதுப்பிக்கவும் - - - - ObjectCreationGrid - - - + Create new: - + புதியதை உருவாக்கு: - - - - ObjectLists - - - Hide all %1 - அனைத்து %1 ஐ மறைக்கவும் - - - - Show all %1 - அனைத்து %1 ஐக் காட்டு - - - - ObjectRow - - - Hide %1 %2 - %1 %2 ஐ மறைக்கவும் - - - - Show %1 %2 - %1 %2 ஐக் காட்டு - - - - Set %1 %2 position - %1 %2 நிலையை அமைக்கவும் - - - - Delete %1 %2 - %1 %2 ஐ நீக்கு - - - - Pick new color for %1 %2 - %1 %2 க்கு புதிய வண்ணத்தைத் தேர்ந்தெடுங்கள் - - - - PickLocation - - - Pointer precision: - சுட்டிக்காட்டி துல்லியம்: - - - - Snap to grid: - கட்டம் வரை: - - - - Pick X - ஃச் பிக் - - - - Pick Y - ஒய் ஐ தேர்வு செய்யுங்கள் - - - - Open picker settings - திறந்த பிக்கர் அமைப்புகள் - - - - Hide picker settings - பிக்கர் அமைப்புகளை மறைக்கவும் - - - - (no pick selected) - (தேர்ந்தெடுக்கப்பட்ட தேர்வு இல்லை) - - - - Preferences - - - Close - மூடு - - - - Settings - - - - X Zoom - ஃச் சூம் - - - - - Y Zoom - மற்றும் பெரிதாக்கு - - - - - Min X - என் ஃச் - - - - - Max Y - அதிகபட்சம் மற்றும் - - - - Max X - அதிகபட்ச ஃச் - - - - Min Y - Min ஒய் - - - - - X Axis Step - ஃச் அச்சு படி - - - - - Y Axis Step - ஒய் அச்சு படி - - - - - Line width - வரி அகலம் - - - - - Text size (px) - உரை அளவு (பிஎக்ச்) - - - - - X Label - ஃச் சிட்டை - - - - - Y Label - ஒய் சிட்டை - - - - - X Log scale - ஃச் பதிவு அளவுகோல் - - - - - Show X graduation - ஃச் பட்டப்படிப்பைக் காட்டு - - - - - Show Y graduation - ஒய் பட்டப்படிப்பைக் காட்டு - - - - Copy to clipboard - இடைநிலைப்பலகைக்கு நகலெடுக்கவும் - - - - Save plot - சதித்திட்டத்தை சேமிக்கவும்… - - - - Save plot as - சூழ்ச்சி சேமிக்கவும்… - - - - Load plot - திறந்த சதி… - - - - ThanksTo - - - Thanks and Contributions - LogarithmPlotter - நன்றி மற்றும் பங்களிப்புகள் - மடக்கை - - - - Source code - மூலக் குறியீடு - - - - Original library by Raphael Graf - ரபேல் கிராஃப் எழுதிய அசல் நூலகம் - - - - Source - மூலம் - - - - Ported to Javascript by Matthew Crumley - மத்தேயு க்ரம்லி எழுதிய சாவாச்கிரிப்டுக்கு அனுப்பப்பட்டது - - - - - - Website - வலைத்தளம் - - - - Ported to QMLJS by Ad5001 - AD5001 ஆல் QMLJS க்கு அனுப்பப்பட்டது - - - - Libraries included - நூலகங்கள் சேர்க்கப்பட்டுள்ளன - - - - Email - மின்னஞ்சல் - - - - English - ஆங்கிலம் - - - - French - பிரஞ்சு - - - - German - செர்மன் - - - - Hungarian - அங்கேரியன் - - - - - - - Github - கிரப் - - - - Norwegian - நோர்வே - - - - Spanish - ச்பானிச் - - - - Tamil - தமிழ் - - - - Translations included - மொழிபெயர்ப்புகள் சேர்க்கப்பட்டுள்ளன - - - - Improve - மேம்படுத்தவும் - - - - bodemagnitude - - - Bode Magnitude - போட் அளவு - - - - Bode Magnitudes - போட் அளவுகள் - - - - - low-pass - குறைந்த பாச் - - - - - high-pass - உயர்-பாச் - - - - bodemagnitudesum - - - - Bode Magnitudes Sum - போட் அளவு தொகை - - - - bodephase - - - Bode Phase - போட் கட்டம் - - - - Bode Phases - போட் கட்டங்கள் - - - - bodephasesum - - - - Bode Phases Sum - போட் கட்டங்கள் தொகை - - - - changelog - - - Could not fetch changelog: Server error {}. - சேஞ்ச்லாக் பெற முடியவில்லை: சேவையக பிழை {}. - - - - - Could not fetch update: {}. - சேஞ்ச்லாக் பெற முடியவில்லை: {}. - - - - color - - - - %1 %2's color changed from %3 to %4. - %1 %2 இன் நிறம் %3 முதல் %4 வரை மாற்றப்பட்டது. - - - - comment - - - Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} - Ex: r+* (ℝ⁺*), n (ℕ), z-* (ℤ⁻*),] 0; 1 [, {3; 4; 5} - - - - The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) - டொமைன் ஒரு தொடர்ச்சியான தொகுப்பாக இருக்கும்போது பின்வரும் அளவுருக்கள் பயன்படுத்தப்படுகின்றன. (எ.கா: ℕ, ℤ, {0; 3}…) - - - - Note: Specify the probability for each value. - குறிப்பு: ஒவ்வொரு மதிப்புக்கும் நிகழ்தகவைக் குறிப்பிடவும். - - - - Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... - குறிப்பு: %1ₙ, %1 [n+1] ஐ %1ₙ₊₁ க்கு குறிக்க %1 [n] ஐப் பயன்படுத்தவும்… - - - - If you have latex enabled, you can use use latex markup in between $$ to create equations. - நீங்கள் லேடெக்ச் இயக்கப்பட்டிருந்தால், சமன்பாடுகளை உருவாக்க லேடெக்ச் மார்க்அப்பைப் பயன்படுத்தலாம். - - - - control - - - - - - - %1: - %1: - - - - create - - - - New %1 %2 created. - புதிய %1 %2 உருவாக்கப்பட்டது. - - - - delete - - - - %1 %2 deleted. - %1 %2 நீக்கப்பட்டது. - - - - distribution - - - Repartition - பரவல் - - - - Repartition functions - விநியோக செயல்பாடுகள் - - - - editproperty - - - %1 of %2 %3 changed from "%4" to "%5". - %2 %3 இல் %1 " %4" இலிருந்து " %5" ஆக மாற்றப்பட்டது. - - - - %1 of %2 changed from %3 to %4. - %2 இல் %1 %3 முதல் %4 வரை மாற்றப்பட்டது. - - - - error - - - No object found with names %1. - %1 பெயர்களைக் கொண்ட எந்த பொருளும் காணப்படவில்லை. - - - - No object found with name %1. - %1 என்ற பெயருடன் எந்த பொருளும் காணப்படவில்லை. - - - - Object cannot be dependent on itself. - பொருள் தன்னைச் சார்ந்து இருக்க முடியாது. - - - - Circular dependency detected. Object %1 depends on %2. - வட்ட சார்பு கண்டறியப்பட்டது. பொருள் %1 %2 ஐப் பொறுத்தது. - - - - Circular dependency detected. Objects %1 depend on %2. - வட்ட சார்பு கண்டறியப்பட்டது. பொருள்கள் %1 %2 ஐ சார்ந்துள்ளது. - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - சொத்து %1 க்கான வெளிப்பாட்டை பாகுபடுத்தும்போது பிழை: - %2 - - மதிப்பீடு செய்யப்பட்ட வெளிப்பாடு: %3 - - - - Error while attempting to draw %1 %2: -%3 - -Undoing last change. - %1 %2 ஐ வரைய முயற்சிக்கும் போது பிழை: - %3 - - கடைசி மாற்றத்தை செயல்தவிர்க்கவும். - - - - - Cannot find property %1 of object %2. - பொருள் %2 இன் சொத்து %1 ஐக் கண்டுபிடிக்க முடியாது. - - - - Undefined variable %1. - வரையறுக்கப்படாத மாறி %1. - - - - In order to be executed, object %1 must have at least one argument. - செயல்படுத்தப்படுவதற்கு, பொருள் %1 க்கு குறைந்தது ஒரு உரையாடல் இருக்க வேண்டும். - - - - %1 cannot be executed. - %1 ஒரு செயல்பாடு அல்ல. - - - - - - Invalid expression. - தவறான வெளிப்பாடு. - - - - Invalid expression (parity). - தவறான வெளிப்பாடு (சமநிலை). - - - - EOF - வெளிப்பாட்டின் முடிவு - - - - - Parse error [position %1]: %2 - பாகுபடுத்தும் பிழை [நிலை %1]: %2 - - - - Expected %1 - எதிர்பார்க்கப்படும் %1 - - - - Unexpected %1 - எதிர்பாராத %1 - - - - Unexpected ".": member access is not permitted - எதிர்பாராதது ".": உறுப்பினர் அணுகல் அனுமதிக்கப்படவில்லை - - - - Unexpected "[]": arrays are disabled. - எதிர்பாராத "[]": வரிசைகள் முடக்கப்பட்டுள்ளன. - - - - Unexpected symbol: %1. - எதிர்பாராத சின்னம்: %1. - - - - - Function %1 must have at least one argument. - செயல்பாடு %1 க்கு குறைந்தது ஒரு உரையாடல் இருக்க வேண்டும். - - - - Unknown character "%1". - அறியப்படாத எழுத்து "%1". - - - - - Illegal escape sequence: %1. - சட்டவிரோத தப்பிக்கும் வரிசை: %1. - - - - expression - - - - LogarithmPlotter - Parsing error - மடக்கை பிளாட்டர் - பாகுபடுத்தும் பிழை - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - சொத்து %1 க்கான வெளிப்பாட்டை பாகுபடுத்தும்போது பிழை: - %2 - - மதிப்பீடு செய்யப்பட்ட வெளிப்பாடு: %3 - - - - LogarithmPlotter - Drawing error - மடக்கை - வரைதல் பிழை - - - - Automatically close parenthesises and brackets - தானாகவே அடைப்புக்குறிப்புகள் மற்றும் அடைப்புக்குறிகளை மூடு - - - - Enable syntax highlighting - தொடரியல் சிறப்பம்சத்தை இயக்கவும் - - - - Enable autocompletion - தன்னியக்கவியல் இயக்கவும் - - - - Color Scheme - வண்ணத் திட்டம் - - - - function - - - Function - சார்பு - - - - Functions - செயல்பாடுகள் - - - - general - - - Check for updates on startup - தொடக்கத்தின் புதுப்பிப்புகளைச் சரிபார்க்கவும் - - - - Reset redo stack automaticly - மீண்டும் அடுக்கை மீட்டமைக்கவும் - - - - Enable LaTeX rendering - லேடெக்ச் வழங்குதல் இயக்கவும் - - - - Enable threaded LaTeX renderer (experimental) - திரிக்கப்பட்ட லேடெக்ச் ரெண்டரரை இயக்கவும் (சோதனை) - - - - latex - - - No Latex installation found. -If you already have a latex distribution installed, make sure it's installed on your path. -Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. - லேடெக்ச் நிறுவல் எதுவும் கிடைக்கவில்லை. - உங்களிடம் ஏற்கனவே லேடெக்ச் வழங்கல் நிறுவப்பட்டிருந்தால், அது உங்கள் பாதையில் நிறுவப்பட்டுள்ளதா என்பதை உறுதிப்படுத்திக் கொள்ளுங்கள். - இல்லையெனில், டெக்ச் லைவ் போன்ற லேடெக்ச் விநியோகத்தை https://tug.org/texlive/ இல் பதிவிறக்கம் செய்யலாம். - - - - DVIPNG was not found. Make sure you include it from your Latex distribution. - Dvipng கண்டுபிடிக்கப்படவில்லை. உங்கள் லேடெக்ச் விநியோகத்திலிருந்து இதைச் சேர்த்துள்ளீர்கள் என்பதை உறுதிப்படுத்திக் கொள்ளுங்கள். - - - - An exception occured within the creation of the latex formula. -Process '{}' ended with a non-zero return code {}: - -{} -Please make sure your latex installation is correct and report a bug if so. - லேடெக்ச் சூத்திரத்தை உருவாக்குவதற்குள் விதிவிலக்கு ஏற்பட்டது. - '{}' ஐ பூச்சியமற்ற வருவாய் குறியீட்டோடு முடிந்தது {}: - - {} - உங்கள் லேடெக்ச் நிறுவல் சரியானது என்பதை உறுதிப்படுத்தவும், அப்படியானால் ஒரு பிழையைப் புகாரளிக்கவும். - - - - Your LaTeX installation does not include some required packages: - -- {} (https://ctan.org/pkg/{}) - -Make sure said package is installed, or disable the LaTeX rendering in LogarithmPlotter. - உங்கள் லேடெக்ச் நிறுவலில் தேவையான சில தொகுப்புகள் இல்லை: - - - {} (https://ctan.org/pkg/ {}) - - தொகுப்பு நிறுவப்பட்டிருப்பதை உறுதிசெய்து கொள்ளுங்கள், அல்லது லோகரிதம்லட்டரில் லேடெக்ச் வழங்குதல் முடக்கவும். - - - - An exception occured within the creation of the latex formula. -Process '{}' took too long to finish: -{} -Please make sure your latex installation is correct and report a bug if so. - லேடெக்ச் சூத்திரத்தை உருவாக்குவதற்குள் விதிவிலக்கு ஏற்பட்டது. - '{}' செயல்முறை முடிக்க அதிக நேரம் எடுத்தது: - {} - உங்கள் லேடெக்ச் நிறுவல் சரியானது என்பதை உறுதிப்படுத்தவும், அப்படியானால் ஒரு பிழையைப் புகாரளிக்கவும். - - - - main - - - This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}. -Please update LogarithmPlotter to open this file. - இந்த கோப்பு மடக்கை படத்தின் மிக அண்மைக் கால பதிப்பால் உருவாக்கப்பட்டது, மேலும் லோகரிதம் பிளாட்டர் v {fol இல் பின்வாங்க முடியாது. - இந்த கோப்பைத் திறக்க லோகரிதில் பிளாட்டரைப் புதுப்பிக்கவும். - - - - Could not open file "{}": -{} - "{}" கோப்பைத் திறக்க முடியவில்லை: - {} - - - - Could not open file: "{}" -File does not exist. - கோப்பைத் திறக்க முடியவில்லை: "{}" - கோப்பு இல்லை. - - - - Built with PySide6 (Qt) v{} and python v{} - Pyside6 (qt) V {} மற்றும் பைதான் V {with உடன் கட்டப்பட்டுள்ளது - - - - name - - - - %1 %2 renamed to %3. - %1 %2 %3 என மறுபெயரிடப்பட்டது. - - - - parameters - - - above - மேலே - - - - below - கீழே - - - - - left - . இடது - - - - - right - சரி - - - - above-left - இடதுபுறம் மேலே - - - - above-right - ↗ மேலே வலதுபுறம் - - - - below-left - இடதுபுறமாக கீழே - - - - below-right - ↘ கீழே வலது கீழே - - - - center - > | <மையம் - - - - top - . மேல் - - - - bottom - கீழே - - - - top-left - ↖ மேல் இடது - - - - top-right - ↗ மேல் வலது - - - - bottom-left - ↙ கீழ் இடது - - - - bottom-right - வலது வலது - - - - application - பயன்பாடு - - - - function - சார்பு - - - - high - உயர்ந்த - - - - low - குறைந்த - - - - Next to target - இலக்கு அடுத்து - - - - With label - லேபிளுடன் - - - - Hidden - மறைக்கப்பட்ட - - - - point - - - Point - புள்ளியம் - - - - Points - பிரிவகம் - - - - position - - - Position of %1 %2 set from "%3" to "%4". - %1%2 "%3" இலிருந்து "%4" க்கு நகர்ந்தது. - - - - Position of %1 set from %2 to %3. - %1 %2 முதல் %3 வரை நகர்ந்தது. - - - - prop - - - expression - கோவை - - - - definitionDomain - டொமைன் - - - - destinationDomain - வீச்சு - - - - displayMode - காட்சி முறை - - - - - - - - - - - - - labelPosition - சிட்டை நிலை - - - - - - - - - - labelX - லேபிளின் ஃச் நிலை - - - - - drawPoints - புள்ளிகளைக் காட்டு - - - - - drawDashedLines - கோடு கோடுகளைக் காட்டு - - - - - om_0 - - - - - pass - கணவாய் - - - - gain - அளவு ஆதாயம் - - - - omGraduation - Ω₀ இல் பட்டப்படிப்பைக் காட்டு - - - - phase - கட்டம் - - - - unit - பயன்படுத்த அலகு - - - - - - x - ஃச் - - - - - y - ஒய் - - - - pointStyle - புள்ளி நடை - - - - probabilities - நிகழ்தகவுகள் பட்டியல் - - - - defaultExpression - இயல்புநிலை வெளிப்பாடு - - - - baseValues - துவக்க மதிப்புகள் - - - - text - உள்ளடக்கம் - - - - disableLatex - இந்த உரைக்கு லேடெக்ச் வழங்குதல் முடக்கு - - - - targetElement - இலக்கை எதிர்க்கவும் - - - - approximate - வட்டமான கணக்கிடப்பட்ட மதிப்பைக் காட்டு - - - - rounding - சுற்று - - - - displayStyle - காட்சி நடை - - - - targetValuePosition - இலக்கின் மதிப்பு நிலை - - - - labelContent - சிட்டை உள்ளடக்கம் - - - - sequence - - - Sequence - வரிசை - - - - Sequences - வரிசைகள் - - - - settingCategory - - - general - பொது - - - - editor - வெளிப்பாடு ஆசிரியர் - - - - default - இயல்புநிலை அமைப்புகள் - - - - text - - - Text - உரை - - - - Texts - நூல்கள் - - - - update - - - An update for LogarithmPlotter (v{}) is available. - மடக்கை (v {}) க்கான புதுப்பிப்பு கிடைக்கிறது. - - - - No update available. - புதுப்பிப்பு எதுவும் கிடைக்கவில்லை. - - - - Could not fetch update information: Server error {}. - புதுப்பிப்பு தகவலைப் பெற முடியவில்லை: சேவையக பிழை {}. - - - - Could not fetch update information: {}. - புதுப்பிப்பு தகவல்களைப் பெற முடியவில்லை: {}. - - - - usage - - - integral(<from: number>, <to: number>, <f: ExecutableObject>) - ஒருங்கிணைந்த (<இலிருந்து: எண்>, <க்கு: எண்>, <f: செயல்பாடு போன்ற பொருள்>) - - - - - Usage: -%1 - பயன்பாடு: - %1 - - - - - - Usage: -%1 -%2 - பயன்பாடு: - %1 - %2 - - - - integral(<from: number>, <to: number>, <f: string>, <variable: string>) - ஒருங்கிணைந்த (<இலிருந்து: எண்>, <க்கு: எண்>, <f: சரம்>, <மாறி: சரம்>) - - - - derivative(<f: ExecutableObject>, <x: number>) - வழித்தோன்றல் (<f: செயல்பாடு போன்ற பொருள்>, <x: எண்>) - - - - derivative(<f: string>, <variable: string>, <x: number>) - வழித்தோன்றல் (<f: சரம்>, <மாறி: சரம்>, <x: எண்>) - - - - visibility - - - - %1 %2 shown. - %1 %2 காட்டப்பட்டுள்ளது. - - - - - %1 %2 hidden. - %1 %2 மறைக்கப்பட்டுள்ளது. - - - - xcursor - - - X Cursor - ஃச் கர்சர் - - - - X Cursors - ஃச் கர்சர்கள் - - - diff --git a/assets/i18n/lp_template.ts b/assets/i18n/lp_template.ts deleted file mode 100644 index 7f1bc28..0000000 --- a/assets/i18n/lp_template.ts +++ /dev/null @@ -1,1569 +0,0 @@ - - - - - About - - - About LogarithmPlotter - - - - - LogarithmPlotter v%1 - - - - - 2D plotter software to make BODE plots, sequences and repartition functions. - - - - - Report a bug - - - - - Official website - - - - - AppMenuBar - - - &File - - - - - &Load... - - - - - &Save - - - - - Save &As... - - - - - &Quit - - - - - &Edit - - - - - &Undo - - - - - &Redo - - - - - &Copy plot - - - - - &Preferences - - - - - &Create - - - - - &Help - - - - - &Source code - - - - - &Report a bug - - - - - &User manual - - - - - &Changelog - - - - - &Help translating! - - - - - &Thanks - - - - - &About - - - - - Save unsaved changes? - - - - - This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? - - - - - BaseDialog - - - Close - - - - - Browser - - - Filter... - - - - - Redo > - - - - - > Now - - - - - < Undo - - - - - Changelog - - - Fetching changelog... - - - - - Close - - - - - CustomPropertyList - - - - + Create new %1 - - - - - Pick on graph - - - - - Dialog - - - Edit properties of %1 %2 - - - - - LogarithmPlotter - Invalid object name - - - - - An object with the name '%1' already exists. - - - - - Name - - - - - null - - - - - name - - - - - name + value - - - - - ExpressionEditor - - - Object Properties - - - - - Variables - - - - - Constants - - - - - Functions - - - - - Executable Objects - - - - - Objects - - - - - FileDialog - - - Export Logarithm Plot file - - - - - Import Logarithm Plot file - - - - - GreetScreen - - - Welcome to LogarithmPlotter - - - - - Version %1 - - - - - User manual - - - - - Changelog - - - - - Preferences - - - - - Close - - - - - ListSetting - - - + Add Entry - - - - - Loading - - - Loading... - - - - - Finished rendering of %1 - - - - - LogarithmPlotter - - - untitled - - - - - Objects - - - - - Settings - - - - - History - - - - - Copied plot screenshot to clipboard! - - - - - &Update - - - - - &Update LogarithmPlotter - - - - - ObjectCreationGrid - - - + Create new: - - - - - ObjectLists - - - Hide all %1 - - - - - Show all %1 - - - - - ObjectRow - - - Hide %1 %2 - - - - - Show %1 %2 - - - - - Set %1 %2 position - - - - - Delete %1 %2 - - - - - Pick new color for %1 %2 - - - - - PickLocation - - - Pointer precision: - - - - - Snap to grid: - - - - - Pick X - - - - - Pick Y - - - - - Open picker settings - - - - - Hide picker settings - - - - - (no pick selected) - - - - - Preferences - - - Close - - - - - Settings - - - - X Zoom - - - - - - Y Zoom - - - - - - Min X - - - - - - Max Y - - - - - Max X - - - - - Min Y - - - - - - X Axis Step - - - - - - Y Axis Step - - - - - - Line width - - - - - - Text size (px) - - - - - - X Label - - - - - - Y Label - - - - - - X Log scale - - - - - - Show X graduation - - - - - - Show Y graduation - - - - - Copy to clipboard - - - - - Save plot - - - - - Save plot as - - - - - Load plot - - - - - ThanksTo - - - Thanks and Contributions - LogarithmPlotter - - - - - Source code - - - - - Original library by Raphael Graf - - - - - Source - - - - - Ported to Javascript by Matthew Crumley - - - - - - - Website - - - - - Ported to QMLJS by Ad5001 - - - - - Libraries included - - - - - Email - - - - - English - - - - - French - - - - - German - - - - - Hungarian - - - - - - - - Github - - - - - Norwegian - - - - - Spanish - - - - - Tamil - - - - - Translations included - - - - - Improve - - - - - bodemagnitude - - - Bode Magnitude - - - - - Bode Magnitudes - - - - - - low-pass - - - - - - high-pass - - - - - bodemagnitudesum - - - - Bode Magnitudes Sum - - - - - bodephase - - - Bode Phase - - - - - Bode Phases - - - - - bodephasesum - - - - Bode Phases Sum - - - - - changelog - - - Could not fetch changelog: Server error {}. - - - - - - Could not fetch update: {}. - - - - - color - - - - %1 %2's color changed from %3 to %4. - - - - - comment - - - Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5} - - - - - The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...) - - - - - Note: Specify the probability for each value. - - - - - Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁... - - - - - If you have latex enabled, you can use use latex markup in between $$ to create equations. - - - - - control - - - - - - - %1: - - - - - create - - - - New %1 %2 created. - - - - - delete - - - - %1 %2 deleted. - - - - - distribution - - - Repartition - - - - - Repartition functions - - - - - editproperty - - - %1 of %2 %3 changed from "%4" to "%5". - - - - - %1 of %2 changed from %3 to %4. - - - - - error - - - No object found with names %1. - - - - - No object found with name %1. - - - - - Object cannot be dependent on itself. - - - - - Circular dependency detected. Object %1 depends on %2. - - - - - Circular dependency detected. Objects %1 depend on %2. - - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - - - - - Error while attempting to draw %1 %2: -%3 - -Undoing last change. - - - - - - Cannot find property %1 of object %2. - - - - - Undefined variable %1. - - - - - In order to be executed, object %1 must have at least one argument. - - - - - %1 cannot be executed. - - - - - - - Invalid expression. - - - - - Invalid expression (parity). - - - - - EOF - - - - - - Parse error [position %1]: %2 - - - - - Expected %1 - - - - - Unexpected %1 - - - - - Unexpected ".": member access is not permitted - - - - - Unexpected "[]": arrays are disabled. - - - - - Unexpected symbol: %1. - - - - - - Function %1 must have at least one argument. - - - - - Unknown character "%1". - - - - - - Illegal escape sequence: %1. - - - - - expression - - - - LogarithmPlotter - Parsing error - - - - - Error while parsing expression for property %1: -%2 - -Evaluated expression: %3 - - - - - LogarithmPlotter - Drawing error - - - - - Automatically close parenthesises and brackets - - - - - Enable syntax highlighting - - - - - Enable autocompletion - - - - - Color Scheme - - - - - function - - - Function - - - - - Functions - - - - - general - - - Check for updates on startup - - - - - Reset redo stack automaticly - - - - - Enable LaTeX rendering - - - - - Enable threaded LaTeX renderer (experimental) - - - - - latex - - - No Latex installation found. -If you already have a latex distribution installed, make sure it's installed on your path. -Otherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/. - - - - - DVIPNG was not found. Make sure you include it from your Latex distribution. - - - - - An exception occured within the creation of the latex formula. -Process '{}' ended with a non-zero return code {}: - -{} -Please make sure your latex installation is correct and report a bug if so. - - - - - Your LaTeX installation does not include some required packages: - -- {} (https://ctan.org/pkg/{}) - -Make sure said package is installed, or disable the LaTeX rendering in LogarithmPlotter. - - - - - An exception occured within the creation of the latex formula. -Process '{}' took too long to finish: -{} -Please make sure your latex installation is correct and report a bug if so. - - - - - main - - - This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}. -Please update LogarithmPlotter to open this file. - - - - - Could not open file "{}": -{} - - - - - Could not open file: "{}" -File does not exist. - - - - - Built with PySide6 (Qt) v{} and python v{} - - - - - name - - - - %1 %2 renamed to %3. - - - - - parameters - - - above - - - - - below - - - - - - left - - - - - - right - - - - - above-left - - - - - above-right - - - - - below-left - - - - - below-right - - - - - center - - - - - top - - - - - bottom - - - - - top-left - - - - - top-right - - - - - bottom-left - - - - - bottom-right - - - - - application - - - - - function - - - - - high - - - - - low - - - - - Next to target - - - - - With label - - - - - Hidden - - - - - point - - - Point - - - - - Points - - - - - position - - - Position of %1 %2 set from "%3" to "%4". - - - - - Position of %1 set from %2 to %3. - - - - - prop - - - expression - - - - - definitionDomain - - - - - destinationDomain - - - - - displayMode - - - - - - - - - - - - - - labelPosition - - - - - - - - - - - labelX - - - - - - drawPoints - - - - - - drawDashedLines - - - - - - om_0 - - - - - pass - - - - - gain - - - - - omGraduation - - - - - phase - - - - - unit - - - - - - - x - - - - - - y - - - - - pointStyle - - - - - probabilities - - - - - defaultExpression - - - - - baseValues - - - - - text - - - - - disableLatex - - - - - targetElement - - - - - approximate - - - - - rounding - - - - - displayStyle - - - - - targetValuePosition - - - - - labelContent - - - - - sequence - - - Sequence - - - - - Sequences - - - - - settingCategory - - - general - - - - - editor - - - - - default - - - - - text - - - Text - - - - - Texts - - - - - update - - - An update for LogarithmPlotter (v{}) is available. - - - - - No update available. - - - - - Could not fetch update information: Server error {}. - - - - - Could not fetch update information: {}. - - - - - usage - - - integral(<from: number>, <to: number>, <f: ExecutableObject>) - - - - - - Usage: -%1 - - - - - - - Usage: -%1 -%2 - - - - - integral(<from: number>, <to: number>, <f: string>, <variable: string>) - - - - - derivative(<f: ExecutableObject>, <x: number>) - - - - - derivative(<f: string>, <variable: string>, <x: number>) - - - - - visibility - - - - %1 %2 shown. - - - - - - %1 %2 hidden. - - - - - xcursor - - - X Cursor - - - - - X Cursors - - - - diff --git a/assets/i18n/release.sh b/assets/i18n/release.sh deleted file mode 100755 index 58477c3..0000000 --- a/assets/i18n/release.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -pyside6-lrelease *.ts diff --git a/assets/i18n/update.sh b/assets/i18n/update.sh deleted file mode 100755 index 98f4201..0000000 --- a/assets/i18n/update.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash -# -# This file automatically renames .mjs files to js, and (tries) to fix most common ECMAScript -# specificities so that lupdate doesn't cry out in pain. -# See also: https://bugreports.qt.io/browse/QTBUG-123819 -# - -escape() { - str="$1" - str="${str//\//\\/}" # Escape slashes - str="${str//\*/\\*}" # Escape asterixes - echo "$str" -} - -replace() { - file="$1" - from="$(escape "$2")" - to="$(escape "$3")" - sed -i "s/${from}/${to}/g" "$file" -} - -rm ../qml/eu/ad5001/LogarithmPlotter/js/index.mjs # Remove index which should not be scanned - -files=$(find ../../common/src -name '*.mjs') -for file in $files; do - echo "Moving '$file' to '${file%.*}.js'..." - mv "$file" "${file%.*}.js" - # Replacements to make it valid js - replace "${file%.*}.js" "^import" "/*import" - replace "${file%.*}.js" "^export *" "/*export *" - replace "${file%.*}.js" '.mjs"$' '.mjs"*/' - replace "${file%.*}.js" "^export default" "/*export default*/" - replace "${file%.*}.js" "^export" "/*export*/" - replace "${file%.*}.js" "async " "/*async */" - replace "${file%.*}.js" "await" "/*await */" - replace "${file%.*}.js" " #" "// #" - replace "${file%.*}.js" "this.#" "/*this.#*/" -done - -echo "----------------------------" -echo "| Updating translations... |" -echo "----------------------------" -pyside6-lupdate -extensions js,qs,qml,py -recursive ../../common/src -recursive ../../runtime-pyside6/LogarithmPlotter -ts lp_*.ts -# Updating locations in files -for lp in *.ts; do - echo "Replacing locations in $lp..." - for file in $files; do - replace "$lp" "${file%.*}.js" "$file" - done -done - -for file in $files; do - echo "Moving '${file%.*}.js' to '$file'..." - mv "${file%.*}.js" "$file" - # Resetting changes - replace "$file" "/*await */" "await" - replace "$file" "/*async */" "async " - replace "$file" "^/*export*/" "export" - replace "$file" "^/*export default*/" "export default" - replace "$file" '.mjs"*/' '.mjs"' - replace "$file" "^/*import" "import" - replace "$file" "^/*export" "export" - replace "$file" "// #" " #" - replace "$file" "/*this.#*/" "this.#" -done diff --git a/assets/icons/common/manual.svg b/assets/icons/common/manual.svg deleted file mode 100644 index 9ca9b8e..0000000 --- a/assets/icons/common/manual.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/icons/common/new.svg b/assets/icons/common/new.svg deleted file mode 100644 index 7c7f6fd..0000000 --- a/assets/icons/common/new.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/icons/common/settings.svg b/assets/icons/common/settings.svg deleted file mode 100644 index 4fb694d..0000000 --- a/assets/icons/common/settings.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/icons/history/delete.svg b/assets/icons/history/delete.svg deleted file mode 120000 index dca0794..0000000 --- a/assets/icons/history/delete.svg +++ /dev/null @@ -1 +0,0 @@ -../common/close.svg \ No newline at end of file diff --git a/assets/icons/objects/Function.svg b/assets/icons/objects/Function.svg deleted file mode 100644 index 6d707ce..0000000 --- a/assets/icons/objects/Function.svg +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - image/svg+xml - - 2021-2023 - - - Ad5001 - - - - - (C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA - - - - - - - - - - - - - - - f - x - ( - ) - - diff --git a/assets/icons/objects/Gain Bode.svg b/assets/icons/objects/Gain Bode.svg deleted file mode 100644 index 3a2b85f..0000000 --- a/assets/icons/objects/Gain Bode.svg +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - image/svg+xml - - 2021-2023 - - - Ad5001 - - - - - (C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA - - - - - - - - - - - - - - - - - - ω - - - - diff --git a/assets/icons/objects/Phase Bode.svg b/assets/icons/objects/Phase Bode.svg deleted file mode 100644 index 9bb60bf..0000000 --- a/assets/icons/objects/Phase Bode.svg +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - image/svg+xml - - 2021-2023 - - - Ad5001 - - - - - (C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA - - - - - - - - - - - - - - - - - - - - φ - - diff --git a/assets/icons/objects/Point.svg b/assets/icons/objects/Point.svg deleted file mode 100644 index d8de8b0..0000000 --- a/assets/icons/objects/Point.svg +++ /dev/null @@ -1,67 +0,0 @@ - - - - - A - - - - 2021-2023 - - - Ad5001 - - - - - (C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA - - - - - - - - - - - - - - - - diff --git a/assets/icons/objects/Sequence.svg b/assets/icons/objects/Sequence.svg deleted file mode 100644 index 75c08f1..0000000 --- a/assets/icons/objects/Sequence.svg +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - image/svg+xml - - 2021-2023 - - - Ad5001 - - - - - (C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA - - - - - - - u - n - ( - ) - - diff --git a/assets/icons/objects/X Cursor.svg b/assets/icons/objects/X Cursor.svg deleted file mode 100644 index aeb04c8..0000000 --- a/assets/icons/objects/X Cursor.svg +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - image/svg+xml - - 2021-2023 - - - Ad5001 - - - - - (C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA - - - - - - - - - - - - - - - - - - - X - - diff --git a/assets/icons/properties/displayMode.svg b/assets/icons/properties/displayMode.svg deleted file mode 120000 index 41b711b..0000000 --- a/assets/icons/properties/displayMode.svg +++ /dev/null @@ -1 +0,0 @@ -../common/appearance.svg \ No newline at end of file diff --git a/assets/icons/properties/displayStyle.svg b/assets/icons/properties/displayStyle.svg deleted file mode 120000 index 41b711b..0000000 --- a/assets/icons/properties/displayStyle.svg +++ /dev/null @@ -1 +0,0 @@ -../common/appearance.svg \ No newline at end of file diff --git a/assets/icons/properties/labelPosition.svg b/assets/icons/properties/labelPosition.svg deleted file mode 120000 index 3e4c849..0000000 --- a/assets/icons/properties/labelPosition.svg +++ /dev/null @@ -1 +0,0 @@ -../common/arrow.svg \ No newline at end of file diff --git a/assets/icons/properties/labelX.svg b/assets/icons/properties/labelX.svg deleted file mode 120000 index 4eecad3..0000000 --- a/assets/icons/properties/labelX.svg +++ /dev/null @@ -1 +0,0 @@ -../common/position.svg \ No newline at end of file diff --git a/assets/icons/properties/om_0.svg b/assets/icons/properties/om_0.svg deleted file mode 120000 index e4130be..0000000 --- a/assets/icons/properties/om_0.svg +++ /dev/null @@ -1 +0,0 @@ -../common/angle.svg \ No newline at end of file diff --git a/assets/icons/properties/phase.svg b/assets/icons/properties/phase.svg deleted file mode 120000 index e4130be..0000000 --- a/assets/icons/properties/phase.svg +++ /dev/null @@ -1 +0,0 @@ -../common/angle.svg \ No newline at end of file diff --git a/assets/icons/properties/pointStyle.svg b/assets/icons/properties/pointStyle.svg deleted file mode 120000 index 41b711b..0000000 --- a/assets/icons/properties/pointStyle.svg +++ /dev/null @@ -1 +0,0 @@ -../common/appearance.svg \ No newline at end of file diff --git a/assets/icons/properties/targetElement.svg b/assets/icons/properties/targetElement.svg deleted file mode 120000 index 659962b..0000000 --- a/assets/icons/properties/targetElement.svg +++ /dev/null @@ -1 +0,0 @@ -../common/target.svg \ No newline at end of file diff --git a/assets/icons/properties/targetValuePosition.svg b/assets/icons/properties/targetValuePosition.svg deleted file mode 120000 index 4eecad3..0000000 --- a/assets/icons/properties/targetValuePosition.svg +++ /dev/null @@ -1 +0,0 @@ -../common/position.svg \ No newline at end of file diff --git a/assets/icons/properties/text.svg b/assets/icons/properties/text.svg deleted file mode 120000 index 280f9ad..0000000 --- a/assets/icons/properties/text.svg +++ /dev/null @@ -1 +0,0 @@ -../common/label.svg \ No newline at end of file diff --git a/assets/icons/properties/unit.svg b/assets/icons/properties/unit.svg deleted file mode 120000 index e4130be..0000000 --- a/assets/icons/properties/unit.svg +++ /dev/null @@ -1 +0,0 @@ -../common/angle.svg \ No newline at end of file diff --git a/assets/icons/properties/x.svg b/assets/icons/properties/x.svg deleted file mode 120000 index 4eecad3..0000000 --- a/assets/icons/properties/x.svg +++ /dev/null @@ -1 +0,0 @@ -../common/position.svg \ No newline at end of file diff --git a/assets/icons/properties/y.svg b/assets/icons/properties/y.svg deleted file mode 120000 index 4eecad3..0000000 --- a/assets/icons/properties/y.svg +++ /dev/null @@ -1 +0,0 @@ -../common/position.svg \ No newline at end of file diff --git a/assets/icons/settings/color.svg b/assets/icons/settings/color.svg deleted file mode 120000 index 41b711b..0000000 --- a/assets/icons/settings/color.svg +++ /dev/null @@ -1 +0,0 @@ -../common/appearance.svg \ No newline at end of file diff --git a/assets/icons/settings/label.svg b/assets/icons/settings/label.svg deleted file mode 120000 index 280f9ad..0000000 --- a/assets/icons/settings/label.svg +++ /dev/null @@ -1 +0,0 @@ -../common/label.svg \ No newline at end of file diff --git a/assets/icons/settings/text.svg b/assets/icons/settings/text.svg deleted file mode 120000 index a705389..0000000 --- a/assets/icons/settings/text.svg +++ /dev/null @@ -1 +0,0 @@ -../common/text.svg \ No newline at end of file diff --git a/assets/logarithmplotter.svg b/assets/logarithmplotter.svg deleted file mode 100644 index 1f3a5e0..0000000 --- a/assets/logarithmplotter.svg +++ /dev/null @@ -1,171 +0,0 @@ - - - - - LogarithmPlotter Icon - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - LogarithmPlotter Icon - 2024-10-06 - - - Adsooi <mail@ad5001.eu> - - - - - (c) Adsooi 2021-2024 - - - - - - - - - - - - - - - - diff --git a/assets/native/linux/application-x-logarithm-plot.svg b/assets/native/linux/application-x-logarithm-plot.svg deleted file mode 120000 index 4e7d218..0000000 --- a/assets/native/linux/application-x-logarithm-plot.svg +++ /dev/null @@ -1 +0,0 @@ -../../logplotterfile.svg \ No newline at end of file diff --git a/assets/native/linux/debian/changelog b/assets/native/linux/debian/changelog deleted file mode 100644 index bc80cc9..0000000 --- a/assets/native/linux/debian/changelog +++ /dev/null @@ -1,233 +0,0 @@ -logarithmplotter (0.5.0) stable; urgency=medium - - * 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. - - * Changed: When creating an object that can be positioned, new default behavior is to pick first instead of opening object settings. - * Changed: Icons with text now use the SVG's text element, allowing them to integrate better with the system's default font. - * Changed: Special characters popup is now context aware (e.g. no sub/supscript symbols in expressions). - * Changed: New symbols in special characters popup. - * Changed: Integrals and derivatives can now be provided with an executable object (e.g. Functions) instead of strings as function. - * Changed: New description on Linux. - - * Fixed: Fixing ∞ 'variable' in domains and expressions. - * Fixed: Several other bugs related to constants in expresions were fixed as well. - * Fixed: 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. - -logarithmplotter (0.4.0) stable; urgency=medium - - * Fully ported to PySide6 (Qt6). - - * New: Customizable color schemes for expressions. - * New, rewamped and improved picked location overlay settings: - * It's now possible to disable picking x or y when setting a location. - * Properties which are related to positioning (X, Y, Label's X position) can now be set using the picker. - * Visual redesign that enhances readability of settings. - * There is now a button to hide picker settings. - - * Changed: Greet screen settings are now scrollable. - * Changed: Changelog is now freezed to current version. - - * Fixed bug: Cursors in expression are now easier to see. - * Fixed bug: Symbols in LaTeX rendered Texts cause the LaTeX renderer to crash. - * Fixed bug: Underscores in distribution names are automatically removed if the name is modified. - * Fixed bug: Autocomplete categories now properly respect theme colors. - * Fixed bug: Functions in expressions (like indexOf, map...) now properly send errors when the arguments are of the wrong type or count. - * Fixed bug: Executable Objects called (like functions, bode magnitures, phases...) now send an error if provided with no arguments. - * Fixed bug: Function calls with no argument no longer make LogarithmPlotter crash under certain circumstances. - * Fixed bug: Thank you dialog's lists are no longer draggable. - - * Internal change: Disabled auto detect of visual theme if the `QT_QUICK_CONTROLS_STYLE` environment variable is set. - * Internal change: (macOS, Windows, Flatpak) Drastically reducing installer sizes (more than halved). - * Internal change: (Launchpad/Ubuntu) Using custom built packages of PySide6, meaning smaller installation and distro dependency. - - -- Ad5001 Fri, 27 May 2023 17:00:00 +0100 - -logarithmplotter (0.3.0) stable; urgency=medium - - * New: Completely revamped expression editor: - * Automatic closing of parentheses and brackets (can be disabled in settings). - * Syntax highlighting (can be disabled in the settings). - * Autocompletion is now available (for function names, variables and constants, object names and properties) (can be disabled in the settings). - * New: Object properties can now be used in expressions (e.g. if you have a point named A, you can use A.x to access its x value). - * Similarly executable objects (Functions, bode magnitudes and phases, distributions, and sequences) can be now be used in expressions (e.g. if you have a function named 'f', you can access its value using `f()`). - * New LaTeX-rendered formulas are now used in the Objects and History tabs when LaTeX rendering is enabled. - * New: Errors in formulas are now reported in message boxes. - - * Changed: The Object Editor dialog has been completely reworked internally, resulting in notable performance improvements. - * Changed: Vast improvements to the objects system: names are now consistently reported and cannot be shared amongst different objects. - * Changed: Disabled access to custom variable and function definition in expressions (can cause issues and vulnerabilities) - * Changed: When using the set position cursor on Points and Texts, the position change is now saved a single history action: the position setting. - * Changed: Distribution are now prefixed with an 'F_' to prevent confusion with X Cursors. - - * Translated: Autocompletion categories (English, French, German, Hungarian). - * Translated: Expression editor settings (English, French, German, Hungarian). - * Translated: Expression syntax errors (English, French, German, Hungarian). - * On top of the above: - * Translated: (Hungarian) v0.2.0 added text (thanks @ovari!) - * Translated: (Spanish) Menu bars (thanks @Sergio Varela) - * Fixing Texts not being properly recognized as texts when saving. - - * Fixed bug: Text's 'Disable LaTeX' property is now properly saved. - * Fixed bug: X Cursors LaTeX rendering made the app crash. - * Fixed bug: Attempting to insert special character no longer automatically saves the expression you're editing. - * Fixed bug: Proper HDPI support for icons and buttons (note: HDPI is not available for the rendered canvas yet). - * Fixed bug: Support for non-latin characters in variables (e.g. greek letters, subtext, suptext) - * Fixed bug: Silent error when misentering variable names in the expression editor causing internal issues preventing you from changing the expression ever again and causing issues and rendering. - * Fixed bug: Some utils function simplifying parentheses when they shouldn't have (note: you may see more parentheses than before in expressions). - * Fixed bug: (Normally) Fixing deb building. - - * Internal changes: Object dependencies are now registered on both the dependant object, and the object it's depending on. - * Internal changes: Objects now have a proper per-name registry. - * Internal changes: Object Editor Dialog has been reworked to use loaders instead of loading and hiding every property editor for each property. - * Internal changes: Reworked the file loading system to be able to load dependencies properly. - - -- Ad5001 Fri, 28 Oct 2022 17:00:00 +0100 - -logarithmplotter (0.2.0) stable; urgency=medium - - * New: (EXPERIMENTAL) LogarithmPlotter now has an optional LaTeX integration. - * It requires a LaTeX installation, including `latexmk` and `dvipng` available in the PATH. - * NOTE: LaTeX support is disabled by default and is only for working for the rendering on the graph. - * NOTE: The objects and history tab still use the legacy text based expression rendering. - * New: Thanks and contributions dialog, showing included libraries and translations, their license and author(s). - * New: LaTeX rendering can be disabled for texts, even if LaTeX is enabled. - * Changed: History re/undos only redraw the graph every 4 change at most in order to speed up the process when re/undoing a lot of changes. - * Changed: Gradients are no longer hidden when filtered out in the history tab. - * Added translation: LaTeX options and error messages - * Added translation: Thanks and contribution dialog - * Added translation: New option for text. - * Fixed translation: "repartition" which should be "distribution" in certain remaining strings. - * Fixed bug: (macos) #1 - Opening files don't work on compiled versions of LogarithmPlotter on MacOS - * Fixed bug: (snapcraft) Fixed bug preventing from launching LogarithmPlotter. This fix has been backported to v0.1.8. - * Fixed bug: (snapcraft) Files are now properly opened. - * Fixed bug: (snapcraft) Added changelog support. - * Internal changes: Moved python modules to "util" directory for more clarity. - * Internal changes: Moved flatpak metainfo to eu.ad5001.LogarithmPlotter repository. - * Internal changes: Componented the Mathlib library in order to have a more readable source. - * Internal changes: Added documentation for most internal JavaScript modules. - * Internal changes: (flatpak) Updated SDK version to v5.15-21.08. - - -- Ad5001 Fri, 22 Apr 2022 20:00:00 +0100 - - logarithmplotter (0.1.8) stable; urgency=medium - - * New: There is now a user manual for LogarithmPlotter! Contributions apprecriated. - * Changed: A link to LogarithmPlotter's official website (https://apps.ad5001.eu/logarithmplotter/) has been added in the about dialog. - * Changed: A link to the user manual has been added both on the greeting screen and the `Help` menu. - * Added translation: User manual. - * Added translation: Official website. - * Added translation: Filtering for history browser. - * Fixed bug: The label position of X Cursors now display the label even when unexpected values are entered. - * Fixed bug: X Cursors target object in history are now properly rendered when no object were selected - * Fixed bug: Fixed slight clipping at the bottom of the border. - * Fixed bug: TextInput no longer allow to input forbidden characters for numbers. - * Fixed bug: ALT+ shortcuts on the menu bar now work. NOTE: May break some mobile configuration. Qt bug report: https://bugreports.qt.io/browse/QTBUG-100539 - * Fixed bug (flatpak): Buttons on side menu to create object now have proper width on startup. - * Internal changes: There is now a script to generate offline versions of the manual based on their online version. - * Internal changes: Sidebar button width is now fixed. - * Internal changes: Artifacts have been added to appstream metadata. - - -- Ad5001 Sat, 19 Feb 2022 20:00:00 +0100 - -logarithmplotter (0.1.7) stable; urgency=medium - - * New: The history browser has been completly redesigned, improving UX. - * New: The history browser now features a filter bar. - * New: All side panel tabs now have a visually identifiable scrollbar. - * Changed: Shorter rich text representations of history entries to improve readability. - * Changed: Usage of gradiants and icons to better identify history entries at a glance. - * Changed: History entries are now showing the whole label on several lines, instead of cutting it at the end. - * Changed: New history action for renaming. - * Changed: New history action for coloring. Note: color changing history items created in previous versions of LogarithmPlotter will not be updated to the new action. - * Changed: Tooltips for object creation buttons have been added. - * Changed: Tooltips have been set to have a delay of 200ms to match most software's handling of them. - * Changed: Object creation buttons now have a unified style accross all platforms. - * Added translation: History action of renaming objects. - * Added translation: History action of changing the color of objects. - * Added translation: Filtering for history browser. - * Fixed bug: Visibility history actions (shown and hidden) are now properly saved, solving the issue that when loading file with one, it's not automaticly changed to "Show". - * Fixed bug: Name changes history actions are now properly saved. - * Fixed bug: Non translated object type on the "+ Create new object" item selection combobox for Bode Magnitude and Phase. - * Fixed bug: Proper handling for future LogarithmPlotter files. - * Fixed bug: Shortcuts not being displayed in the menu bar are now properly shown. - * Fixed bug (flatpak): Black versions of the icons when using a black theme with the KDE SDK. - * Fixed bug (debian): Fixed launchpad building properly. - * Internal changes: Better organisation on icons. - * Internal changes: Historylib has been separated in several files. - * Internal changes: Trying to switch metainfo once more to try and fix another bug. - * Internal changes: Keywords added to metainfo. - - -- Ad5001 Thu, 03 Feb 2022 00:00:00 +0100 - -logarithmplotter (0.1.6) stable; urgency=medium - - * New: A new changelog popup is available at startup and in the help menu. - * Added translation: Object properties names. - * Added translation: Object properties enum values. - * Added translation: Object comments. - * Added translation: Most elements using a ":". - * Fixed bug: X Cursor's targets can now be set to null. - * Fixed bug: History now imports domains and objects properly. - * Fixed bug: Proper handling for future LogarithmPlotter files. - * Fixed bug (debian): Fixing bug that created a /build directory and didn't put the icons in the right directories. - * Other: Refractoring done on helper. - * Other: All QML elements are now properly commented. - * Other: Scripts have been moved to it's own directory. - - -- Ad5001 Sat, 29 Jan 2022 20:00:00 +0100 - -logarithmplotter (0.1.5) stable; urgency=medium - - * New: LogarithmPlotter has now better handling of very high values in logarithmic scale. - * Added translation for flatpak metadata, including translated image. - * [URGENT PATCH] Fixed bug: File saving dialog was not working. - * [URGENT PATCH] Fixed bug: Debian packages does include any language file. - * Fixed bug: X Cursor pointing does not detect any object. - - -- Ad5001 Wed, 26 Jan 2022 10:00:00 +0100 - -logarithmplotter (0.1.4) stable; urgency=medium - - * New feature: LogarithmPlotter detects unsaved changes. - * New feature: LogarithmPlotter is now translated! See https://hosted.weblate.org/engage/logarithmplotter/ to help. - * New translation: English by Ad5001: 100% - * New translation: French by Ad5001: 100% - * New translation: German by Ad5001: 100% - * New translation: Hungarian by Óvári (@ovari on github): 100% - * New translation: Norvegian by Allan Nordhøy (@comradekingu on github): 80% - * Fixed bug: No notification when closing LogarithmPlotter with unsaved changes. - * Fixed bug: π unavailable in symbols. - - -- Ad5001 Wed, 24 Jan 2022 20:00:00 +0100 - -logarithmplotter (0.1.3) stable; urgency=medium - - * Fixed bug: Confined packages (snapcraft & flatpak) won't show error messages related to update checks. - * FIxed bug: Equations of the form (x + y) / z were not being simplified properly. - - -- Ad5001 Wed, 19 Jan 2022 20:00:00 +0100 - -logarithmplotter (0.1.2) unstable; urgency=medium - - * Fixed bug: Unable to move Bode diagrams elements when having deleted the sum element. - * Fixed bug: Names were not not being changed from previous object when editing a new one. - * Fixed bug: Bode Magnitude was not drawn far enough - * Fixed bug: Bode Magnitude had undefined ending. - * Fixed other bugs from v0.1.1. - - -- Ad5001 Mon, 30 Sep 2021 20:00:00 +0100 - -logarithmplotter (0.1) UNRELEASED; urgency=medium - - * Initial release. - - -- Ad5001 Thu, 26 Aug 2021 08:48:28 +0100 diff --git a/assets/native/linux/debian/control.bkp b/assets/native/linux/debian/control.bkp deleted file mode 100644 index 1df7fd8..0000000 --- a/assets/native/linux/debian/control.bkp +++ /dev/null @@ -1,13 +0,0 @@ -Package: logarithmplotter -Source: logarithmplotter -Version: 0.6.0 -Architecture: all -Maintainer: Ad5001 -Depends: python3 (>= 3.9), python3-pip, python3-pyside6-essentials (>= 6.7.0), python3-pyside6-addons (>= 6.7), texlive-latex-base, dvipng - -Build-Depends: debhelper (>=11~), dh-python, dpkg-dev (>= 1.16.1~), python-setuptools -Section: science -Priority: optional -Homepage: https://apps.ad5001.eu/logarithmplotter/ -Installed-Size: 174 -Description: Create and edit Bode plots diff --git a/assets/native/linux/debian/depends.packaged b/assets/native/linux/debian/depends.packaged deleted file mode 100644 index 0bace27..0000000 --- a/assets/native/linux/debian/depends.packaged +++ /dev/null @@ -1 +0,0 @@ -python3 (>= 3.9), python3-pip, python3-pyside6.qtcore (>= 6), python3-pyside6.qtgui (>= 6), python3-pyside6.qtqml (>= 6), python3-pyside6.qtwidgets (>= 6), python3-pyside6.qtquick (>= 6), python3-pyside6.qtquickcontrols2 (>= 6), qml6-module-qt-labs-platform (>= 6), qml6-module-qtquick-dialogs (>= 6), texlive-latex-base, dvipng diff --git a/assets/native/linux/debian/depends.wheels b/assets/native/linux/debian/depends.wheels deleted file mode 100644 index 5b7d902..0000000 --- a/assets/native/linux/debian/depends.wheels +++ /dev/null @@ -1 +0,0 @@ -python3 (>= 3.9), python3-pip, python3-pyside6-essentials (>= 6.7.0), texlive-latex-base, dvipng diff --git a/assets/native/linux/eu.ad5001.LogarithmPlotter.metainfo.xml b/assets/native/linux/eu.ad5001.LogarithmPlotter.metainfo.xml deleted file mode 100644 index f7e4c9d..0000000 --- a/assets/native/linux/eu.ad5001.LogarithmPlotter.metainfo.xml +++ /dev/null @@ -1,626 +0,0 @@ - - - - eu.ad5001.LogarithmPlotter - logarithmplotter.desktop - CC0-1.0 - GPL-3.0+ - - LogarithmPlotter - https://apps.ad5001.eu/icons/apps/svg/logarithmplotter.svg - Create and edit Bode plots - Erstellen und Bearbeiten von Bode-Diagrammen - Créez et éditez des diagrammes de Bode - Bode-diagramok létrehozása és szerkesztése - Opprette og redigere Bode-diagrammer - - -

- LogarithmPlotter is, as its name suggests, a plotter made with logarithm scales in mind. With an object system similar to Geogebra's, it allows dynamic creation of both logarithmic-scaled and non logarithmic-scaled plots with very few limitations. -

-

- LogarithmPlotter est, comme son nom l'indique, un créateur de graphes et diagrammes 2D réalisé avec l'échelle logarithmique en tête. Avec un système d'objets similaire à Geogebra, ce qui lui permet de créer des graphes à échelle logarithmique et non logarithmique avec peu de limitations. -

-

- A LogarithmPlotter egy logaritmus-ábrázoló, amely logaritmikus léptékek figyelembevételével készült. A Geogebrához hasonló objektumrendszerrel dinamikus parcellák létrehozását teszi lehetővé, nagyon kevés korlátozással. -

-

- Its primary use is to quickly create asymptotic Bode plots, but its extensible nature and ability to switch to non-logarithmic scales allow it to create other things with it, like sequences or statistical repartition functions. -

-

- Son intérêt principal est de permettre de créer des diagrammes asymptotiques de Bode, mais sa nature extensible et sa capacité à passer à une échelle non-logarithmique lui permet de créer d'autres choses. -

-

- Elsődleges felhasználása az aszimptotikus Bode-ábrák gyors létrehozása, de bővíthető jellege és a nem logaritmikus skálákra váltás lehetősége lehetővé teszi, hogy más dolgokat is létrehozzon vele, például sorozatokat vagy statisztikai újraosztási függvényeket. -

-

Features:

-

Fonctionnalités:

-
    -
  • Graphical objects (points, fonctions, Bode magnitudes...) management system
  • -
  • Complete object edition
  • -
  • Advanced history system
  • -
  • Diagram looks customisation
  • -
  • Système de gestion des objets graphiques (points, fonctions, gains de Bode...)
  • -
  • Modification complète des objets
  • -
  • Système d'historique avancé
  • -
  • Personnalisation de l'apparence des diagrammes
  • -
-

LogarithmPlotter is available in:

-

LogarithmPlotter est disponible en:

-
    -
  • 🇬🇧 English
  • -
  • 🇫🇷 French
  • -
  • 🇩🇪 German
  • -
  • 🇭🇺 Hungarian
  • -
  • 🇳🇴 Norwergian
  • -
  • 🇪🇸 Spanish
  • -
  • 🇬🇧 Anglais
  • -
  • 🇫🇷 Français
  • -
  • 🇩🇪 Allemand
  • -
  • 🇭🇺 Hongrois
  • -
  • 🇳🇴 Norvégien
  • -
  • 🇪🇸 Espagnol
  • -
-
- - - Science - Education - - - https://apps.ad5001.eu/logarithmplotter/ - https://git.ad5001.eu/Ad5001/LogarithmPlotter/issues/ - https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/ - https://hosted.weblate.org/engage/logarithmplotter/ - - - https://apps.ad5001.eu/img/en/logarithmplotter/gain.png?v=0.6 - https://apps.ad5001.eu/img/de/logarithmplotter/gain.png?v=0.6 - https://apps.ad5001.eu/img/fr/logarithmplotter/gain.png?v=0.6 - https://apps.ad5001.eu/img/hu/logarithmplotter/gain.png?v=0.6 - https://apps.ad5001.eu/img/no/logarithmplotter/gain.png?v=0.6 - https://apps.ad5001.eu/img/es/logarithmplotter/gain.png?v=0.6 - Main view of LogarithmPlotter showing an asymptotic Bode magnitude plot. - Die Hauptansicht des LogarithmPlotters zeigt eine asymptotische Bode-Magnitude-Darstellung. - Vue principale de LogarithmPlotter montrant un tracé asymptotique d'une magnitude de Bode. - A LogarithmPlotter fő nézete, amely egy aszimptotikus Bode-magnitúdó ábrát mutat. - Hovedvisning av LogarithmPlotter som viser et asymptotisk Bode-størrelsesplott. - Vista principal de LogarithmPlotter mostrando un gráfico asintótico de una magnitud de Bode. - - - https://apps.ad5001.eu/img/en/logarithmplotter/phase.png?v=0.6 - https://apps.ad5001.eu/img/de/logarithmplotter/phase.png?v=0.6 - https://apps.ad5001.eu/img/fr/logarithmplotter/phase.png?v=0.6 - https://apps.ad5001.eu/img/hu/logarithmplotter/phase.png?v=0.6 - https://apps.ad5001.eu/img/no/logarithmplotter/phase.png?v=0.6 - https://apps.ad5001.eu/img/es/logarithmplotter/phase.png?v=0.6 - Main view of LogarithmPlotter showing an asymptotic Bode phase plot. - Hauptansicht des LogarithmPlotters mit einer asymptotischen Bode-Phasendarstellung. - Vue principale de LogarithmPlotter montrant un tracé asymptotique d'une phase de Bode. - A LogarithmPlotter fő nézete, amely egy aszimptotikus Bode-fázis ábrát mutat. - Hovedvisning av LogarithmPlotter som viser et asymptotisk Bode-fasediagram. - Vista principal de LogarithmPlotter mostrando un gráfico asintótico de una fase de Bode. - - - https://apps.ad5001.eu/img/en/logarithmplotter/welcome.png?v=0.6 - https://apps.ad5001.eu/img/de/logarithmplotter/welcome.png?v=0.6 - https://apps.ad5001.eu/img/fr/logarithmplotter/welcome.png?v=0.6 - https://apps.ad5001.eu/img/hu/logarithmplotter/welcome.png?v=0.6 - https://apps.ad5001.eu/img/no/logarithmplotter/welcome.png?v=0.6 - https://apps.ad5001.eu/img/es/logarithmplotter/welcome.png?v=0.6 - LogarithmPlotter's welcome page. - LogarithmPlotter's Willkommensseite. - Page d'accueil de LogarithmPlotter. - LogarithmPlotter üdvözlő oldala. - LogarithmPlotters velkomstside. - Página de bienvenida de LogarithmPlotter. - - - - - - 768 - - - - - 3840 - 360 - - - - - -

Changes for v0.5.0:

-

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.
  • -
-
- - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.5.0/logarithmplotter-v0.5.0-setup.exe - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.5.0/LogarithmPlotter-v0.5.0-setup.dmg - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.5.0/logarithmplotter-0.5.0.tar.gz - - -
- - -

Changes for v0.4.0:

-

Changes

-
    -
  • Fully ported to PySide6 (Qt6).
  • -
  • Greet screen settings are now scrollable.
  • -
  • Changelog is now freezed to current version.
  • -
-

New

-
    -
  • Customizable color schemes for expressions.
  • -
  • New, rewamped and improved picked location overlay settings:
  • -
  • It's now possible to disable picking x or y when setting a location.
  • -
  • Properties which are related to positioning (X, Y, Label's X position) can now be set using the picker.
  • -
  • Visual redesign that enhances readability of settings.
  • -
  • There is now a button to hide picker settings.
  • -
-

Fixed bugs

-
    -
  • Cursors in expression are now easier to see.
  • -
  • Symbols in LaTeX rendered Texts cause the LaTeX renderer to crash.
  • -
  • Underscores in distribution names are automatically removed if the name is modified.
  • -
  • Autocomplete categories now properly respect theme colors.
  • -
  • Functions in expressions (like indexOf, map...) now properly send errors when the arguments are of the wrong type or count.
  • -
  • Executable Objects called (like functions, bode magnitures, phases...) now send an error if provided with no arguments.
  • -
  • Function calls with no argument no longer make LogarithmPlotter crash under certain circumstances.
  • -
  • Thank you dialog's lists are no longer draggable.
  • -
-

Internal changes

-
    -
  • A lot of inner changes led by porting to Qt6, fixing a lot of bugs at the same time.
  • -
  • Disabled auto detect of visual theme if the QT_QUICK_CONTROLS_STYLE environment variable is set.
  • -
  • (macOS, Windows, Flatpak) Drastically reducing installer sizes (more than halved).
  • -
  • (Launchpad/Ubuntu) Using custom built packages of PySide6, meaning smaller installation and distro dependency.
  • -
-
- - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.4.0/logarithmplotter-v0.4.0-setup.exe - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.4.0/LogarithmPlotter-v0.4.0-setup.dmg - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.4.0/logarithmplotter-0.4.0.tar.gz - - -
- - -

Changes for v0.3.0:

-

New

-
    -
  • New completely revamped expression editor:
  • -
  • Automatic closing of parentheses and brackets (can be disabled in settings).
  • -
  • Syntax highlighting (can be disabled in the settings).
  • -
  • Autocompletion is now available (for function names, variables and constants, object names and properties) (can be disabled in the settings).
  • -
  • Object properties can now be used in expressions (e.g. if you have a point named A, you can use A.x to access its x value).
  • -
  • Similarly executable objects (Functions, bode magnitudes and phases, distributions, and sequences) can be now be used in expressions (e.g. if you have a function named 'f', you can access its value using `f(value)`).
  • -
  • LaTeX-rendered formulas are now used in the Objects and History tabs when LaTeX rendering is enabled.
  • -
  • Errors in formulas are now reported in message boxes.
  • -
-

Changes

-
    -
  • The Object Editor dialog has been completely reworked internally, resulting in notable performance improvements.
  • -
  • Vast improvements to the objects system: names are now consistently reported and cannot be shared amongst different objects.
  • -
  • Disabled access to custom variable and function definition in expressions (can cause issues and vulnerabilities)
  • -
  • When using the set position cursor on Points and Texts, the position change is now saved a single history action: the position setting.
  • -
  • Distribution are now prefixed with an 'F_' to prevent confusion with X Cursors.
  • -
-

Added translations

-
    -
  • Autocompletion categories (English, French, German, Hungarian).
  • -
  • Expression editor settings (English, French, German, Hungarian).
  • -
  • Expression syntax errors (English, French, German, Hungarian).
  • -
  • On top of the above:
  • -
  • Hungarian: v0.2.0 added text (thanks @ovari!)
  • -
  • Spanish: Menu bars (thanks @Sergio Varela)
  • -
  • You can contribute to translation on Weblate.
  • -
-

Fixed bugs

-
    -
  • Fixing Texts not being properly recognized as texts when saving.
  • -
  • Text's 'Disable LaTeX' property is now properly saved.
  • -
  • X Cursors LaTeX rendering made the app crash.
  • -
  • Attempting to insert special character no longer automatically saves the expression you're editing.
  • -
  • Proper HDPI support for icons and buttons (note: HDPI is not available for the rendered canvas yet).
  • -
  • Support for non-latin characters in variables (e.g. greek letters, subtext, suptext)
  • -
  • Silent error when misentering variable names in the expression editor causing internal issues preventing you from changing the expression ever again and causing issues and rendering.
  • -
  • Fixing some utils function simplifying parentheses when they shouldn't have (note: you may see more parentheses than before in expressions).
  • -
  • (flatpak and KDE SDK) Fixing the sometimes invisible buttons on the objects tab on startup.
  • -
  • (macos) Application string version does not match LogarithmPlotter's version.
  • -
  • (debian) (Normally) Fixing deb building.
  • -
-

Internal changes

-
    -
  • Object dependencies are now registered on both the dependant object, and the object it's depending on.
  • -
  • Objects now have a proper per-name registry.
  • -
  • Object Editor Dialog has been reworked to use loaders instead of loading and hiding every property editor for each property.
  • -
  • Reworked the file loading system to be able to load dependencies properly.
  • -
-
- - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.3.0/logarithmplotter-v0.3.0-setup.exe - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.3.0/LogarithmPlotter-v0.3.0-setup.dmg - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.3.0/logarithmplotter-0.3.0.tar.gz - - -
- - -

Changes for v0.2.0:

-

New

-
    -
  • (EXPERIMENTAL) LogarithmPlotter now has an optional LaTeX integration.
  • -
  • It requires a LaTeX installation, including `latexmk` and `dvipng` available in the PATH.
  • -
  • NOTE: LaTeX support is disabled by default and is only for working for the rendering on the graph.
  • -
  • NOTE: The objects and history tab still use the legacy text based expression rendering.
  • -
  • Thanks and contributions dialog, showing included libraries and translations, their license and author(s).
  • -
  • LaTeX rendering can be disabled for texts, even if LaTeX is enabled.
  • -
-

Changes

-
    -
  • History re/undos only redraw the graph every 4 change at most in order to speed up the process when re/undoing a lot of changes.
  • -
  • Gradients are no longer hidden when filtered out in the history tab.
  • -
-

Added translations

-
    -
  • LaTeX options and error messages
  • -
  • Thanks and contribution dialog
  • -
  • New option for text.
  • -
  • Fixed translation of "repartition" which should be "distribution" in certain remaining strings.
  • -
-

Fixed bugs

-
    -
  • (macos) #1 - Opening files don't work on compiled versions of LogarithmPlotter on MacOS
  • -
  • (snapcraft) Fixed bug preventing from launching LogarithmPlotter. This fix has been backported to v0.1.8.
  • -
  • (snapcraft) Files are now properly opened.
  • -
  • (snapcraft) Added changelog support.
  • -
-

Internal changes

-
    -
  • Moved python modules to "util" directory for more clarity.
  • -
  • Moved flatpak metainfo to eu.ad5001.LogarithmPlotter repository.
  • -
  • Componented the Mathlib library in order to have a more readable source.
  • -
  • Added documentation for most internal JavaScript modules.
  • -
  • Merge label drawing methods due to it's complexity.
  • -
  • (flatpak) Updated SDK version to v5.15-21.08.
  • -
-
- - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.2.0/logarithmplotter-v0.2.0-setup.exe - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.2.0/LogarithmPlotter-v0.2.0-setup.dmg - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.2.0/logarithmplotter-0.2.0.tar.gz - - -
- - -

Changes for v0.1.8:

-

New

-
    -
  • There is now a user manual for LogarithmPlotter! Contributions apprecriated.
  • -
-

Changes

-
    -
  • A link to LogarithmPlotter's official website has been added in the about dialog.
  • -
  • A link to the user manual has been added both on the greeting screen and the `Help` menu.
  • -
-

Added translations

-
    -
  • User manual.
  • -
  • Official website.
  • -
-

Fixed bugs

-
    -
  • The label position of X Cursors now display the label even when unexpected values are entered.
  • -
  • X Cursors target object in history are now properly rendered when no object were selected
  • -
  • Fixed slight clipping at the bottom of the border.
  • -
  • TextInput no longer allow to input forbidden characters for numbers.
  • -
  • ALT+ shortcuts on the menu bar now work. NOTE: May break some mobile configuration. Qt bug report
  • -
  • (flatpak) Buttons on side menu to create object now have proper width on startup.
  • -
-

Internal changes

-
    -
  • There is now a script to generate offline versions of the manual based on their online version.
  • -
  • Sidebar button width is now fixed.
  • -
  • Artifacts have been added to appstream metadata.
  • -
-
- - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.8/logarithmplotter-v0.1.8-setup.exe - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.8/LogarithmPlotter-v0.1.8-setup.dmg - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.8/logarithmplotter-0.1.8.tar.gz - - -
- - -

Changes for v0.1.7:

-

New

-
    -
  • The history browser has been completly redesigned, improving UX.
  • -
  • The history browser now features a filter bar.
  • -
  • All side panel tabs now have a visually identifiable scrollbar.
  • -
-

Changes

-
    -
  • Shorter rich text representations of history entries to improve readability.
  • -
  • Usage of gradiants and icons to better identify history entries at a glance.
  • -
  • History entries are now showing the whole label on several lines, instead of cutting it at the end.
  • -
  • New history action for renaming.
  • -
  • New history action for coloring. Note: color changing history entries created in previous versions of LogarithmPlotter will not be updated.
  • -
  • Tooltips for object creation buttons have been added.
  • -
  • Tooltips have been set to have a delay of 200ms to match most software's handling of them.
  • -
  • Object creation buttons now have a unified style accross all platforms.
  • -
-

Added translations

-
    -
  • History action of renaming objects.
  • -
  • History action of changing the color of objects.
  • -
  • Filtering for history browser.
  • -
-

Fixed bugs

-
    -
  • Visibility history actions (shown and hidden) are now properly savedmaking loading them not automaticly changed to "Show".
  • -
  • Name changes history actions are now properly saved.
  • -
  • Non translated object type on the "+ Create new object" item selection combobox for Bode Magnitude and Phase.
  • -
  • Proper handling for future LogarithmPlotter files.
  • -
  • Shortcuts not being displayed in the menu bar are now properly shown.
  • -
  • (flatpak) Black versions of the icons when using a black theme with the KDE SDK.
  • -
  • (debian) Fixed launchpad building properly.
  • -
-

Internal changes

-
    -
  • Better organisation on icons.
  • -
  • Historylib has been separated in several files.
  • -
  • Trying to switch metainfo once more to try and fix another bug.
  • -
  • Keywords added to metainfo.
  • -
-
- - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.7/logarithmplotter-v0.1.7-setup.exe - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.7/LogarithmPlotter-v0.1.7-setup.dmg - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.7/logarithmplotter-0.1.7.tar.gz - - -
- - -

Changes for v0.1.6:

-

New

-
    -
  • A new changelog popup is available at startup and in the help menu.
  • -
-

Added translations

-
    -
  • Object properties names.
  • -
  • Object properties enum values.
  • -
  • Object comments.
  • -
  • Most elements using a ":".
  • -
-

Fixed bugs

-
    -
  • X Cursor's targets can now be set to null.
  • -
  • History now imports domains and objects properly.
  • -
  • Proper handling for future LogarithmPlotter files.
  • -
  • (debian) Fixing bug that created a /build directory and didn't put the icons in the right directories.
  • -
-

Other

-
    -
  • Other: Refractoring done on helper.
  • -
  • Other: All QML elements are now properly commented.
  • -
  • Other: Scripts have been moved to it's own directory.
  • -
  • Other: Added changelog to metainfo for flathub.
  • -
-
- - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.6/logarithmplotter-v0.1.6-setup.exe - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.6/LogarithmPlotter-v0.1.6-setup.dmg - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.6/LogarithmPlotter-v0.1.6.tar.gz - - -
- - -

Changes for v0.1.5:

-

New

-
    -
  • LogarithmPlotter has now better handling of very high values in logarithmic scale.
  • -
-

Added translations

-
    -
  • Flatpak metadata, including translated image.
  • -
-

Fixed bugs

-
    -
  • (!) File saving dialog was not working.
  • -
  • (!) Debian packages does include any language file.
  • -
  • X Cursor pointing does not detect any object.
  • -
-
- - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.5/logarithmplotter-v0.1.5-setup.exe - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.5/LogarithmPlotter-v0.1.5-setup.dmg - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.5/LogarithmPlotter-v0.1.5.tar.gz - - -
- - -

Changes for v0.1.4:

-

New

-
    -
  • LogarithmPlotter detects unsaved changes.
  • -
  • LogarithmPlotter is now translated!
  • -
  • New translation: English by Ad5001: 100%
  • -
  • New translation: French by Ad5001: 100%
  • -
  • New translation: German by Ad5001: 100%
  • -
  • New translation: Hungarian by Óvári (@ovari on github): 100%
  • -
  • New translation: Norvegian by Allan Nordhøy (@comradekingu on github): 80%
  • -
-

Fixed bugs

-
    -
  • Fixed bug: No notification when closing LogarithmPlotter with unsaved changes.
  • -
  • Fixed bug: π unavailable in symbols.
  • -
-
- - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.4/logarithmplotter-v0.1.4-setup.exe - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.4/LogarithmPlotter-v0.1.4-setup.dmg - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.4/LogarithmPlotter-v0.1.4.tar.gz - - -
- - -

Changes for v0.1.3:

-

Fixed bugs

-
    -
  • Sandboxed packages (snapcraft and flatpak) won't show error messages related to update checks.
  • -
  • Equations of the form (x + y) / z were not being simplified properly.
  • -
-
- - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.3/logarithmplotter-v0.1.3-setup.exe - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.3/LogarithmPlotter-v0.1.3-setup.dmg - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.3/LogarithmPlotter-v0.1.3.tar.gz - - -
- - -

Changes for v0.1.2:

-

Fixed bugs

-
    -
  • Unable to move Bode diagrams elements when having deleted the sum element.
  • -
  • Names were not not being changed from previous object when editing a new one.
  • -
  • Bode Magnitude was not drawn far enough.
  • -
  • Bode Magnitude had undefined ending.
  • -
  • Other bugs patched in v0.1.1.
  • -
-
- - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.2/logarithmplotter-v0.1.2-setup.exe - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.2/LogarithmPlotter-v0.1.2-setup.dmg - - - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.2/LogarithmPlotter-v0.1.2.tar.gz - - -
- - -

Changes for v0.1:

-
    -
  • Initial release.
  • -
-
-
-
- - - - Ad5001 - https://ad5001.eu - - - - Plot - Plotter - Log - Logarithm - Logarithmic - Bode - Magnitude - Diagram - Graph - Phase - Sequence - Distribution - Qt - -
- - diff --git a/assets/native/mac/logarithmplotter.icns b/assets/native/mac/logarithmplotter.icns deleted file mode 100644 index bd032df..0000000 Binary files a/assets/native/mac/logarithmplotter.icns and /dev/null differ diff --git a/assets/native/mac/logarithmplotter.iconset/icon_128x128.png b/assets/native/mac/logarithmplotter.iconset/icon_128x128.png deleted file mode 100644 index 369662e..0000000 Binary files a/assets/native/mac/logarithmplotter.iconset/icon_128x128.png and /dev/null differ diff --git a/assets/native/mac/logarithmplotter.iconset/icon_16x16.png b/assets/native/mac/logarithmplotter.iconset/icon_16x16.png deleted file mode 100644 index 37e7d64..0000000 Binary files a/assets/native/mac/logarithmplotter.iconset/icon_16x16.png and /dev/null differ diff --git a/assets/native/mac/logarithmplotter.iconset/icon_256x256.png b/assets/native/mac/logarithmplotter.iconset/icon_256x256.png deleted file mode 100644 index 3cbc7b1..0000000 Binary files a/assets/native/mac/logarithmplotter.iconset/icon_256x256.png and /dev/null differ diff --git a/assets/native/mac/logarithmplotter.iconset/icon_32x32.png b/assets/native/mac/logarithmplotter.iconset/icon_32x32.png deleted file mode 100644 index 18e297d..0000000 Binary files a/assets/native/mac/logarithmplotter.iconset/icon_32x32.png and /dev/null differ diff --git a/assets/native/mac/logarithmplotter.iconset/icon_512x512.png b/assets/native/mac/logarithmplotter.iconset/icon_512x512.png deleted file mode 100644 index 2e8143b..0000000 Binary files a/assets/native/mac/logarithmplotter.iconset/icon_512x512.png and /dev/null differ diff --git a/assets/native/win/logarithmplotter.ico b/assets/native/win/logarithmplotter.ico deleted file mode 100644 index cb83949..0000000 Binary files a/assets/native/win/logarithmplotter.ico and /dev/null differ diff --git a/ci/all.lpf b/ci/all.lpf deleted file mode 100644 index 9eeb0b9..0000000 --- a/ci/all.lpf +++ /dev/null @@ -1 +0,0 @@ -LPFv1{"xzoom":100,"yzoom":10,"xmin":0.2696454905834007,"ymax":33.115625,"xaxisstep":"4","yaxisstep":"π","xaxislabel":"","yaxislabel":"","logscalex":true,"linewidth":1,"showxgrad":true,"showygrad":true,"textsize":18,"history":[[["CreateNewObject",["A","Point",["A",true,"#941A97","name + value","1","0","above","●"]]],["EditedPosition",["A","Point","1","175.36","0","9.9"]],["CreateNewObject",["f","Function",["f",true,"#6E590E","name + value","x","ℝ⁺*","ℝ","application","above",1,true,true]]],["EditedProperty",["f","Function","expression","x","((x / 2) - 1)",true]],["CreateNewObject",["t","Text",["t",true,"#118455","null","1","0","center","New text",false]]],["EditedPosition",["t","Text","1","36.48","0","(-13.7)"]],["EditedProperty",["t","Text","text","New text","AEZA",false]],["CreateNewObject",["ω","Point",["ω",true,"#5A3A52","name","1","0","above","●"]]],["CreateNewObject",["G₀","Gain Bode",["G₀",true,"#5A3A52","name + value","ω","high","20","below",1,false]]],["EditedPosition",["ω","Point","1","17.76","0","(-8.9)"]],["EditedProperty",["G₀","Gain Bode","gain","20","10",true]],["EditedProperty",["G₀","Gain Bode","labelPosition","below","below-left",false]],["EditedProperty",["G₀","Gain Bode","pass","high","low",false]],["EditedProperty",["G₀","Gain Bode","labelX",1,62.61,false]],["CreateNewObject",["X","X Cursor",["X",true,"#5909A9","name + value","1",null,"left",true,3,"— — — — — — —","Next to target"]]],["EditedProperty",["X","X Cursor","x","1","5.04",true]],["CreateNewObject",["u","Sequence",["u",true,"#78929E","name + value",true,true,{"1":"n"},{"0":0},"above",1]]],["EditedProperty",["u","Sequence","defaultExpression",{"1":"n"},{"1":"n+1"},false]],["EditedProperty",["u","Sequence","defaultExpression",{"1":"n+1"},{"1":"n+1"},false]],["EditedProperty",["u","Sequence","baseValues",{"0":0},{"0":"-1"},false]],["EditedProperty",["u","Sequence","baseValues",{"0":"-1"},{"0":"-1"},false]],["CreateNewObject",["F_X","Repartition",["F_X",true,"#231931","name + value",{"0":"0"},"above",1]]],["EditedProperty",["F_X","Repartition","labelX",1,12.64,false]],["EditedProperty",["f","Function","labelPosition","above","right",false]],["EditedProperty",["f","Function","labelX",1,30,false]],["EditedProperty",["u","Sequence","labelX",1,3,false]],["EditedProperty",["F_X","Repartition","labelX",12.64,40,false]],["EditedProperty",["ω","Point","labelPosition","above","below",false]],["CreateNewObject",["ω₀","Point",["ω₀","#7C2981","name","name + value","1","0","above","●"]]],["CreateNewObject",["φ₀","Phase Bode",["φ₀",true,"#7C2981","name + value","ω₀","90","°","below",1]]],["EditedPosition",["ω₀","Point","1","3","0","(-8)"]],["EditedPosition",["ω₀","Point","3","2","(-8)","8"]],["EditedProperty",["ω₀","Point","labelPosition","above","above-right",false]],["EditedProperty",["u","Sequence","labelPosition","above","above-left",false]],["EditedProperty",["u","Sequence","labelX",3,20,false]],["EditedProperty",["G","Somme gains Bode","labelX",1,2,false]]],[]],"width":1000,"height":500,"objects":{"Point":[["A",true,"#941A97","name + value","175.36","9.9","above","●"],["ω",true,"#5A3A52","name","17.76","(-8.9)","below","●"],["ω₀",false,"name","name","2","8","above-right","●"]],"Function":[["f",true,"#6E590E","name + value","((x / 2) - 1)","ℝ⁺*","ℝ","application","right",30,true,true]],"Text":[["t",true,"#118455","null","36.48","(-13.7)","center","AEZA",false]],"Gain Bode":[["G₀",true,"#5A3A52","name + value","ω","low","10","below-left",62.61,false]],"Somme gains Bode":[["G",true,"#A83C72","name + value","above",2]],"X Cursor":[["X",true,"#5909A9","name + value","5.04",null,"left",true,null,"— — — — — — —","Next to target"]],"Sequence":[["u",true,"#78929E","name + value",true,true,{"1":"n+1"},{"0":"-1"},"above-left",20]],"Repartition":[["F_X",true,"#231931","name + value",{"0":"0"},"above",40]],"Phase Bode":[["φ₀",true,"#7C2981","name + value","ω₀","90","°","below",1]],"Somme phases Bode":[["φ",true,"#A08B14","name + value","above",1]]},"type":"logplotv1"} \ No newline at end of file diff --git a/ci/drone.yml b/ci/drone.yml index 54ef658..d699291 100644 --- a/ci/drone.yml +++ b/ci/drone.yml @@ -6,43 +6,45 @@ platform: arch: amd64 steps: - - name: submodules - image: alpine/git - commands: - - git submodule update --init --recursive +- name: submodules + image: alpine/git + commands: + - git submodule update --init --recursive - - name: Build - image: ad5001/ubuntu-pyside-xvfb:linux-6-latest-latex-node - commands: - - cd common && npm install && cd .. - - bash scripts/build.sh +- name: Linux test + image: ad5001/ubuntu-pyside2-xvfb:hirsute-5.15.2 + commands: + - apt install -y texlive-latex-base dvipng + - xvfb-run python3 run.py --test-build --no-check-for-updates + - xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/test1.lpf + - xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/test2.lpf + when: + event: [ push, tag ] - - name: Unit Tests - image: ad5001/ubuntu-pyside-xvfb:linux-6-latest-latex-node - commands: - - cd common && npm install -D && cd .. - - xvfb-run bash scripts/run-tests.sh --no-rebuild - when: - event: [ push, tag ] +- name: Windows test + image: ad5001/ubuntu-pyside2-xvfb-wine:win7-5.15.2 + commands: + - # For some reason, launching GUI apps with wine, even with xvfb-run, fails. + - xvfb-run python run.py --test-build --no-check-for-updates + - xvfb-run python run.py --test-build --no-check-for-updates ./ci/test1.lpf + - xvfb-run python run.py --test-build --no-check-for-updates ./ci/test2.lpf + when: + event: [ push, tag ] - - name: File Tests - image: ad5001/ubuntu-pyside-xvfb:linux-6-latest-latex-node - commands: - - xvfb-run python3 run.py --test-build --no-check-for-updates - - xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/test1.lpf - - xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/test2.lpf - - xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/all.lpf - - xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/magnitude.lpf - - xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/phase.lpf - - xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/stress.lpf - when: - event: [ push, tag ] - - name: Windows build - image: ad5001/ubuntu-pyside-xvfb:wine-6-latest - commands: - - bash scripts/build-wine.sh --no-rebuild - - bash scripts/package-wine.sh - when: - event: [ push, tag ] +- name: Linux packaging + image: ad5001/accountfree-build-img:hirsute-5.15.2 + commands: + - bash scripts/package-linux.sh + when: + event: [ push, tag ] + + +- name: Windows building + image: ad5001/accountfree-build-img-wine:win7-5.15.2 + commands: + - bash scripts/build-wine.sh + - bash scripts/package-wine.sh + when: + event: [ push, tag ] diff --git a/ci/magnitude.lpf b/ci/magnitude.lpf deleted file mode 100644 index 35ac7ae..0000000 --- a/ci/magnitude.lpf +++ /dev/null @@ -1 +0,0 @@ -LPFv1{"xzoom":120,"yzoom":10,"xmin":0.5,"ymax":25,"xaxisstep":"4","yaxisstep":"4","xaxislabel":"ω (rad/s)","yaxislabel":"G (dB)","logscalex":true,"linewidth":2,"showxgrad":false,"showygrad":true,"textsize":17,"history":[[["CreateNewObject",["ω","Point",["ω",true,"#94AE66","name","1","0","top","●"]]],["CreateNewObject",["G₀","Gain Bode",["G₀",true,"#94AE66","name + value","ω","high","20","below",1,false]]],["EditedProperty",["G₀","Gain Bode","gain","20","0",true]],["EditedProperty",["ω","Point","y","0","10",true]],["EditedVisibility",["ω","Point","visible"]],["EditedProperty",["G","Somme gains Bode","labelX",1,10,false]],["CreateNewObject",["ω₀","Point",["ω₀",true,"#A38B4D","name","1","0","top","●"]]],["CreateNewObject",["G₁","Gain Bode",["G₁",true,"#A38B4D","name + value","ω₀","high","20","below",1,false]]],["EditedProperty",["G₁","Gain Bode","pass","high","low",false]],["EditedProperty",["G₁","Gain Bode","gain","20","(-20)",true]],["EditedVisibility",["ω₀","Point","visible"]],["EditedProperty",["ω₁","Point","x","1","10",true]],["CreateNewObject",["ω₀","Point",["ω₀",true,"#5F04A2","name","1","0","top","●"]]],["CreateNewObject",["G₂","Gain Bode",["G₂",true,"#5F04A2","name + value","ω₀","high","20","below",1,false]]],["EditedVisibility",["ω₀","Point","visible"]],["EditedProperty",["G₂","Gain Bode","labelX",1,5,false]],["EditedProperty",["ω₂","Point","x","1","5",true]],["EditedProperty",["G₂","Gain Bode","pass","high","low",false]],["EditedProperty",["G₂","Gain Bode","gain","20","(-20)",true]],["EditedProperty",["ω₁","Point","x","10","13",true]],["EditedProperty",["ω₁","Point","x","13","30",true]],["EditedProperty",["G₀","Gain Bode","labelPosition","below","below",false]],["EditedProperty",["G₀","Gain Bode","labelX",1,2,false]],["EditedProperty",["G₀","Gain Bode","labelPosition","below","above",false]],["EditedProperty",["G₂","Gain Bode","labelX",5,40,false]],["EditedProperty",["G₂","Gain Bode","labelX",40,20,false]],["EditedProperty",["G₂","Gain Bode","labelX",20,10,false]],["EditedProperty",["G₁","Gain Bode","labelX",1,40,false]],["EditedProperty",["G₁","Gain Bode","labelPosition","below","above-left",false]],["EditedProperty",["G₁","Gain Bode","labelPosition","above-left","above-right",false]],["EditedProperty",["G₁","Gain Bode","labelX",40,45,false]],["EditedProperty",["G","Somme gains Bode","labelX",10,4,false]],["EditedProperty",["G₀","Gain Bode","labelX",2,20,false]],["EditedProperty",["G₁","Gain Bode","omGraduation",false,true,false]],["EditedProperty",["G₂","Gain Bode","omGraduation",false,true,false]],["EditedProperty",["G","Somme gains Bode","labelX",4,2,false]],["EditedProperty",["G","Somme gains Bode","labelPosition","above","below",false]],["EditedProperty",["G₁","Gain Bode","omGraduation",true,false,false]],["EditedProperty",["ω₁","Point","x","30","0",true]],["EditedVisibility",["ω₁","Point","visible"]],["EditedProperty",["G₁","Gain Bode","gain","(-20)","20",true]],["EditedProperty",["ω₁","Point","x","1","0.05",true]],["EditedProperty",["ω₁","Point","y","0","(-20)",true]],["EditedProperty",["ω₁","Point","x","0.05","0.1",true]],["EditedProperty",["G₁","Gain Bode","labelX",45,2,false]],["EditedProperty",["ω","Point","y","10","(-10)",true]],["EditedProperty",["ω","Point","y","(-10)","(-5)",true]],["EditedProperty",["G","Somme gains Bode","labelX",2,15,false]],["CreateNewObject",["X","X Cursor",["X",true,"#61AD9E","name + value","1",null,"left",true,3,"— — — — — — —","Next to target"]]],["EditedProperty",["X","X Cursor","x","1","50",true]],["EditedProperty",["G₂","Gain Bode","color","#5F04A2","#5f04a2",false]],["EditedProperty",["X","X Cursor","color","#61AD9E","#5f04a2",false]],["CreateNewObject",["text","Text",["text",true,"#242159","null","1","0","center","New text"]]],["EditedProperty",["text","Text","x","1","20",true]],["EditedProperty",["text","Text","x","20","50",true]],["EditedProperty",["text","Text","labelPosition","center","right",false]],["EditedProperty",["text","Text","labelPosition","right","left",false]],["EditedProperty",["text","Text","y","0","20",true]],["EditedProperty",["text","Text","text","New text","10",false]],["EditedProperty",["text","Text","text","10","10ω",false]],["EditedProperty",["text","Text","text","10ω","10ω₂",false]],["EditedProperty",["X","X Cursor","color","#5f04a2","#5f04a2",false]],["EditedProperty",["text","Text","color","#242159","#5f04a2",false]],["EditedProperty",["text","Text","disableLatex",false,true,false]],["EditedProperty",["G₁","Gain Bode","labelPosition","above-right","above-left",false]],["ColorChanged",["G₀","Gain Bode","#94AE66","#00aa00"]],["ColorChanged",["G₀","Gain Bode","#00aa00","#0000ff"]],["ColorChanged",["ω","Point","#94AE66","#0000ff"]],["EditedProperty",["ω₂","Point","labelPosition","top","above-left",false]],["EditedProperty",["ω₂","Point","labelPosition","above-left","below-left",false]],["EditedProperty",["ω₂","Point","labelPosition","below-left","above-right",false]],["EditedProperty",["text","Text","disableLatex",false,true,false]]],[]],"width":961,"height":500,"objects":{"Point":[["ω",false,"#0000ff","name","1","(-5)","top","●"],["ω₁",false,"#A38B4D","name","0.1","(-20)","top","●"],["ω₂",true,"#5F04A2","name","5","0","above-right","●"]],"Gain Bode":[["G₀",true,"#0000ff","name","ω","high","0","above",20,false],["G₁",true,"#A38B4D","name","ω₁","low","20","above-left",2,false],["G₂",true,"#5f04a2","name","ω₂","low","(-20)","below",10,true]],"Somme gains Bode":[["G",true,"#3E6E2A","name + value","below",15]],"X Cursor":[["X",true,"#5f04a2","null","50",null,"left",true,3,"— — — — — — —","Next to target"]],"Text":[["text",true,"#5f04a2","null","50","20","left","10ω₂",false]]},"type":"logplotv1"} diff --git a/ci/phase.lpf b/ci/phase.lpf deleted file mode 100644 index 361d157..0000000 --- a/ci/phase.lpf +++ /dev/null @@ -1 +0,0 @@ -LPFv1{"xzoom":100,"yzoom":100,"xmin":0.5,"ymax":2,"xaxisstep":"4","yaxisstep":"pi/4","xaxislabel":"ω (rad/s)","yaxislabel":"φ (rad)","logscalex":true,"linewidth":2,"showxgrad":true,"showygrad":true,"textsize":20,"history":[[["CreateNewObject",["f","Function",["f",true,"#989E2D","name + value","x","ℝ⁺*","ℝ","application","above",1,true,true]]],["EditedProperty",["f","Function","expression","x","(x ^ 20)",true]],["EditedProperty",["f","Function","expression","(x ^ 20)","(20 * (log10 x))",true]],["DeleteObject",["f","Function",["f",true,"#989E2D","name + value","(20 * (log10 x))","ℝ⁺*","ℝ","application","above",1,true,true]]],["CreateNewObject",["ω","Point",["ω",true,"#995178","name","1","0","bottom","●"]]],["CreateNewObject",["φ₀","Phase Bode",["φ₀",true,"#995178","name + value","ω","90","°","below",1]]],["EditedProperty",["φ₀","Phase Bode","phase","90","0",true]],["EditedProperty",["φ₀","Phase Bode","unit","°","rad",false]],["EditedProperty",["ω","Point","y","0","((-pi) / 2)",true]],["EditedVisibility",["ω","Point","visible"]],["EditedProperty",["φ₀","Phase Bode","labelX",1,10,false]],["CreateNewObject",["ω₀","Point",["ω₀",true,"#037753","name","1","0","bottom","●"]]],["CreateNewObject",["φ₁","Phase Bode",["φ₁",true,"#037753","name + value","ω₀","90","°","below",1]]],["EditedProperty",["ω₀","Point","x","1","10",true]],["EditedProperty",["φ₁","Phase Bode","unit","°","rad",false]],["EditedProperty",["φ₁","Phase Bode","phase","90","(pi / 2)",true]],["EditedProperty",["ω₀","Point","x","10","5",true]],["EditedProperty",["ω₀","Point","labelPosition","bottom","top-left",false]],["EditedProperty",["φ₁","Phase Bode","labelX",1,2,false]],["EditedProperty",["φ","Somme phases Bode","labelX",1,2,false]],["ColorChanged",["φ","Somme phases Bode","#665B74","#550000"]],["EditedProperty",["ω₀","Point","labelPosition","top-left","above-left",false]],["EditedProperty",["ω₀","Point","labelPosition","above-left","above-right",false]]],[]],"width":1000,"height":500,"objects":{"Function":[],"Point":[["ω",false,"#995178","name","1","((-pi) / 2)","below","●"],["ω₀",true,"#037753","name","5","0","above-right","●"]],"Phase Bode":[["φ₀",true,"#995178","name","ω","0","rad","below",10],["φ₁",true,"#037753","name","ω₀","(pi / 2)","rad","below",2]],"Somme phases Bode":[["φ",true,"#550000","name + value","above",2]]},"type":"logplotv1"} \ No newline at end of file diff --git a/ci/stress.lpf b/ci/stress.lpf deleted file mode 100644 index 05e171c..0000000 --- a/ci/stress.lpf +++ /dev/null @@ -1 +0,0 @@ -LPFv1{"xzoom":150,"yzoom":10,"xmin":0.49556664658184385,"ymax":25.120703,"xaxisstep":"4","yaxisstep":"4","xaxislabel":"","yaxislabel":"","logscalex":true,"linewidth":1,"showxgrad":true,"showygrad":true,"textsize":18,"history":[[["CreateNewObject",["A","Point",["A",true,"#A35D32","name + value","1","0","above","●"]]],["CreateNewObject",["B","Point",["B",true,"#AF370C","name + value","1","0","above","●"]]],["CreateNewObject",["C","Point",["C",true,"#AB9221","name + value","1","0","above","●"]]],["CreateNewObject",["D","Point",["D",true,"#43A98E","name + value","1","0","above","●"]]],["CreateNewObject",["E","Point",["E",true,"#1F1C6A","name + value","1","0","above","●"]]],["CreateNewObject",["F","Point",["F",true,"#407099","name + value","1","0","above","●"]]],["CreateNewObject",["J","Point",["J",true,"#9B4B7A","name + value","1","0","above","●"]]],["CreateNewObject",["K","Point",["K",true,"#3B5698","name + value","1","0","above","●"]]],["CreateNewObject",["L","Point",["L",true,"#0C6A4D","name + value","1","0","above","●"]]],["CreateNewObject",["M","Point",["M",true,"#350945","name + value","1","0","above","●"]]],["CreateNewObject",["N","Point",["N",true,"#46511E","name + value","1","0","above","●"]]],["CreateNewObject",["O","Point",["O",true,"#0B318B","name + value","1","0","above","●"]]],["CreateNewObject",["P","Point",["P",true,"#354125","name + value","1","0","above","●"]]],["CreateNewObject",["Q","Point",["Q",true,"#598608","name + value","1","0","above","●"]]],["CreateNewObject",["R","Point",["R",true,"#733D64","name + value","1","0","above","●"]]],["CreateNewObject",["S","Point",["S",true,"#7477A8","name + value","1","0","above","●"]]],["CreateNewObject",["T","Point",["T",true,"#5B6034","name + value","1","0","above","●"]]],["CreateNewObject",["U","Point",["U",true,"#951A57","name + value","1","0","above","●"]]],["CreateNewObject",["V","Point",["V",true,"#72804E","name + value","1","0","above","●"]]],["CreateNewObject",["W","Point",["W",true,"#7B1070","name + value","1","0","above","●"]]],["CreateNewObject",["A₀","Point",["A₀",true,"#AFA71F","name + value","1","0","above","●"]]],["CreateNewObject",["B₀","Point",["B₀",true,"#121733","name + value","1","0","above","●"]]],["CreateNewObject",["C₀","Point",["C₀",true,"#363740","name + value","1","0","above","●"]]],["CreateNewObject",["D₀","Point",["D₀",true,"#96896E","name + value","1","0","above","●"]]],["CreateNewObject",["E₀","Point",["E₀",true,"#051126","name + value","1","0","above","●"]]],["CreateNewObject",["F₀","Point",["F₀",true,"#2F004D","name + value","1","0","above","●"]]],["CreateNewObject",["J₀","Point",["J₀",true,"#4B064D","name + value","1","0","above","●"]]],["CreateNewObject",["K₀","Point",["K₀",true,"#7D6527","name + value","1","0","above","●"]]],["CreateNewObject",["L₀","Point",["L₀",true,"#55725F","name + value","1","0","above","●"]]],["CreateNewObject",["M₀","Point",["M₀",true,"#A09E28","name + value","1","0","above","●"]]],["CreateNewObject",["N₀","Point",["N₀",true,"#3A7E1A","name + value","1","0","above","●"]]],["CreateNewObject",["O₀","Point",["O₀",true,"#905361","name + value","1","0","above","●"]]],["CreateNewObject",["P₀","Point",["P₀",true,"#824C96","name + value","1","0","above","●"]]],["CreateNewObject",["t","Text",["t",true,"#7F6A2B","null","1","0","center","New text",false]]],["CreateNewObject",["Q₀","Point",["Q₀",true,"#0B919F","name + value","1","0","above","●"]]],["CreateNewObject",["R₀","Point",["R₀",true,"#308188","name + value","1","0","above","●"]]],["CreateNewObject",["S₀","Point",["S₀",true,"#434DA2","name + value","1","0","above","●"]]],["CreateNewObject",["T₀","Point",["T₀",true,"#5A4455","name + value","1","0","above","●"]]],["CreateNewObject",["U₀","Point",["U₀",true,"#289954","name + value","1","0","above","●"]]],["CreateNewObject",["V₀","Point",["V₀",true,"#5D2371","name + value","1","0","above","●"]]],["CreateNewObject",["W₀","Point",["W₀",true,"#9E9F13","name + value","1","0","above","●"]]],["CreateNewObject",["A₁","Point",["A₁",true,"#6B8E7A","name + value","1","0","above","●"]]],["CreateNewObject",["B₁","Point",["B₁",true,"#25186C","name + value","1","0","above","●"]]],["CreateNewObject",["C₁","Point",["C₁",true,"#8FA8AD","name + value","1","0","above","●"]]],["CreateNewObject",["D₁","Point",["D₁",true,"#5D4528","name + value","1","0","above","●"]]],["CreateNewObject",["E₁","Point",["E₁",true,"#26435D","name + value","1","0","above","●"]]],["CreateNewObject",["F₁","Point",["F₁",true,"#6B7C9C","name + value","1","0","above","●"]]],["CreateNewObject",["J₁","Point",["J₁",true,"#96275E","name + value","1","0","above","●"]]],["CreateNewObject",["K₁","Point",["K₁",true,"#3DAF12","name + value","1","0","above","●"]]],["CreateNewObject",["L₁","Point",["L₁",true,"#1EA902","name + value","1","0","above","●"]]],["CreateNewObject",["t₀","Text",["t₀",true,"#511C6A","null","1","0","center","New text",false]]],["CreateNewObject",["M₁","Point",["M₁",true,"#515D07","name + value","1","0","above","●"]]],["CreateNewObject",["N₁","Point",["N₁",true,"#711764","name + value","1","0","above","●"]]],["CreateNewObject",["O₁","Point",["O₁",true,"#732C94","name + value","1","0","above","●"]]],["CreateNewObject",["P₁","Point",["P₁",true,"#66174C","name + value","1","0","above","●"]]],["CreateNewObject",["Q₁","Point",["Q₁",true,"#75816A","name + value","1","0","above","●"]]],["CreateNewObject",["R₁","Point",["R₁",true,"#AF8C59","name + value","1","0","above","●"]]],["CreateNewObject",["S₁","Point",["S₁",true,"#1E446B","name + value","1","0","above","●"]]],["CreateNewObject",["T₁","Point",["T₁",true,"#A58DA6","name + value","1","0","above","●"]]],["CreateNewObject",["U₁","Point",["U₁",true,"#912D54","name + value","1","0","above","●"]]],["CreateNewObject",["V₁","Point",["V₁",true,"#8E8886","name + value","1","0","above","●"]]],["CreateNewObject",["W₁","Point",["W₁",true,"#26602E","name + value","1","0","above","●"]]],["CreateNewObject",["A₂","Point",["A₂",true,"#8C6064","name + value","1","0","above","●"]]],["CreateNewObject",["B₂","Point",["B₂",true,"#431889","name + value","1","0","above","●"]]],["CreateNewObject",["C₂","Point",["C₂",true,"#8D6959","name + value","1","0","above","●"]]],["CreateNewObject",["D₂","Point",["D₂",true,"#A57776","name + value","1","0","above","●"]]],["CreateNewObject",["E₂","Point",["E₂",true,"#312374","name + value","1","0","above","●"]]],["CreateNewObject",["F₂","Point",["F₂",true,"#3D3150","name + value","1","0","above","●"]]],["CreateNewObject",["J₂","Point",["J₂",true,"#6F9C66","name + value","1","0","above","●"]]],["CreateNewObject",["t₁","Text",["t₁",true,"#84223B","null","1","0","center","New text",false]]],["CreateNewObject",["K₂","Point",["K₂",true,"#3C2899","name + value","1","0","above","●"]]],["CreateNewObject",["L₂","Point",["L₂",true,"#753355","name + value","1","0","above","●"]]],["CreateNewObject",["M₂","Point",["M₂",true,"#3F046A","name + value","1","0","above","●"]]],["CreateNewObject",["N₂","Point",["N₂",true,"#9E4B51","name + value","1","0","above","●"]]],["CreateNewObject",["O₂","Point",["O₂",true,"#199B64","name + value","1","0","above","●"]]],["CreateNewObject",["P₂","Point",["P₂",true,"#59893D","name + value","1","0","above","●"]]],["CreateNewObject",["Q₂","Point",["Q₂",true,"#944D3E","name + value","1","0","above","●"]]],["CreateNewObject",["R₂","Point",["R₂",true,"#8D8836","name + value","1","0","above","●"]]],["CreateNewObject",["S₂","Point",["S₂",true,"#A52589","name + value","1","0","above","●"]]],["CreateNewObject",["t₂","Text",["t₂",true,"#A10A90","null","1","0","center","New text",false]]],["CreateNewObject",["T₂","Point",["T₂",true,"#7F723D","name + value","1","0","above","●"]]],["CreateNewObject",["U₂","Point",["U₂",true,"#78AB96","name + value","1","0","above","●"]]],["CreateNewObject",["V₂","Point",["V₂",true,"#626A23","name + value","1","0","above","●"]]],["CreateNewObject",["W₂","Point",["W₂",true,"#401362","name + value","1","0","above","●"]]],["CreateNewObject",["A₃","Point",["A₃",true,"#9F0E30","name + value","1","0","above","●"]]],["CreateNewObject",["B₃","Point",["B₃",true,"#100C0E","name + value","1","0","above","●"]]],["CreateNewObject",["C₃","Point",["C₃",true,"#858161","name + value","1","0","above","●"]]],["CreateNewObject",["D₃","Point",["D₃",true,"#7482A8","name + value","1","0","above","●"]]],["CreateNewObject",["E₃","Point",["E₃",true,"#0D03A3","name + value","1","0","above","●"]]],["CreateNewObject",["F₃","Point",["F₃",true,"#652922","name + value","1","0","above","●"]]],["CreateNewObject",["J₃","Point",["J₃",true,"#39A671","name + value","1","0","above","●"]]],["CreateNewObject",["K₃","Point",["K₃",true,"#4E0481","name + value","1","0","above","●"]]],["CreateNewObject",["L₃","Point",["L₃",true,"#1F9239","name + value","1","0","above","●"]]],["CreateNewObject",["M₃","Point",["M₃",true,"#986A07","name + value","1","0","above","●"]]],["CreateNewObject",["N₃","Point",["N₃",true,"#4AAE1F","name + value","1","0","above","●"]]],["CreateNewObject",["O₃","Point",["O₃",true,"#3C0911","name + value","1","0","above","●"]]],["CreateNewObject",["P₃","Point",["P₃",true,"#400A42","name + value","1","0","above","●"]]],["CreateNewObject",["Q₃","Point",["Q₃",true,"#9A9C5F","name + value","1","0","above","●"]]],["CreateNewObject",["R₃","Point",["R₃",true,"#AB7FA9","name + value","1","0","above","●"]]],["CreateNewObject",["S₃","Point",["S₃",true,"#4E9D33","name + value","1","0","above","●"]]],["CreateNewObject",["T₃","Point",["T₃",true,"#564E4C","name + value","1","0","above","●"]]],["CreateNewObject",["U₃","Point",["U₃",true,"#674739","name + value","1","0","above","●"]]],["CreateNewObject",["V₃","Point",["V₃",true,"#5D689B","name + value","1","0","above","●"]]],["CreateNewObject",["W₃","Point",["W₃",true,"#6B8E57","name + value","1","0","above","●"]]],["CreateNewObject",["A₄","Point",["A₄",true,"#169C3E","name + value","1","0","above","●"]]],["CreateNewObject",["B₄","Point",["B₄",true,"#95104E","name + value","1","0","above","●"]]],["CreateNewObject",["C₄","Point",["C₄",true,"#73379E","name + value","1","0","above","●"]]],["CreateNewObject",["D₄","Point",["D₄",true,"#98143F","name + value","1","0","above","●"]]],["CreateNewObject",["E₄","Point",["E₄",true,"#A8926F","name + value","1","0","above","●"]]],["CreateNewObject",["F₄","Point",["F₄",true,"#3172AB","name + value","1","0","above","●"]]],["CreateNewObject",["J₄","Point",["J₄",true,"#8A9C96","name + value","1","0","above","●"]]],["CreateNewObject",["K₄","Point",["K₄",true,"#AF001D","name + value","1","0","above","●"]]],["CreateNewObject",["L₄","Point",["L₄",true,"#711495","name + value","1","0","above","●"]]],["CreateNewObject",["M₄","Point",["M₄",true,"#082241","name + value","1","0","above","●"]]],["CreateNewObject",["N₄","Point",["N₄",true,"#631582","name + value","1","0","above","●"]]],["CreateNewObject",["O₄","Point",["O₄",true,"#895378","name + value","1","0","above","●"]]],["CreateNewObject",["P₄","Point",["P₄",true,"#812404","name + value","1","0","above","●"]]],["CreateNewObject",["Q₄","Point",["Q₄",true,"#3B2755","name + value","1","0","above","●"]]],["CreateNewObject",["R₄","Point",["R₄",true,"#5B6E03","name + value","1","0","above","●"]]],["CreateNewObject",["S₄","Point",["S₄",true,"#AF4237","name + value","1","0","above","●"]]],["CreateNewObject",["T₄","Point",["T₄",true,"#9C75AC","name + value","1","0","above","●"]]],["CreateNewObject",["U₄","Point",["U₄",true,"#926178","name + value","1","0","above","●"]]],["CreateNewObject",["V₄","Point",["V₄",true,"#8C3DA7","name + value","1","0","above","●"]]],["CreateNewObject",["W₄","Point",["W₄",true,"#95843C","name + value","1","0","above","●"]]],["CreateNewObject",["A₅","Point",["A₅",true,"#022359","name + value","1","0","above","●"]]],["CreateNewObject",["B₅","Point",["B₅",true,"#8B906E","name + value","1","0","above","●"]]],["CreateNewObject",["C₅","Point",["C₅",true,"#317B9A","name + value","1","0","above","●"]]],["CreateNewObject",["D₅","Point",["D₅",true,"#559232","name + value","1","0","above","●"]]],["CreateNewObject",["E₅","Point",["E₅",true,"#9324A5","name + value","1","0","above","●"]]],["CreateNewObject",["F₅","Point",["F₅",true,"#574940","name + value","1","0","above","●"]]],["CreateNewObject",["J₅","Point",["J₅",true,"#A93968","name + value","1","0","above","●"]]],["CreateNewObject",["K₅","Point",["K₅",true,"#1F6266","name + value","1","0","above","●"]]],["CreateNewObject",["L₅","Point",["L₅",true,"#181928","name + value","1","0","above","●"]]],["CreateNewObject",["M₅","Point",["M₅",true,"#760C13","name + value","1","0","above","●"]]],["CreateNewObject",["N₅","Point",["N₅",true,"#366B7E","name + value","1","0","above","●"]]],["CreateNewObject",["O₅","Point",["O₅",true,"#8E6060","name + value","1","0","above","●"]]],["CreateNewObject",["P₅","Point",["P₅",true,"#A8158D","name + value","1","0","above","●"]]],["CreateNewObject",["Q₅","Point",["Q₅",true,"#1D206C","name + value","1","0","above","●"]]],["CreateNewObject",["R₅","Point",["R₅",true,"#9C6169","name + value","1","0","above","●"]]],["CreateNewObject",["S₅","Point",["S₅",true,"#6F412F","name + value","1","0","above","●"]]],["CreateNewObject",["T₅","Point",["T₅",true,"#0B2453","name + value","1","0","above","●"]]],["CreateNewObject",["U₅","Point",["U₅",true,"#04839E","name + value","1","0","above","●"]]],["CreateNewObject",["V₅","Point",["V₅",true,"#8B29A5","name + value","1","0","above","●"]]],["CreateNewObject",["W₅","Point",["W₅",true,"#491438","name + value","1","0","above","●"]]],["CreateNewObject",["A₆","Point",["A₆",true,"#00087B","name + value","1","0","above","●"]]],["CreateNewObject",["B₆","Point",["B₆",true,"#0F431C","name + value","1","0","above","●"]]],["CreateNewObject",["C₆","Point",["C₆",true,"#1D3B07","name + value","1","0","above","●"]]],["CreateNewObject",["D₆","Point",["D₆",true,"#7F695A","name + value","1","0","above","●"]]],["CreateNewObject",["E₆","Point",["E₆",true,"#AB4383","name + value","1","0","above","●"]]],["CreateNewObject",["F₆","Point",["F₆",true,"#9C9E78","name + value","1","0","above","●"]]],["CreateNewObject",["J₆","Point",["J₆",true,"#243945","name + value","1","0","above","●"]]],["CreateNewObject",["K₆","Point",["K₆",true,"#A49EAA","name + value","1","0","above","●"]]],["CreateNewObject",["L₆","Point",["L₆",true,"#822C32","name + value","1","0","above","●"]]],["CreateNewObject",["M₆","Point",["M₆",true,"#095D68","name + value","1","0","above","●"]]],["CreateNewObject",["N₆","Point",["N₆",true,"#AA361D","name + value","1","0","above","●"]]],["CreateNewObject",["O₆","Point",["O₆",true,"#045B40","name + value","1","0","above","●"]]],["CreateNewObject",["P₆","Point",["P₆",true,"#2B10AD","name + value","1","0","above","●"]]],["CreateNewObject",["Q₆","Point",["Q₆",true,"#8F0607","name + value","1","0","above","●"]]],["CreateNewObject",["R₆","Point",["R₆",true,"#953472","name + value","1","0","above","●"]]],["CreateNewObject",["S₆","Point",["S₆",true,"#7F7663","name + value","1","0","above","●"]]],["CreateNewObject",["T₆","Point",["T₆",true,"#9A4294","name + value","1","0","above","●"]]],["CreateNewObject",["U₆","Point",["U₆",true,"#924762","name + value","1","0","above","●"]]],["CreateNewObject",["V₆","Point",["V₆",true,"#006385","name + value","1","0","above","●"]]],["CreateNewObject",["W₆","Point",["W₆",true,"#8C0504","name + value","1","0","above","●"]]],["CreateNewObject",["A₇","Point",["A₇",true,"#337BA0","name + value","1","0","above","●"]]],["CreateNewObject",["B₇","Point",["B₇",true,"#970A47","name + value","1","0","above","●"]]],["CreateNewObject",["C₇","Point",["C₇",true,"#8D071F","name + value","1","0","above","●"]]],["CreateNewObject",["D₇","Point",["D₇",true,"#241417","name + value","1","0","above","●"]]],["CreateNewObject",["E₇","Point",["E₇",true,"#8DAB44","name + value","1","0","above","●"]]],["CreateNewObject",["F₇","Point",["F₇",true,"#555D7B","name + value","1","0","above","●"]]],["CreateNewObject",["J₇","Point",["J₇",true,"#537A31","name + value","1","0","above","●"]]],["CreateNewObject",["K₇","Point",["K₇",true,"#298D88","name + value","1","0","above","●"]]],["CreateNewObject",["L₇","Point",["L₇",true,"#55249C","name + value","1","0","above","●"]]],["CreateNewObject",["M₇","Point",["M₇",true,"#AB5E71","name + value","1","0","above","●"]]],["CreateNewObject",["N₇","Point",["N₇",true,"#834D89","name + value","1","0","above","●"]]],["CreateNewObject",["O₇","Point",["O₇",true,"#7B4EAC","name + value","1","0","above","●"]]],["CreateNewObject",["P₇","Point",["P₇",true,"#451133","name + value","1","0","above","●"]]],["CreateNewObject",["Q₇","Point",["Q₇",true,"#07410B","name + value","1","0","above","●"]]],["CreateNewObject",["R₇","Point",["R₇",true,"#5D0B61","name + value","1","0","above","●"]]],["CreateNewObject",["S₇","Point",["S₇",true,"#1F184F","name + value","1","0","above","●"]]],["CreateNewObject",["T₇","Point",["T₇",true,"#897283","name + value","1","0","above","●"]]],["CreateNewObject",["U₇","Point",["U₇",true,"#079906","name + value","1","0","above","●"]]],["CreateNewObject",["V₇","Point",["V₇",true,"#1DA545","name + value","1","0","above","●"]]],["CreateNewObject",["W₇","Point",["W₇",true,"#4E4A71","name + value","1","0","above","●"]]],["CreateNewObject",["A₈","Point",["A₈",true,"#563577","name + value","1","0","above","●"]]],["CreateNewObject",["B₈","Point",["B₈",true,"#6FA324","name + value","1","0","above","●"]]],["CreateNewObject",["C₈","Point",["C₈",true,"#099187","name + value","1","0","above","●"]]],["CreateNewObject",["D₈","Point",["D₈",true,"#0F976A","name + value","1","0","above","●"]]],["CreateNewObject",["E₈","Point",["E₈",true,"#525B2F","name + value","1","0","above","●"]]],["CreateNewObject",["F₈","Point",["F₈",true,"#249C5A","name + value","1","0","above","●"]]],["CreateNewObject",["J₈","Point",["J₈",true,"#359EAD","name + value","1","0","above","●"]]],["CreateNewObject",["K₈","Point",["K₈",true,"#014113","name + value","1","0","above","●"]]],["CreateNewObject",["L₈","Point",["L₈",true,"#992D2C","name + value","1","0","above","●"]]],["CreateNewObject",["M₈","Point",["M₈",true,"#2A9D50","name + value","1","0","above","●"]]],["CreateNewObject",["N₈","Point",["N₈",true,"#3BA6A8","name + value","1","0","above","●"]]],["CreateNewObject",["O₈","Point",["O₈",true,"#2F2BAB","name + value","1","0","above","●"]]],["CreateNewObject",["P₈","Point",["P₈",true,"#A35282","name + value","1","0","above","●"]]],["CreateNewObject",["Q₈","Point",["Q₈",true,"#346C86","name + value","1","0","above","●"]]],["CreateNewObject",["R₈","Point",["R₈",true,"#738755","name + value","1","0","above","●"]]],["CreateNewObject",["S₈","Point",["S₈",true,"#2E302D","name + value","1","0","above","●"]]],["CreateNewObject",["T₈","Point",["T₈",true,"#187E5B","name + value","1","0","above","●"]]],["CreateNewObject",["U₈","Point",["U₈",true,"#4B868E","name + value","1","0","above","●"]]],["CreateNewObject",["V₈","Point",["V₈",true,"#13937C","name + value","1","0","above","●"]]],["CreateNewObject",["W₈","Point",["W₈",true,"#938E72","name + value","1","0","above","●"]]]],[]],"width":1000,"height":500,"objects":{"Point":[["A",true,"#A35D32","name + value","1","0","above","●"],["B",true,"#AF370C","name + value","1","0","above","●"],["C",true,"#AB9221","name + value","1","0","above","●"],["D",true,"#43A98E","name + value","1","0","above","●"],["E",true,"#1F1C6A","name + value","1","0","above","●"],["F",true,"#407099","name + value","1","0","above","●"],["J",true,"#9B4B7A","name + value","1","0","above","●"],["K",true,"#3B5698","name + value","1","0","above","●"],["L",true,"#0C6A4D","name + value","1","0","above","●"],["M",true,"#350945","name + value","1","0","above","●"],["N",true,"#46511E","name + value","1","0","above","●"],["O",true,"#0B318B","name + value","1","0","above","●"],["P",true,"#354125","name + value","1","0","above","●"],["Q",true,"#598608","name + value","1","0","above","●"],["R",true,"#733D64","name + value","1","0","above","●"],["S",true,"#7477A8","name + value","1","0","above","●"],["T",true,"#5B6034","name + value","1","0","above","●"],["U",true,"#951A57","name + value","1","0","above","●"],["V",true,"#72804E","name + value","1","0","above","●"],["W",true,"#7B1070","name + value","1","0","above","●"],["A₀",true,"#AFA71F","name + value","1","0","above","●"],["B₀",true,"#121733","name + value","1","0","above","●"],["C₀",true,"#363740","name + value","1","0","above","●"],["D₀",true,"#96896E","name + value","1","0","above","●"],["E₀",true,"#051126","name + value","1","0","above","●"],["F₀",true,"#2F004D","name + value","1","0","above","●"],["J₀",true,"#4B064D","name + value","1","0","above","●"],["K₀",true,"#7D6527","name + value","1","0","above","●"],["L₀",true,"#55725F","name + value","1","0","above","●"],["M₀",true,"#A09E28","name + value","1","0","above","●"],["N₀",true,"#3A7E1A","name + value","1","0","above","●"],["O₀",true,"#905361","name + value","1","0","above","●"],["P₀",true,"#824C96","name + value","1","0","above","●"],["Q₀",true,"#0B919F","name + value","1","0","above","●"],["R₀",true,"#308188","name + value","1","0","above","●"],["S₀",true,"#434DA2","name + value","1","0","above","●"],["T₀",true,"#5A4455","name + value","1","0","above","●"],["U₀",true,"#289954","name + value","1","0","above","●"],["V₀",true,"#5D2371","name + value","1","0","above","●"],["W₀",true,"#9E9F13","name + value","1","0","above","●"],["A₁",true,"#6B8E7A","name + value","1","0","above","●"],["B₁",true,"#25186C","name + value","1","0","above","●"],["C₁",true,"#8FA8AD","name + value","1","0","above","●"],["D₁",true,"#5D4528","name + value","1","0","above","●"],["E₁",true,"#26435D","name + value","1","0","above","●"],["F₁",true,"#6B7C9C","name + value","1","0","above","●"],["J₁",true,"#96275E","name + value","1","0","above","●"],["K₁",true,"#3DAF12","name + value","1","0","above","●"],["L₁",true,"#1EA902","name + value","1","0","above","●"],["M₁",true,"#515D07","name + value","1","0","above","●"],["N₁",true,"#711764","name + value","1","0","above","●"],["O₁",true,"#732C94","name + value","1","0","above","●"],["P₁",true,"#66174C","name + value","1","0","above","●"],["Q₁",true,"#75816A","name + value","1","0","above","●"],["R₁",true,"#AF8C59","name + value","1","0","above","●"],["S₁",true,"#1E446B","name + value","1","0","above","●"],["T₁",true,"#A58DA6","name + value","1","0","above","●"],["U₁",true,"#912D54","name + value","1","0","above","●"],["V₁",true,"#8E8886","name + value","1","0","above","●"],["W₁",true,"#26602E","name + value","1","0","above","●"],["A₂",true,"#8C6064","name + value","1","0","above","●"],["B₂",true,"#431889","name + value","1","0","above","●"],["C₂",true,"#8D6959","name + value","1","0","above","●"],["D₂",true,"#A57776","name + value","1","0","above","●"],["E₂",true,"#312374","name + value","1","0","above","●"],["F₂",true,"#3D3150","name + value","1","0","above","●"],["J₂",true,"#6F9C66","name + value","1","0","above","●"],["K₂",true,"#3C2899","name + value","1","0","above","●"],["L₂",true,"#753355","name + value","1","0","above","●"],["M₂",true,"#3F046A","name + value","1","0","above","●"],["N₂",true,"#9E4B51","name + value","1","0","above","●"],["O₂",true,"#199B64","name + value","1","0","above","●"],["P₂",true,"#59893D","name + value","1","0","above","●"],["Q₂",true,"#944D3E","name + value","1","0","above","●"],["R₂",true,"#8D8836","name + value","1","0","above","●"],["S₂",true,"#A52589","name + value","1","0","above","●"],["T₂",true,"#7F723D","name + value","1","0","above","●"],["U₂",true,"#78AB96","name + value","1","0","above","●"],["V₂",true,"#626A23","name + value","1","0","above","●"],["W₂",true,"#401362","name + value","1","0","above","●"],["A₃",true,"#9F0E30","name + value","1","0","above","●"],["B₃",true,"#100C0E","name + value","1","0","above","●"],["C₃",true,"#858161","name + value","1","0","above","●"],["D₃",true,"#7482A8","name + value","1","0","above","●"],["E₃",true,"#0D03A3","name + value","1","0","above","●"],["F₃",true,"#652922","name + value","1","0","above","●"],["J₃",true,"#39A671","name + value","1","0","above","●"],["K₃",true,"#4E0481","name + value","1","0","above","●"],["L₃",true,"#1F9239","name + value","1","0","above","●"],["M₃",true,"#986A07","name + value","1","0","above","●"],["N₃",true,"#4AAE1F","name + value","1","0","above","●"],["O₃",true,"#3C0911","name + value","1","0","above","●"],["P₃",true,"#400A42","name + value","1","0","above","●"],["Q₃",true,"#9A9C5F","name + value","1","0","above","●"],["R₃",true,"#AB7FA9","name + value","1","0","above","●"],["S₃",true,"#4E9D33","name + value","1","0","above","●"],["T₃",true,"#564E4C","name + value","1","0","above","●"],["U₃",true,"#674739","name + value","1","0","above","●"],["V₃",true,"#5D689B","name + value","1","0","above","●"],["W₃",true,"#6B8E57","name + value","1","0","above","●"],["A₄",true,"#169C3E","name + value","1","0","above","●"],["B₄",true,"#95104E","name + value","1","0","above","●"],["C₄",true,"#73379E","name + value","1","0","above","●"],["D₄",true,"#98143F","name + value","1","0","above","●"],["E₄",true,"#A8926F","name + value","1","0","above","●"],["F₄",true,"#3172AB","name + value","1","0","above","●"],["J₄",true,"#8A9C96","name + value","1","0","above","●"],["K₄",true,"#AF001D","name + value","1","0","above","●"],["L₄",true,"#711495","name + value","1","0","above","●"],["M₄",true,"#082241","name + value","1","0","above","●"],["N₄",true,"#631582","name + value","1","0","above","●"],["O₄",true,"#895378","name + value","1","0","above","●"],["P₄",true,"#812404","name + value","1","0","above","●"],["Q₄",true,"#3B2755","name + value","1","0","above","●"],["R₄",true,"#5B6E03","name + value","1","0","above","●"],["S₄",true,"#AF4237","name + value","1","0","above","●"],["T₄",true,"#9C75AC","name + value","1","0","above","●"],["U₄",true,"#926178","name + value","1","0","above","●"],["V₄",true,"#8C3DA7","name + value","1","0","above","●"],["W₄",true,"#95843C","name + value","1","0","above","●"],["A₅",true,"#022359","name + value","1","0","above","●"],["B₅",true,"#8B906E","name + value","1","0","above","●"],["C₅",true,"#317B9A","name + value","1","0","above","●"],["D₅",true,"#559232","name + value","1","0","above","●"],["E₅",true,"#9324A5","name + value","1","0","above","●"],["F₅",true,"#574940","name + value","1","0","above","●"],["J₅",true,"#A93968","name + value","1","0","above","●"],["K₅",true,"#1F6266","name + value","1","0","above","●"],["L₅",true,"#181928","name + value","1","0","above","●"],["M₅",true,"#760C13","name + value","1","0","above","●"],["N₅",true,"#366B7E","name + value","1","0","above","●"],["O₅",true,"#8E6060","name + value","1","0","above","●"],["P₅",true,"#A8158D","name + value","1","0","above","●"],["Q₅",true,"#1D206C","name + value","1","0","above","●"],["R₅",true,"#9C6169","name + value","1","0","above","●"],["S₅",true,"#6F412F","name + value","1","0","above","●"],["T₅",true,"#0B2453","name + value","1","0","above","●"],["U₅",true,"#04839E","name + value","1","0","above","●"],["V₅",true,"#8B29A5","name + value","1","0","above","●"],["W₅",true,"#491438","name + value","1","0","above","●"],["A₆",true,"#00087B","name + value","1","0","above","●"],["B₆",true,"#0F431C","name + value","1","0","above","●"],["C₆",true,"#1D3B07","name + value","1","0","above","●"],["D₆",true,"#7F695A","name + value","1","0","above","●"],["E₆",true,"#AB4383","name + value","1","0","above","●"],["F₆",true,"#9C9E78","name + value","1","0","above","●"],["J₆",true,"#243945","name + value","1","0","above","●"],["K₆",true,"#A49EAA","name + value","1","0","above","●"],["L₆",true,"#822C32","name + value","1","0","above","●"],["M₆",true,"#095D68","name + value","1","0","above","●"],["N₆",true,"#AA361D","name + value","1","0","above","●"],["O₆",true,"#045B40","name + value","1","0","above","●"],["P₆",true,"#2B10AD","name + value","1","0","above","●"],["Q₆",true,"#8F0607","name + value","1","0","above","●"],["R₆",true,"#953472","name + value","1","0","above","●"],["S₆",true,"#7F7663","name + value","1","0","above","●"],["T₆",true,"#9A4294","name + value","1","0","above","●"],["U₆",true,"#924762","name + value","1","0","above","●"],["V₆",true,"#006385","name + value","1","0","above","●"],["W₆",true,"#8C0504","name + value","1","0","above","●"],["A₇",true,"#337BA0","name + value","1","0","above","●"],["B₇",true,"#970A47","name + value","1","0","above","●"],["C₇",true,"#8D071F","name + value","1","0","above","●"],["D₇",true,"#241417","name + value","1","0","above","●"],["E₇",true,"#8DAB44","name + value","1","0","above","●"],["F₇",true,"#555D7B","name + value","1","0","above","●"],["J₇",true,"#537A31","name + value","1","0","above","●"],["K₇",true,"#298D88","name + value","1","0","above","●"],["L₇",true,"#55249C","name + value","1","0","above","●"],["M₇",true,"#AB5E71","name + value","1","0","above","●"],["N₇",true,"#834D89","name + value","1","0","above","●"],["O₇",true,"#7B4EAC","name + value","1","0","above","●"],["P₇",true,"#451133","name + value","1","0","above","●"],["Q₇",true,"#07410B","name + value","1","0","above","●"],["R₇",true,"#5D0B61","name + value","1","0","above","●"],["S₇",true,"#1F184F","name + value","1","0","above","●"],["T₇",true,"#897283","name + value","1","0","above","●"],["U₇",true,"#079906","name + value","1","0","above","●"],["V₇",true,"#1DA545","name + value","1","0","above","●"],["W₇",true,"#4E4A71","name + value","1","0","above","●"],["A₈",true,"#563577","name + value","1","0","above","●"],["B₈",true,"#6FA324","name + value","1","0","above","●"],["C₈",true,"#099187","name + value","1","0","above","●"],["D₈",true,"#0F976A","name + value","1","0","above","●"],["E₈",true,"#525B2F","name + value","1","0","above","●"],["F₈",true,"#249C5A","name + value","1","0","above","●"],["J₈",true,"#359EAD","name + value","1","0","above","●"],["K₈",true,"#014113","name + value","1","0","above","●"],["L₈",true,"#992D2C","name + value","1","0","above","●"],["M₈",true,"#2A9D50","name + value","1","0","above","●"],["N₈",true,"#3BA6A8","name + value","1","0","above","●"],["O₈",true,"#2F2BAB","name + value","1","0","above","●"],["P₈",true,"#A35282","name + value","1","0","above","●"],["Q₈",true,"#346C86","name + value","1","0","above","●"],["R₈",true,"#738755","name + value","1","0","above","●"],["S₈",true,"#2E302D","name + value","1","0","above","●"],["T₈",true,"#187E5B","name + value","1","0","above","●"],["U₈",true,"#4B868E","name + value","1","0","above","●"],["V₈",true,"#13937C","name + value","1","0","above","●"],["W₈",true,"#938E72","name + value","1","0","above","●"]],"Text":[["t",true,"#7F6A2B","null","1","0","center","New text",false],["t₀",true,"#511C6A","null","1","0","center","New text",false],["t₁",true,"#84223B","null","1","0","center","New text",false],["t₂",true,"#A10A90","null","1","0","center","New text",false]]},"type":"logplotv1"} \ No newline at end of file diff --git a/common/babel.config.json b/common/babel.config.json deleted file mode 100644 index fd63c92..0000000 --- a/common/babel.config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "presets": ["@babel/preset-env"], - "targets": { - "esmodules": true - } -} \ No newline at end of file diff --git a/common/package-lock.json b/common/package-lock.json deleted file mode 100644 index 9ca31b3..0000000 --- a/common/package-lock.json +++ /dev/null @@ -1,4083 +0,0 @@ -{ - "name": "logarithmplotter", - "version": "0.6.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "logarithmplotter", - "version": "0.6.0", - "license": "GPL-3.0-or-later", - "dependencies": { - "@babel/preset-env": "^7.25.4", - "@rollup/plugin-babel": "^6.0.4", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "c8": "^10.1.2", - "rollup": "^4.22.4", - "rollup-plugin-cleanup": "^3.2.1" - }, - "devDependencies": { - "@types/chai": "^5.0.0", - "@types/chai-as-promised": "^8.0.1", - "@types/chai-spies": "^1.0.6", - "@types/mocha": "^10.0.8", - "chai": "^5.1.1", - "chai-as-promised": "^8.0.0", - "chai-spies": "^1.1.0", - "esm": "^3.2.25", - "mocha": "^10.7.3" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", - "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.10", - "@babel/types": "^7.26.10", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", - "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", - "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.26.8", - "@babel/helper-validator-option": "^7.25.9", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.0.tgz", - "integrity": "sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/helper-replace-supers": "^7.26.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/traverse": "^7.27.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.0.tgz", - "integrity": "sha512-fO8l08T76v48BhpNRW/nQ0MxfnSdoSKUJBMjubOAYffsVuGG5qOfMq7N6Es7UJvi7Y8goXXo07EfcHZXDPuELQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "regexpu-core": "^6.2.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz", - "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", - "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", - "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", - "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-wrap-function": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", - "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/traverse": "^7.26.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", - "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", - "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", - "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", - "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", - "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", - "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", - "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", - "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", - "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", - "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", - "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", - "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.26.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", - "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-remap-async-to-generator": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", - "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.0.tgz", - "integrity": "sha512-u1jGphZ8uDI2Pj/HJj6YQ6XQLZCNjOlprjxB5SVz6rq2T6SwAR+CdrWK0CP7F+9rDVMXdB0+r6Am5G5aobOjAQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", - "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", - "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", - "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", - "@babel/traverse": "^7.25.9", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", - "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/template": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", - "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", - "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", - "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", - "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", - "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", - "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", - "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", - "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", - "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", - "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", - "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", - "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", - "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", - "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", - "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", - "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", - "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", - "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", - "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.26.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", - "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", - "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", - "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", - "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", - "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", - "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", - "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", - "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", - "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", - "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.0.tgz", - "integrity": "sha512-LX/vCajUJQDqE7Aum/ELUMZAY19+cDpghxrnyt5I1tV6X5PyC86AOoWXWFYFeIvauyeSA6/ktn4tQVn/3ZifsA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "regenerator-transform": "^0.15.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", - "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", - "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", - "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", - "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", - "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", - "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.0.tgz", - "integrity": "sha512-+LLkxA9rKJpNoGsbLnAgOCdESl73vwYn+V6b+5wHbrE7OGKVDPHIQvbFSzqE6rwqaCw2RE+zdJrlLkcf8YOA0w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", - "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", - "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", - "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", - "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", - "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.26.8", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.26.0", - "@babel/plugin-syntax-import-attributes": "^7.26.0", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.25.9", - "@babel/plugin-transform-async-generator-functions": "^7.26.8", - "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.26.5", - "@babel/plugin-transform-block-scoping": "^7.25.9", - "@babel/plugin-transform-class-properties": "^7.25.9", - "@babel/plugin-transform-class-static-block": "^7.26.0", - "@babel/plugin-transform-classes": "^7.25.9", - "@babel/plugin-transform-computed-properties": "^7.25.9", - "@babel/plugin-transform-destructuring": "^7.25.9", - "@babel/plugin-transform-dotall-regex": "^7.25.9", - "@babel/plugin-transform-duplicate-keys": "^7.25.9", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.26.3", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-for-of": "^7.26.9", - "@babel/plugin-transform-function-name": "^7.25.9", - "@babel/plugin-transform-json-strings": "^7.25.9", - "@babel/plugin-transform-literals": "^7.25.9", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", - "@babel/plugin-transform-member-expression-literals": "^7.25.9", - "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.26.3", - "@babel/plugin-transform-modules-systemjs": "^7.25.9", - "@babel/plugin-transform-modules-umd": "^7.25.9", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", - "@babel/plugin-transform-numeric-separator": "^7.25.9", - "@babel/plugin-transform-object-rest-spread": "^7.25.9", - "@babel/plugin-transform-object-super": "^7.25.9", - "@babel/plugin-transform-optional-catch-binding": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9", - "@babel/plugin-transform-private-methods": "^7.25.9", - "@babel/plugin-transform-private-property-in-object": "^7.25.9", - "@babel/plugin-transform-property-literals": "^7.25.9", - "@babel/plugin-transform-regenerator": "^7.25.9", - "@babel/plugin-transform-regexp-modifiers": "^7.26.0", - "@babel/plugin-transform-reserved-words": "^7.25.9", - "@babel/plugin-transform-shorthand-properties": "^7.25.9", - "@babel/plugin-transform-spread": "^7.25.9", - "@babel/plugin-transform-sticky-regex": "^7.25.9", - "@babel/plugin-transform-template-literals": "^7.26.8", - "@babel/plugin-transform-typeof-symbol": "^7.26.7", - "@babel/plugin-transform-unicode-escapes": "^7.25.9", - "@babel/plugin-transform-unicode-property-regex": "^7.25.9", - "@babel/plugin-transform-unicode-regex": "^7.25.9", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.11.0", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.40.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", - "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", - "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.27.0", - "@babel/parser": "^7.27.0", - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@rollup/plugin-babel": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz", - "integrity": "sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@rollup/pluginutils": "^5.0.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "@types/babel__core": "^7.1.9", - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "@types/babel__core": { - "optional": true - }, - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-commonjs": { - "version": "28.0.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.3.tgz", - "integrity": "sha512-pyltgilam1QPdn+Zd9gaCfOLcnjMEJ9gV+bTw6/r73INdvzf1ah9zLIJBm+kW7R6IUFIQ1YO+VqZtYxZNWFPEQ==", - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "commondir": "^1.0.1", - "estree-walker": "^2.0.2", - "fdir": "^6.2.0", - "is-reference": "1.2.1", - "magic-string": "^0.30.3", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=16.0.0 || 14 >= 14.17" - }, - "peerDependencies": { - "rollup": "^2.68.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", - "integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==", - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "@types/resolve": "1.20.2", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.78.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", - "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", - "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", - "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", - "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", - "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", - "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", - "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", - "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", - "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", - "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", - "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", - "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", - "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", - "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", - "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", - "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", - "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", - "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", - "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", - "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", - "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/chai": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.1.tgz", - "integrity": "sha512-iu1JLYmGmITRzUgNiLMZD3WCoFzpYtueuyAgHTXqgwSRAMIlFTnZqG6/xenkpUGRJEzSfklUTI4GNSzks/dc0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*" - } - }, - "node_modules/@types/chai-as-promised": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-8.0.2.tgz", - "integrity": "sha512-meQ1wDr1K5KRCSvG2lX7n7/5wf70BeptTKst0axGvnN6zqaVpRqegoIbugiAPSqOW9K9aL8gDVrm7a2LXOtn2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "*" - } - }, - "node_modules/@types/chai-spies": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-1.0.6.tgz", - "integrity": "sha512-xkk4HmhBB9OQeTAifa9MJ+6R5/Rq9+ungDe4JidZD+vqZVeiWZwc2i7/pd1ZKjyGlSBIQePoWdyUyFUGT0rv5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "*" - } - }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "license": "MIT" - }, - "node_modules/@types/mocha": { - "version": "10.0.10", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", - "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/resolve": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", - "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", - "license": "MIT" - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz", - "integrity": "sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.4", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", - "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.3", - "core-js-compat": "^3.40.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz", - "integrity": "sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.4" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true, - "license": "ISC" - }, - "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/c8": { - "version": "10.1.3", - "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", - "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==", - "license": "ISC", - "dependencies": { - "@bcoe/v8-coverage": "^1.0.1", - "@istanbuljs/schema": "^0.1.3", - "find-up": "^5.0.0", - "foreground-child": "^3.1.1", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.1", - "istanbul-reports": "^3.1.6", - "test-exclude": "^7.0.1", - "v8-to-istanbul": "^9.0.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1" - }, - "bin": { - "c8": "bin/c8.js" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "monocart-coverage-reports": "^2" - }, - "peerDependenciesMeta": { - "monocart-coverage-reports": { - "optional": true - } - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001715", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz", - "integrity": "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/chai-as-promised": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-8.0.1.tgz", - "integrity": "sha512-OIEJtOL8xxJSH8JJWbIoRjybbzR52iFuDHuF8eb+nTPD6tgXLjRqsgnUGqQfFODxYvq5QdirT0pN9dZ0+Gz6rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "check-error": "^2.0.0" - }, - "peerDependencies": { - "chai": ">= 2.1.2 < 6" - } - }, - "node_modules/chai-spies": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-1.1.0.tgz", - "integrity": "sha512-ikaUhQvQWchRYj2K54itFp3nrcxaFRpSDQxDlRzSn9aWgu9Pi7lD8yFxTso4WnQ39+WZ69oB/qOvqp+isJIIWA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - }, - "peerDependencies": { - "chai": "*" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" - }, - "node_modules/core-js-compat": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", - "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.140", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.140.tgz", - "integrity": "sha512-o82Rj+ONp4Ip7Cl1r7lrqx/pXhbp/lh9DpKcMNscFJdh8ebyRofnc7Sh01B4jx403RI0oqTBvlZ7OBIZLMr2+Q==", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "license": "MIT" - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "license": "MIT" - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/js-cleanup": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/js-cleanup/-/js-cleanup-1.2.0.tgz", - "integrity": "sha512-JeDD0yiiSt80fXzAVa/crrS0JDPQljyBG/RpOtaSbyDq03VHa9szJWMaWOYU/bcTn412uMN2MxApXq8v79cUiQ==", - "license": "MIT", - "dependencies": { - "magic-string": "^0.25.7", - "perf-regexes": "^1.0.1", - "skip-regex": "^1.0.2" - }, - "engines": { - "node": "^10.14.2 || >=12.0.0" - } - }, - "node_modules/js-cleanup/node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "license": "MIT", - "dependencies": { - "sourcemap-codec": "^1.4.8" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "peer": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mocha": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", - "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^8.1.0", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/perf-regexes": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/perf-regexes/-/perf-regexes-1.0.1.tgz", - "integrity": "sha512-L7MXxUDtqr4PUaLFCDCXBfGV/6KLIuSEccizDI7JxT+c9x1G1v04BQ4+4oag84SHaCdrBgQAIs/Cqn+flwFPng==", - "license": "MIT", - "engines": { - "node": ">=6.14" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regexpu-core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.0.2" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/rollup": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", - "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.40.0", - "@rollup/rollup-android-arm64": "4.40.0", - "@rollup/rollup-darwin-arm64": "4.40.0", - "@rollup/rollup-darwin-x64": "4.40.0", - "@rollup/rollup-freebsd-arm64": "4.40.0", - "@rollup/rollup-freebsd-x64": "4.40.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", - "@rollup/rollup-linux-arm-musleabihf": "4.40.0", - "@rollup/rollup-linux-arm64-gnu": "4.40.0", - "@rollup/rollup-linux-arm64-musl": "4.40.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", - "@rollup/rollup-linux-riscv64-gnu": "4.40.0", - "@rollup/rollup-linux-riscv64-musl": "4.40.0", - "@rollup/rollup-linux-s390x-gnu": "4.40.0", - "@rollup/rollup-linux-x64-gnu": "4.40.0", - "@rollup/rollup-linux-x64-musl": "4.40.0", - "@rollup/rollup-win32-arm64-msvc": "4.40.0", - "@rollup/rollup-win32-ia32-msvc": "4.40.0", - "@rollup/rollup-win32-x64-msvc": "4.40.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup-plugin-cleanup": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/rollup-plugin-cleanup/-/rollup-plugin-cleanup-3.2.1.tgz", - "integrity": "sha512-zuv8EhoO3TpnrU8MX8W7YxSbO4gmOR0ny06Lm3nkFfq0IVKdBUtHwhVzY1OAJyNCIAdLiyPnOrU0KnO0Fri1GQ==", - "license": "MIT", - "dependencies": { - "js-cleanup": "^1.2.0", - "rollup-pluginutils": "^2.8.2" - }, - "engines": { - "node": "^10.14.2 || >=12.0.0" - }, - "peerDependencies": { - "rollup": ">=2.0" - } - }, - "node_modules/rollup-pluginutils": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", - "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", - "license": "MIT", - "dependencies": { - "estree-walker": "^0.6.1" - } - }, - "node_modules/rollup-pluginutils/node_modules/estree-walker": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", - "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", - "license": "MIT" - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/skip-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/skip-regex/-/skip-regex-1.0.2.tgz", - "integrity": "sha512-pEjMUbwJ5Pl/6Vn6FsamXHXItJXSRftcibixDmNCWbWhic0hzHrwkMZo0IZ7fMRH9KxcWDFSkzhccB4285PutA==", - "license": "MIT", - "engines": { - "node": ">=4.2" - } - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "license": "MIT" - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", - "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^9.0.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/common/package.json b/common/package.json deleted file mode 100644 index a368ef5..0000000 --- a/common/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "logarithmplotter", - "version": "0.6.0", - "description": "2D plotter software to make Bode plots, sequences and distribution functions.", - "main": "src/index.mjs", - "scripts": { - "build": "rollup --config rollup.config.mjs", - "test": "c8 mocha test/**/*.mjs" - }, - "repository": { - "type": "git", - "url": "https://git.ad5001.eu/Ad5001/LogarithmPlotter" - }, - "author": "Ad5001 ", - "license": "GPL-3.0-or-later", - "dependencies": { - "@babel/preset-env": "^7.25.4", - "@rollup/plugin-babel": "^6.0.4", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-node-resolve": "^15.3.0", - "c8": "^10.1.2", - "rollup": "^4.22.4", - "rollup-plugin-cleanup": "^3.2.1" - }, - "devDependencies": { - "@types/chai": "^5.0.0", - "@types/chai-spies": "^1.0.6", - "@types/chai-as-promised": "^8.0.1", - "@types/mocha": "^10.0.8", - "chai": "^5.1.1", - "chai-as-promised": "^8.0.0", - "chai-spies": "^1.1.0", - "esm": "^3.2.25", - "mocha": "^10.7.3" - } -} diff --git a/common/rollup.config.mjs b/common/rollup.config.mjs deleted file mode 100644 index 5c4d38a..0000000 --- a/common/rollup.config.mjs +++ /dev/null @@ -1,44 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { nodeResolve } from "@rollup/plugin-node-resolve" -import commonjs from "@rollup/plugin-commonjs" -import { babel } from "@rollup/plugin-babel" -import cleanup from "rollup-plugin-cleanup" - -const src = "./src/index.mjs" -const dest = "../build/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Common/index.mjs" - -export default { - input: src, - output: { - file: dest, - compact: false, - sourcemap: true, - format: "es" - }, - plugins: [ - nodeResolve({ browser: true }), - commonjs(), - cleanup({ comments: 'some' }), - babel({ - babelHelpers: "bundled" - }), - ] -} - diff --git a/common/src/events.mjs b/common/src/events.mjs deleted file mode 100644 index 1ef3777..0000000 --- a/common/src/events.mjs +++ /dev/null @@ -1,116 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -/** - * We do not inherit the DOM's Event, because not only the DOM part is unnecessary, - * but also because it does not exist within Qt environments. - */ - - -export class BaseEvent { - ___name = "" - - /** - * @property {string} name - Name of the event. - */ - constructor(name) { - this.___name = name - } - - get name() { - return this.___name - } -} - - -/** - * Base class for all classes which can emit events. - */ -export class BaseEventEmitter { - static emits = [] - - /** @type {Record>} */ - #listeners = {} - - constructor() { - for(const eventType of this.constructor.emits) { - this.#listeners[eventType] = new Set() - } - } - - /** - * Adds a listener to an event that can be emitted by this object. - * - * @param {string} eventType - Name of the event to listen to. Throws an error if this object does not emit this kind of event. - * @param {function(BaseEvent)} eventListener - The function to be called back when the event is emitted. - */ - on(eventType, eventListener) { - if(eventType.includes(" ")) // Listen to several different events with the same listener. - for(const type of eventType.split(" ")) - this.on(type, eventListener) - else { - if(!this.constructor.emits.includes(eventType)) { - const className = this.constructor.name - const eventTypes = this.constructor.emits.join(", ") - throw new Error(`Cannot listen to unknown event ${eventType} in class ${className}. ${className} only emits: ${eventTypes}`) - } - if(!this.#listeners[eventType].has(eventListener)) - this.#listeners[eventType].add(eventListener) - } - } - - /** - * Removes a listener from an event that can be emitted by this object. - * - * @param {string} eventType - Name of the event that was listened to. Throws an error if this object does not emit this kind of event. - * @param {function(BaseEvent)} eventListener - The function previously registered as a listener. - * @returns {boolean} True if the listener was removed, false if it was not found. - */ - off(eventType, eventListener) { - if(eventType.includes(" ")) { // Unlisten to several different events with the same listener. - let found = false - for(const type of eventType.split(" ")) - found ||= this.off(type, eventListener) - return found - } else { - if(!this.constructor.emits.includes(eventType)) { - const className = this.constructor.name - const eventTypes = this.constructor.emits.join(", ") - throw new Error(`Cannot listen to unknown event ${eventType} in class ${className}. ${className} only emits: ${eventTypes}`) - } - return this.#listeners[eventType].delete(eventListener) - } - } - - /** - * Emits an event to all of its listeners. - * - * @param {BaseEvent} e - */ - emit(e) { - if(!(e instanceof BaseEvent)) - throw new Error("Cannot emit non event object.") - if(!this.constructor.emits.includes(e.name)) { - const className = this.constructor.name - const eventTypes = this.constructor.emits.join(", ") - throw new Error(`Cannot emit event '${e.name}' from class ${className}. ${className} can only emit: ${eventTypes}`) - } - for(const listener of this.#listeners[e.name]) - listener(e) - } -} diff --git a/common/src/history/common.mjs b/common/src/history/common.mjs deleted file mode 100644 index 8c46757..0000000 --- a/common/src/history/common.mjs +++ /dev/null @@ -1,118 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import History from "../module/history.mjs" -import Latex from "../module/latex.mjs" - -export class Action { - /** - * Type of the action. - * - * @returns {string} - */ - type() { - return "Unknown" - } - - /** - * Icon associated with the action. - * - * @returns {string} - */ - icon() { - return "position" - } - - // TargetName is the name of the object that's targeted by the event. - constructor(targetName = "", targetType = "Point") { - this.targetName = targetName - this.targetType = targetType - } - - /** - * Undoes the action. - */ - undo() { - } - - /** - * Redoes the action. - */ - redo() { - } - - /** - * Export the action to a serializable format. - * NOTE: These arguments will be reinputed in the constructor in this order. - * - * @returns {string[]} - */ - export() { - return [this.targetName, this.targetType] - } - - /** - * Returns a string with the human-readable description of the action. - * - * @returns {string} - */ - getReadableString() { - return "Unknown action" - } - - /** - * Returns a string containing an HTML tag describing the icon of a type - * - * @param {string} type - Name of the icon to put in rich text. - * @returns {string} - */ - getIconRichText(type) { - return `` - } - - /** - * Renders a LaTeX-formatted string to an image and wraps it in an HTML tag in a string. - * - * @param {string} latexString - Source string of the latex. - * @returns {Promise} - */ - async renderLatexAsHtml(latexString) { - if(!Latex.enabled) - throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.") - const imgDepth = History.imageDepth - const renderArguments = [ - latexString, - imgDepth * (History.fontSize + 2), - History.themeTextColor - ] - let render = Latex.findPrerendered(...renderArguments) - if(render === null) - render = await Latex.requestAsyncRender(...renderArguments) - const { source, width, height } = render - return `` - } - - /** - * Returns a string with the HTML-formatted description of the action. - * - * @returns {string|Promise} - */ - getHTMLString() { - return this.getReadableString() - } -} diff --git a/common/src/history/editproperty.mjs b/common/src/history/editproperty.mjs deleted file mode 100644 index 2a81e7a..0000000 --- a/common/src/history/editproperty.mjs +++ /dev/null @@ -1,159 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import Objects from "../module/objects.mjs" -import Latex from "../module/latex.mjs" -import * as MathLib from "../math/index.mjs" -import { Action } from "./common.mjs" -import { DrawableObject } from "../objs/common.mjs" - -/** - * Action used everytime an object's property has been changed. - */ -export default class EditedProperty extends Action { - type() { - return "EditedProperty" - } - - icon() { - return "modify" - } - - color(darkVer = false) { - return darkVer ? "darkslateblue" : "cyan" - } - - /** - * - * @param {string} targetName - Name of the object to target - * @param {string} targetType - Type of the object to target. - * @param {string} targetProperty - Property being changed - * @param {any} previousValue - Previous value before change - * @param {any} newValue - New value after change - * @param {boolean} valueIsExpressionNeedingImport - True if the value needs to be imported. (e.g expressions) - */ - constructor(targetName = "", targetType = "Point", targetProperty = "visible", previousValue = false, newValue = true, valueIsExpressionNeedingImport = false) { - super(targetName, targetType) - this.targetProperty = targetProperty - this.targetPropertyReadable = qsTranslate("prop", this.targetProperty) - this.previousValue = previousValue - this.newValue = newValue - this.propertyType = Objects.types[targetType].properties()[targetProperty] - if(valueIsExpressionNeedingImport) { - if(typeof this.propertyType == "object" && this.propertyType.type === "Expression") { - this.previousValue = new MathLib.Expression(this.previousValue) - this.newValue = new MathLib.Expression(this.newValue) - } else if(this.propertyType === "Domain") { - this.previousValue = MathLib.parseDomain(this.previousValue) - this.newValue = MathLib.parseDomain(this.newValue) - } else { - // Objects - this.previousValue = Objects.currentObjectsByName[this.previousValue] // Objects.getObjectByName(this.previousValue); - this.newValue = Objects.currentObjectsByName[this.newValue] // Objects.getObjectByName(this.newValue); - } - } - this.setReadableValues() - } - - undo() { - Objects.currentObjectsByName[this.targetName][this.targetProperty] = this.previousValue - Objects.currentObjectsByName[this.targetName].update() - } - - redo() { - Objects.currentObjectsByName[this.targetName][this.targetProperty] = this.newValue - Objects.currentObjectsByName[this.targetName].update() - } - - export() { - if(this.previousValue instanceof MathLib.Expression) { - return [this.targetName, this.targetType, this.targetProperty, this.previousValue.toEditableString(), this.newValue.toEditableString(), true] - } else if(this.previousValue instanceof DrawableObject) { - return [this.targetName, this.targetType, this.targetProperty, this.previousValue.name, this.newValue.name, true] - } else { - return [this.targetName, this.targetType, this.targetProperty, this.previousValue, this.newValue, false] - } - } - - setReadableValues() { - this.prevString = "" - this.nextString = "" - this._renderPromises = [] - if(this.propertyType instanceof Object) { - switch(this.propertyType.type) { - case "Enum": - this.prevString = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.previousValue)] - this.nextString = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.newValue)] - break - case "ObjectType": - this.prevString = this.previousValue == null ? "null" : this.previousValue.name - this.nextString = this.newValue == null ? "null" : this.newValue.name - break - case "List": - this.prevString = this.previousValue.join(",") - this.nextString = this.newValue.name.join(",") - break - case "Dict": - this.prevString = JSON.stringify(this.previousValue) - this.nextString = JSON.stringify(this.newValue) - break - case "Expression": - this.prevString = this.previousValue == null ? "null" : this.previousValue.toString() - this.nextString = this.newValue == null ? "null" : this.newValue.toString() - break - } - } else { - this.prevString = this.previousValue == null ? "null" : this.previousValue.toString() - this.nextString = this.newValue == null ? "null" : this.newValue.toString() - } - // HTML - this.prevHTML = " " + this.prevString + " " - this.nextHTML = " " + this.nextString + " " - if(Latex.enabled && typeof this.propertyType == "object" && this.propertyType.type === "Expression") { - // Store promises so that querying can wait for them to finish. - this._renderPromises = [ - this.renderLatexAsHtml(this.previousValue.latexMarkup).then(prev => this.prevHTML = prev), - this.renderLatexAsHtml(this.newValue.latexMarkup).then(next => this.nextHTML = next) - ] - } - } - - getReadableString() { - return qsTranslate("editproperty", "%1 of %2 %3 changed from \"%4\" to \"%5\".") - .arg(this.targetPropertyReadable) - .arg(Objects.types[this.targetType].displayType()) - .arg(this.targetName).arg(this.prevString).arg(this.nextString) - } - - /** - * - * @return {Promise|string} - */ - async getHTMLString() { - const translation = qsTranslate("editproperty", "%1 of %2 changed from %3 to %4.") - .arg(this.targetPropertyReadable) - .arg(" " + this.targetName + " ") - // Check if we need to wait for LaTeX HTML to be rendered. - if(this.prevHTML === undefined || this.nextHTML === undefined) { - const [prevHTML, nextHTML] = await Promise.all(this._renderPromises) - this.prevHTML = this.prevHTML ?? prevHTML - this.nextHTML = this.nextHTML ?? nextHTML - } - return translation.arg(this.prevHTML).arg(this.nextHTML) - } -} diff --git a/common/src/history/position.mjs b/common/src/history/position.mjs deleted file mode 100644 index 2b9c0ff..0000000 --- a/common/src/history/position.mjs +++ /dev/null @@ -1,109 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import Objects from "../module/objects.mjs" -import Latex from "../module/latex.mjs" -import * as MathLib from "../math/index.mjs" -import { escapeHTML } from "../utils/index.mjs" -import { Action } from "./common.mjs" - -/** - * Action used for objects that have a X and Y expression properties (points, texts...) - */ -export default class EditedPosition extends Action { - type() { - return "EditedPosition" - } - - icon() { - return "position" - } - - color(darkVer = false) { - return darkVer ? "seagreen" : "lightseagreen" - } - - constructor(targetName = "", targetType = "Point", previousXValue = "", newXValue = "", previousYValue = "", newYValue = "") { - super(targetName, targetType) - let imports = { - "previousXValue": previousXValue, - "previousYValue": previousYValue, - "newXValue": newXValue, - "newYValue": newYValue - } - for(let name in imports) - this[name] = (typeof imports[name]) == "string" ? new MathLib.Expression(imports[name]) : imports[name] - this.setReadableValues() - } - - undo() { - Objects.currentObjectsByName[this.targetName].x = this.previousXValue - Objects.currentObjectsByName[this.targetName].y = this.previousYValue - Objects.currentObjectsByName[this.targetName].update() - } - - redo() { - Objects.currentObjectsByName[this.targetName].x = this.newXValue - Objects.currentObjectsByName[this.targetName].y = this.newYValue - Objects.currentObjectsByName[this.targetName].update() - } - - setReadableValues() { - this.prevString = `(${this.previousXValue.toString()},${this.previousYValue.toString()})` - this.nextString = `(${this.newXValue.toString()},${this.newYValue.toString()})` - this._renderPromises = [] - // Render as LaTeX - if(Latex.enabled) { - const prevMarkup = `\\left(${this.previousXValue.latexMarkup},${this.previousYValue.latexMarkup}\\right)` - const nextMarkup = `\\left(${this.newXValue.latexMarkup},${this.newYValue.latexMarkup}\\right)` - this._renderPromises = [ // Will be taken in promise.all - this.renderLatexAsHtml(prevMarkup), - this.renderLatexAsHtml(nextMarkup) - ] - } else { - this.prevHTML = " " + escapeHTML(this.prevString) + " " - this.nextHTML = " " + escapeHTML(this.nextString) + " " - } - - } - - export() { - return [this.targetName, this.targetType, - this.previousXValue.toEditableString(), this.newXValue.toEditableString(), - this.previousYValue.toEditableString(), this.newYValue.toEditableString()] - } - - getReadableString() { - return qsTranslate("position", "Position of %1 %2 set from \"%3\" to \"%4\".") - .arg(Objects.types[this.targetType].displayType()) - .arg(this.targetName).arg(this.prevString).arg(this.nextString) - } - - async getHTMLString() { - const translation = qsTranslate("position", "Position of %1 set from %2 to %3.") - .arg(" " + this.targetName + " ") - // Check if we need to wait for LaTeX HTML to be rendered. - if(this.prevHTML === undefined || this.nextHTML === undefined) { - const [prevHTML, nextHTML] = await Promise.all(this._renderPromises) - this.prevHTML = this.prevHTML ?? prevHTML - this.nextHTML = this.nextHTML ?? nextHTML - } - return translation.arg(this.prevHTML).arg(this.nextHTML) - - } -} diff --git a/common/src/lib/expr-eval/expression.mjs b/common/src/lib/expr-eval/expression.mjs deleted file mode 100644 index 45854e8..0000000 --- a/common/src/lib/expr-eval/expression.mjs +++ /dev/null @@ -1,524 +0,0 @@ -/** - * Based on ndef.parser, by Raphael Graf - * http://www.undefined.ch/mparser/index.html - * - * Ported to JavaScript and modified by Matthew Crumley - * https://silentmatt.com/javascript-expression-evaluator/ - * - * Ported to QMLJS with modifications done accordingly done by Ad5001 (https://ad5001.eu) - * - * Copyright (c) 2015 Matthew Crumley, 2021-2025 Ad5001 - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * You are free to use and modify this code in anyway you find useful. Please leave this comment in the code - * to acknowledge its original source. If you feel like it, I enjoy hearing about projects that use my code, - * but don't feel like you have to let me know or ask permission. - */ - -import { - Instruction, - IOP3, IOP2, IOP1, - INUMBER, IARRAY, - IVAR, IVARNAME, - IEXPR, IEXPREVAL, - IMEMBER, IFUNCALL, - IENDSTATEMENT, - unaryInstruction, binaryInstruction, ternaryInstruction -} from "./instruction.mjs" - -/** - * Simplifies the given instructions - * @param {Instruction[]} tokens - * @param {Record.} unaryOps - * @param {Record.} binaryOps - * @param {Record.} ternaryOps - * @param {Record.} values - * @return {Instruction[]} - */ -function simplify(tokens, unaryOps, binaryOps, ternaryOps, values) { - const nstack = [] - const newexpression = [] - let n1, n2, n3 - let f - for(let i = 0; i < tokens.length; i++) { - let item = tokens[i] - const type = item.type - if(type === INUMBER || type === IVARNAME) { - if(Array.isArray(item.value)) { - nstack.push.apply(nstack, simplify(item.value.map(function(x) { - return new Instruction(INUMBER, x) - }).concat(new Instruction(IARRAY, item.value.length)), unaryOps, binaryOps, ternaryOps, values)) - } else { - nstack.push(item) - } - } else if(type === IVAR && values.hasOwnProperty(item.value)) { - item = new Instruction(INUMBER, values[item.value]) - nstack.push(item) - } else if(type === IOP2 && nstack.length > 1) { - n2 = nstack.pop() - n1 = nstack.pop() - f = binaryOps[item.value] - item = new Instruction(INUMBER, f(n1.value, n2.value)) - nstack.push(item) - } else if(type === IOP3 && nstack.length > 2) { - n3 = nstack.pop() - n2 = nstack.pop() - n1 = nstack.pop() - if(item.value === "?") { - nstack.push(n1.value ? n2.value : n3.value) - } else { - f = ternaryOps[item.value] - item = new Instruction(INUMBER, f(n1.value, n2.value, n3.value)) - nstack.push(item) - } - } else if(type === IOP1 && nstack.length > 0) { - n1 = nstack.pop() - f = unaryOps[item.value] - item = new Instruction(INUMBER, f(n1.value)) - nstack.push(item) - } else if(type === IEXPR) { - while(nstack.length > 0) { - newexpression.push(nstack.shift()) - } - newexpression.push(new Instruction(IEXPR, simplify(item.value, unaryOps, binaryOps, ternaryOps, values))) - } else if(type === IMEMBER && nstack.length > 0) { - n1 = nstack.pop() - if(item.value in n1.value) - nstack.push(new Instruction(INUMBER, n1.value[item.value])) - else - throw new Error(qsTranslate("error", "Cannot find property %1 of object %2.").arg(item.value).arg(n1)) - } else { - while(nstack.length > 0) { - newexpression.push(nstack.shift()) - } - newexpression.push(item) - } - } - while(nstack.length > 0) { - newexpression.push(nstack.shift()) - } - return newexpression -} - -/** - * In the given instructions, replaces variable by expr. - * @param {Instruction[]} tokens - * @param {string} variable - * @param {ExprEvalExpression} expr - * @return {Instruction[]} - */ -function substitute(tokens, variable, expr) { - const newexpression = [] - for(let i = 0; i < tokens.length; i++) { - let item = tokens[i] - const type = item.type - if(type === IVAR && item.value === variable) { - for(let j = 0; j < expr.tokens.length; j++) { - const expritem = expr.tokens[j] - let replitem - if(expritem.type === IOP1) { - replitem = unaryInstruction(expritem.value) - } else if(expritem.type === IOP2) { - replitem = binaryInstruction(expritem.value) - } else if(expritem.type === IOP3) { - replitem = ternaryInstruction(expritem.value) - } else { - replitem = new Instruction(expritem.type, expritem.value) - } - newexpression.push(replitem) - } - } else if(type === IEXPR) { - newexpression.push(new Instruction(IEXPR, substitute(item.value, variable, expr))) - } else { - newexpression.push(item) - } - } - return newexpression -} - -/** - * Evaluates the given instructions for a given Expression with given values. - * @param {Instruction[]} tokens - * @param {ExprEvalExpression} expr - * @param {Record.} values - * @return {number} - */ -function evaluate(tokens, expr, values) { - const nstack = [] - let n1, n2, n3 - let f, args, argCount - - if(isExpressionEvaluator(tokens)) { - return resolveExpression(tokens, values) - } - - for(let i = 0; i < tokens.length; i++) { - const item = tokens[i] - const type = item.type - if(type === INUMBER || type === IVARNAME) { - nstack.push(item.value) - } else if(type === IOP2) { - n2 = nstack.pop() - n1 = nstack.pop() - if(item.value === "and") { - nstack.push(n1 ? !!evaluate(n2, expr, values) : false) - } else if(item.value === "or") { - nstack.push(n1 ? true : !!evaluate(n2, expr, values)) - } else { - f = expr.binaryOps[item.value] - nstack.push(f(resolveExpression(n1, values), resolveExpression(n2, values))) - } - } else if(type === IOP3) { - n3 = nstack.pop() - n2 = nstack.pop() - n1 = nstack.pop() - if(item.value === "?") { - nstack.push(evaluate(n1 ? n2 : n3, expr, values)) - } else { - f = expr.ternaryOps[item.value] - nstack.push(f(resolveExpression(n1, values), resolveExpression(n2, values), resolveExpression(n3, values))) - } - } else if(type === IVAR) { - // Check for variable value - if(/^__proto__|prototype|constructor$/.test(item.value)) { - throw new Error("WARNING: Prototype access detected and denied. If you downloaded this file from the internet, this file might be a virus.") - } else if(item.value in expr.functions) { - nstack.push(expr.functions[item.value]) - } else if(item.value in expr.unaryOps && expr.parser.isOperatorEnabled(item.value)) { - nstack.push(expr.unaryOps[item.value]) - } else { - const v = values[item.value] - if(v !== undefined) { - nstack.push(v) - } else { - throw new Error(qsTranslate("error", "Undefined variable %1.").arg(item.value)) - } - } - } else if(type === IOP1) { - n1 = nstack.pop() - f = expr.unaryOps[item.value] - nstack.push(f(resolveExpression(n1, values))) - } else if(type === IFUNCALL) { - argCount = item.value - args = [] - while(argCount-- > 0) { - args.unshift(resolveExpression(nstack.pop(), values)) - } - f = nstack.pop() - if(f.apply && f.call) { - nstack.push(f.apply(undefined, args)) - } else if(f.execute) { - // Objects & expressions execution - if(args.length >= 1) - nstack.push(f.execute.apply(f, args)) - else - throw new Error(qsTranslate("error", "In order to be executed, object %1 must have at least one argument.").arg(f)) - } else { - throw new Error(qsTranslate("error", "%1 cannot be executed.").arg(f)) - } - } else if(type === IEXPR) { - nstack.push(createExpressionEvaluator(item, expr)) - } else if(type === IEXPREVAL) { - nstack.push(item) - } else if(type === IMEMBER) { - n1 = nstack.pop() - if(item.value in n1) - if(n1[item.value].execute && n1[item.value].cached) - nstack.push(n1[item.value].execute()) - else - nstack.push(n1[item.value]) - else - throw new Error(qsTranslate("error", "Cannot find property %1 of object %2.").arg(item.value).arg(n1)) - } else if(type === IENDSTATEMENT) { - nstack.pop() - } else if(type === IARRAY) { - argCount = item.value - args = [] - while(argCount-- > 0) { - args.unshift(nstack.pop()) - } - nstack.push(args) - } else { - throw new Error(qsTranslate("error", "Invalid expression.")) - } - } - if(nstack.length > 1) { - throw new Error(qsTranslate("error", "Invalid expression (parity).")) - } - // Explicitly return zero to avoid test issues caused by -0 - return nstack[0] === 0 ? 0 : resolveExpression(nstack[0], values) -} - -function createExpressionEvaluator(token, expr) { - if(isExpressionEvaluator(token)) return token - return { - type: IEXPREVAL, - value: function(scope) { - return evaluate(token.value, expr, scope) - } - } -} - -function isExpressionEvaluator(n) { - return n && n.type === IEXPREVAL -} - -function resolveExpression(n, values) { - return isExpressionEvaluator(n) ? n.value(values) : n -} - -/** - * Converts the given instructions to a string - * If toJS is active, can be evaluated with eval, otherwise it can be reparsed by the parser. - * @param {Instruction[]} tokens - * @param {boolean} toJS - * @return {string} - */ -function expressionToString(tokens, toJS) { - let nstack = [] - let n1, n2, n3 - let f, args, argCount - for(let i = 0; i < tokens.length; i++) { - const item = tokens[i] - const type = item.type - if(type === INUMBER) { - if(typeof item.value === "number" && item.value < 0) { - nstack.push("(" + item.value + ")") - } else if(Array.isArray(item.value)) { - nstack.push("[" + item.value.map(escapeValue).join(", ") + "]") - } else { - nstack.push(escapeValue(item.value)) - } - } else if(type === IOP2) { - n2 = nstack.pop() - n1 = nstack.pop() - f = item.value - if(toJS) { - if(f === "^") { - nstack.push("Math.pow(" + n1 + ", " + n2 + ")") - } else if(f === "and") { - nstack.push("(!!" + n1 + " && !!" + n2 + ")") - } else if(f === "or") { - nstack.push("(!!" + n1 + " || !!" + n2 + ")") - } else if(f === "||") { - nstack.push("(function(a,b){ return Array.isArray(a) && Array.isArray(b) ? a.concat(b) : String(a) + String(b); }((" + n1 + "),(" + n2 + ")))") - } else if(f === "==") { - nstack.push("(" + n1 + " === " + n2 + ")") - } else if(f === "!=") { - nstack.push("(" + n1 + " !== " + n2 + ")") - } else if(f === "[") { - nstack.push(n1 + "[(" + n2 + ") | 0]") - } else { - nstack.push("(" + n1 + " " + f + " " + n2 + ")") - } - } else { - if(f === "[") { - nstack.push(n1 + "[" + n2 + "]") - } else { - nstack.push("(" + n1 + " " + f + " " + n2 + ")") - } - } - } else if(type === IOP3) { - n3 = nstack.pop() - n2 = nstack.pop() - n1 = nstack.pop() - f = item.value - if(f === "?") { - nstack.push("(" + n1 + " ? " + n2 + " : " + n3 + ")") - } else { - throw new Error(qsTranslate("error", "Invalid expression.")) - } - } else if(type === IVAR || type === IVARNAME) { - nstack.push(item.value) - } else if(type === IOP1) { - n1 = nstack.pop() - f = item.value - if(f === "-" || f === "+") { - nstack.push("(" + f + n1 + ")") - } else if(toJS) { - if(f === "not") { - nstack.push("(" + "!" + n1 + ")") - } else if(f === "!") { - nstack.push("fac(" + n1 + ")") - } else { - nstack.push(f + "(" + n1 + ")") - } - } else if(f === "!") { - nstack.push("(" + n1 + "!)") - } else { - nstack.push("(" + f + " " + n1 + ")") - } - } else if(type === IFUNCALL) { - argCount = item.value - args = [] - while(argCount-- > 0) { - args.unshift(nstack.pop()) - } - f = nstack.pop() - nstack.push(f + "(" + args.join(", ") + ")") - } else if(type === IMEMBER) { - n1 = nstack.pop() - nstack.push(n1 + "." + item.value) - } else if(type === IARRAY) { - argCount = item.value - args = [] - while(argCount-- > 0) { - args.unshift(nstack.pop()) - } - nstack.push("[" + args.join(", ") + "]") - } else if(type === IEXPR) { - nstack.push("(" + expressionToString(item.value, toJS) + ")") - } else if(type === IENDSTATEMENT) { - - } else { - throw new Error(qsTranslate("error", "Invalid expression.")) - } - } - if(nstack.length > 1) { - if(toJS) { - nstack = [nstack.join(",")] - } else { - nstack = [nstack.join(";")] - } - } - return String(nstack[0]) -} - -export function escapeValue(v) { - if(typeof v === "string") { - return JSON.stringify(v).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029") - } - return v -} - -/** - * Pushes all symbols from tokens into the symbols array. - * @param {Instruction[]} tokens - * @param {string[]} symbols - * @param {{withMembers: (boolean|undefined)}}options - */ -function getSymbols(tokens, symbols, options) { - options = options || {} - const withMembers = !!options.withMembers - let prevVar = null - - for(let i = 0; i < tokens.length; i++) { - const item = tokens[i] - if(item.type === IVAR || item.type === IVARNAME) { - if(!withMembers && !symbols.includes(item.value)) { - symbols.push(item.value) - } else if(prevVar !== null) { - if(!symbols.includes(prevVar)) { - symbols.push(prevVar) - } - prevVar = item.value - } else { - prevVar = item.value - } - } else if(item.type === IMEMBER && withMembers && prevVar !== null) { - prevVar += "." + item.value - } else if(item.type === IEXPR) { - getSymbols(item.value, symbols, options) - } else if(prevVar !== null) { - if(!symbols.includes(prevVar)) { - symbols.push(prevVar) - } - prevVar = null - } - } - - if(prevVar !== null && !symbols.includes(prevVar)) { - symbols.push(prevVar) - } -} - -export class ExprEvalExpression { - /** - * @param {Instruction[]} tokens - * @param {Parser} parser - */ - constructor(tokens, parser) { - this.tokens = tokens - this.parser = parser - this.unaryOps = parser.unaryOps - this.binaryOps = parser.binaryOps - this.ternaryOps = parser.ternaryOps - this.functions = parser.functions - } - - /** - * Simplifies the expression. - * @param {Object|undefined} values - * @returns {ExprEvalExpression} - */ - simplify(values) { - values = values || {} - return new ExprEvalExpression(simplify(this.tokens, this.unaryOps, this.binaryOps, this.ternaryOps, values), this.parser) - } - - /** - * Creates a new expression where the variable is substituted by the given expression. - * @param {string} variable - * @param {string|ExprEvalExpression} expr - * @returns {ExprEvalExpression} - */ - substitute(variable, expr) { - if(!(expr instanceof ExprEvalExpression)) { - expr = this.parser.parse(String(expr)) - } - - return new ExprEvalExpression(substitute(this.tokens, variable, expr), this.parser) - } - - /** - * Calculates the value of the expression by giving all variables and their corresponding values. - * @param {Object} values - * @returns {number} - */ - evaluate(values) { - values = Object.assign({}, values, this.parser.consts) - return evaluate(this.tokens, this, values) - } - - toString() { - return expressionToString(this.tokens, false) - } - - - /** - * Returns the list of symbols (string of characters) which are not defined - * as constants or functions. - * @returns {string[]} - */ - variables(options = {}) { - const vars = [] - getSymbols(this.tokens, vars, options) - const functions = this.functions - const consts = this.parser.consts - return vars.filter((name) => { - return !(name in functions) && !(name in consts) - }) - } - - - /** - * Converts the expression to a JS function. - * @param {string} param - Parsed variables for the function. - * @param {Object.} variables - Default variables to provide. - * @returns {function(...any)} - */ - toJSFunction(param, variables) { - const expr = this - const f = new Function(param, "with(this.functions) with (this.ternaryOps) with (this.binaryOps) with (this.unaryOps) { return " + expressionToString(this.simplify(variables).tokens, true) + "; }") // eslint-disable-line no-new-func - return function() { - return f.apply(expr, arguments) - } - } -} \ No newline at end of file diff --git a/common/src/lib/expr-eval/instruction.mjs b/common/src/lib/expr-eval/instruction.mjs deleted file mode 100644 index 9157fea..0000000 --- a/common/src/lib/expr-eval/instruction.mjs +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Based on ndef.parser, by Raphael Graf - * http://www.undefined.ch/mparser/index.html - * - * Ported to JavaScript and modified by Matthew Crumley - * https://silentmatt.com/javascript-expression-evaluator/ - * - * Ported to QMLJS with modifications done accordingly done by Ad5001 (https://ad5001.eu) - * - * Copyright (c) 2015 Matthew Crumley, 2021-2025 Ad5001 - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * You are free to use and modify this code in anyway you find useful. Please leave this comment in the code - * to acknowledge its original source. If you feel like it, I enjoy hearing about projects that use my code, - * but don't feel like you have to let me know or ask permission. - */ - -export const INUMBER = "INUMBER" -export const IOP1 = "IOP1" -export const IOP2 = "IOP2" -export const IOP3 = "IOP3" -export const IVAR = "IVAR" -export const IVARNAME = "IVARNAME" -export const IFUNCALL = "IFUNCALL" -export const IEXPR = "IEXPR" -export const IEXPREVAL = "IEXPREVAL" -export const IMEMBER = "IMEMBER" -export const IENDSTATEMENT = "IENDSTATEMENT" -export const IARRAY = "IARRAY" - - -export class Instruction { - /** - * - * @param {string} type - * @param {any} value - */ - constructor(type, value) { - this.type = type - this.value = (value !== undefined && value !== null) ? value : 0 - } - - toString() { - switch(this.type) { - case INUMBER: - case IOP1: - case IOP2: - case IOP3: - case IVAR: - case IVARNAME: - case IENDSTATEMENT: - return this.value - case IFUNCALL: - return "CALL " + this.value - case IARRAY: - return "ARRAY " + this.value - case IMEMBER: - return "." + this.value - default: - return "Invalid Instruction" - } - } -} - -export function unaryInstruction(value) { - return new Instruction(IOP1, value) -} - -export function binaryInstruction(value) { - return new Instruction(IOP2, value) -} - -export function ternaryInstruction(value) { - return new Instruction(IOP3, value) -} \ No newline at end of file diff --git a/common/src/lib/expr-eval/parser.mjs b/common/src/lib/expr-eval/parser.mjs deleted file mode 100644 index 98f4539..0000000 --- a/common/src/lib/expr-eval/parser.mjs +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Based on ndef.parser, by Raphael Graf - * http://www.undefined.ch/mparser/index.html - * - * Ported to JavaScript and modified by Matthew Crumley - * https://silentmatt.com/javascript-expression-evaluator/ - * - * Ported to QMLJS with modifications done accordingly done by Ad5001 (https://ad5001.eu) - * - * Copyright (c) 2015 Matthew Crumley, 2021-2025 Ad5001 - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * You are free to use and modify this code in anyway you find useful. Please leave this comment in the code - * to acknowledge its original source. If you feel like it, I enjoy hearing about projects that use my code, - * but don't feel like you have to let me know or ask permission. - */ - -import * as Polyfill from "./polyfill.mjs" -import { ParserState } from "./parserstate.mjs" -import { TEOF, TokenStream } from "./tokens.mjs" -import { ExprEvalExpression } from "./expression.mjs" - -const optionNameMap = { - "+": "add", - "-": "subtract", - "*": "multiply", - "/": "divide", - "%": "remainder", - "^": "power", - "!": "factorial", - "<": "comparison", - ">": "comparison", - "<=": "comparison", - ">=": "comparison", - "==": "comparison", - "!=": "comparison", - "||": "concatenate", - "and": "logical", - "or": "logical", - "not": "logical", - "?": "conditional", - ":": "conditional", - "[": "array" -} - -export class Parser { - constructor(options) { - this.options = options || {} - this.unaryOps = { - sin: Math.sin, - cos: Math.cos, - tan: Math.tan, - asin: Math.asin, - acos: Math.acos, - atan: Math.atan, - sinh: Math.sinh || Polyfill.sinh, - cosh: Math.cosh || Polyfill.cosh, - tanh: Math.tanh || Polyfill.tanh, - asinh: Math.asinh || Polyfill.asinh, - acosh: Math.acosh || Polyfill.acosh, - atanh: Math.atanh || Polyfill.atanh, - sqrt: Math.sqrt, - cbrt: Math.cbrt || Polyfill.cbrt, - log: Math.log, - log2: Math.log2 || Polyfill.log2, - ln: Math.log, - lg: Math.log10 || Polyfill.log10, - log10: Math.log10 || Polyfill.log10, - expm1: Math.expm1 || Polyfill.expm1, - log1p: Math.log1p || Polyfill.log1p, - abs: Math.abs, - ceil: Math.ceil, - floor: Math.floor, - round: Math.round, - trunc: Math.trunc || Polyfill.trunc, - "-": Polyfill.neg, - "+": Number, - exp: Math.exp, - not: Polyfill.not, - length: Polyfill.stringOrArrayLength, - "!": Polyfill.factorial, - sign: Math.sign || Polyfill.sign - } - this.unaryOpsList = Object.keys(this.unaryOps) - - this.binaryOps = { - "+": Polyfill.add, - "-": Polyfill.sub, - "*": Polyfill.mul, - "/": Polyfill.div, - "%": Polyfill.mod, - "^": Math.pow, - "||": Polyfill.concat, - "==": Polyfill.equal, - "!=": Polyfill.notEqual, - ">": Polyfill.greaterThan, - "<": Polyfill.lessThan, - ">=": Polyfill.greaterThanEqual, - "<=": Polyfill.lessThanEqual, - and: Polyfill.andOperator, - or: Polyfill.orOperator, - "in": Polyfill.inOperator, - "[": Polyfill.arrayIndex - } - - this.ternaryOps = { - "?": Polyfill.condition - } - - this.functions = { - random: Polyfill.random, - fac: Polyfill.factorial, - min: Polyfill.min, - max: Polyfill.max, - hypot: Math.hypot || Polyfill.hypot, - pyt: Math.hypot || Polyfill.hypot, - pow: Math.pow, - atan2: Math.atan2, - "if": Polyfill.condition, - gamma: Polyfill.gamma, - "Γ": Polyfill.gamma, - roundTo: Polyfill.roundTo, - } - - // These constants will automatically be replaced the MOMENT they are parsed. - // (Original consts from the parser) - this.builtinConsts = {} - // These consts will only be replaced when the expression is evaluated. - this.consts = {} - - } - - parse(expr) { - const instr = [] - const parserState = new ParserState( - this, - new TokenStream(this, expr), - { allowMemberAccess: this.options.allowMemberAccess } - ) - - parserState.parseExpression(instr) - parserState.expect(TEOF, QT_TRANSLATE_NOOP("error", "EOF")) - - return new ExprEvalExpression(instr, this) - } - - isOperatorEnabled(op) { - const optionName = optionNameMap.hasOwnProperty(op) ? optionNameMap[op] : op - const operators = this.options.operators || {} - - return !(optionName in operators) || !!operators[optionName] - } -} \ No newline at end of file diff --git a/common/src/lib/expr-eval/parserstate.mjs b/common/src/lib/expr-eval/parserstate.mjs deleted file mode 100644 index d4b3a3b..0000000 --- a/common/src/lib/expr-eval/parserstate.mjs +++ /dev/null @@ -1,398 +0,0 @@ -/** - * Based on ndef.parser, by Raphael Graf - * http://www.undefined.ch/mparser/index.html - * - * Ported to JavaScript and modified by Matthew Crumley - * https://silentmatt.com/javascript-expression-evaluator/ - * - * Ported to QMLJS with modifications done accordingly done by Ad5001 (https://ad5001.eu) - * - * Copyright (c) 2015 Matthew Crumley, 2021-2025 Ad5001 - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * You are free to use and modify this code in anyway you find useful. Please leave this comment in the code - * to acknowledge its original source. If you feel like it, I enjoy hearing about projects that use my code, - * but don't feel like you have to let me know or ask permission. - */ - -import { TBRACKET, TCOMMA, TEOF, TNAME, TNUMBER, TOP, TPAREN, TSTRING } from "./tokens.mjs" -import { - Instruction, - IARRAY, IEXPR, IFUNCALL, IMEMBER, - INUMBER, IVAR, - ternaryInstruction, binaryInstruction, unaryInstruction -} from "./instruction.mjs" - -const COMPARISON_OPERATORS = ["==", "!=", "<", "<=", ">=", ">", "in"] -const ADD_SUB_OPERATORS = ["+", "-", "||"] -const TERM_OPERATORS = ["*", "/", "%"] - -export class ParserState { - /** - * - * @param {Parser} parser - * @param {TokenStream} tokenStream - * @param {{[operators]: Object., [allowMemberAccess]: boolean}} options - */ - constructor(parser, tokenStream, options) { - this.parser = parser - this.tokens = tokenStream - this.current = null - this.nextToken = null - this.next() - this.savedCurrent = null - this.savedNextToken = null - this.allowMemberAccess = options.allowMemberAccess !== false - } - - /** - * Queries the next token for parsing. - * @return {Token} - */ - next() { - this.current = this.nextToken - this.nextToken = this.tokens.next() - return this.nextToken - } - - /** - * Checks if a given Token matches a condition (called if function, one of if array, and exact match otherwise) - * @param {Token} token - * @param {Array|function(Token): boolean|string|number|boolean} [value] - * @return {boolean} - */ - tokenMatches(token, value) { - if(typeof value === "undefined") { - return true - } else if(Array.isArray(value)) { - return value.includes(token.value) - } else if(typeof value === "function") { - return value(token) - } else { - return token.value === value - } - } - - /** - * Saves the current state (current and next token) to be restored later. - */ - save() { - this.savedCurrent = this.current - this.savedNextToken = this.nextToken - this.tokens.save() - } - - /** - * Restores a previous state (current and next token) from last save. - */ - restore() { - this.tokens.restore() - this.current = this.savedCurrent - this.nextToken = this.savedNextToken - } - - /** - * Checks if the next token matches the given type and value, and if so, consume the current token. - * Returns true if the check matches. - * @param {string} type - * @param {any} [value] - * @return {boolean} - */ - accept(type, value) { - if(this.nextToken.type === type && this.tokenMatches(this.nextToken, value)) { - this.next() - return true - } - return false - } - - /** - * Throws an error if the next token does not match the given type and value. Otherwise, consumes the current token. - * @param {string} type - * @param {any} [value] - */ - expect(type, value) { - if(!this.accept(type, value)) { - throw new Error(qsTranslate("error", "Parse error [position %1]: %2") - .arg(this.tokens.pos) - .arg(qsTranslate("error", "Expected %1").arg(value || type))) - } - } - - /** - * Converts enough Tokens to form an expression atom (generally the next part of the expression) into an instruction - * and pushes it to the instruction list. - * Throws an error if an unexpected token gets parsed. - * @param {Instruction[]} instr - */ - parseAtom(instr) { - const prefixOperators = this.tokens.unaryOpsList - - if(this.accept(TNAME) || this.accept(TOP, prefixOperators)) { - instr.push(new Instruction(IVAR, this.current.value)) - } else if(this.accept(TNUMBER)) { - instr.push(new Instruction(INUMBER, this.current.value)) - } else if(this.accept(TSTRING)) { - instr.push(new Instruction(INUMBER, this.current.value)) - } else if(this.accept(TPAREN, "(")) { - this.parseExpression(instr) - this.expect(TPAREN, ")") - } else if(this.accept(TBRACKET, "[")) { - if(this.accept(TBRACKET, "]")) { - instr.push(new Instruction(IARRAY, 0)) - } else { - const argCount = this.parseArrayList(instr) - instr.push(new Instruction(IARRAY, argCount)) - } - } else { - throw new Error(qsTranslate("error", "Unexpected %1").arg(this.nextToken)) - } - } - - /** - * Consumes the next tokens to compile a general expression which should return a value, and compiles - * the instructions into the list. - * @param {Instruction[]} instr - */ - parseExpression(instr) { - const exprInstr = [] - this.parseConditionalExpression(exprInstr) - instr.push(...exprInstr) - } - - /** - * Parses an array indice, and return the number of arguments found at the end. - * @param {Instruction[]} instr - * @return {number} - */ - parseArrayList(instr) { - let argCount = 0 - - while(!this.accept(TBRACKET, "]")) { - this.parseExpression(instr) - ++argCount - while(this.accept(TCOMMA)) { - this.parseExpression(instr) - ++argCount - } - } - - return argCount - } - - /** - * Parses a tertiary statement ( ? : ) and pushes it into the instruction - * list. - * @param {Instruction[]} instr - */ - parseConditionalExpression(instr) { - this.parseOrExpression(instr) - while(this.accept(TOP, "?")) { - const trueBranch = [] - const falseBranch = [] - this.parseConditionalExpression(trueBranch) - this.expect(TOP, ":") - this.parseConditionalExpression(falseBranch) - instr.push(new Instruction(IEXPR, trueBranch)) - instr.push(new Instruction(IEXPR, falseBranch)) - instr.push(ternaryInstruction("?")) - } - } - - /** - * Parses a binary or statement ( or ) and pushes it into the instruction list. - * @param {Instruction[]} instr - */ - parseOrExpression(instr) { - this.parseAndExpression(instr) - while(this.accept(TOP, "or")) { - const falseBranch = [] - this.parseAndExpression(falseBranch) - instr.push(new Instruction(IEXPR, falseBranch)) - instr.push(binaryInstruction("or")) - } - } - - /** - * Parses a binary and statement ( and ) and pushes it into the instruction list. - * @param {Instruction[]} instr - */ - parseAndExpression(instr) { - this.parseComparison(instr) - while(this.accept(TOP, "and")) { - const trueBranch = [] - this.parseComparison(trueBranch) - instr.push(new Instruction(IEXPR, trueBranch)) - instr.push(binaryInstruction("and")) - } - } - - /** - * Parses a binary equality statement ( == and so on) and pushes it into the instruction list. - * @param {Instruction[]} instr - */ - parseComparison(instr) { - this.parseAddSub(instr) - while(this.accept(TOP, COMPARISON_OPERATORS)) { - const op = this.current - this.parseAddSub(instr) - instr.push(binaryInstruction(op.value)) - } - } - - /** - * Parses add, minus and concat operations and pushes them into the instruction list. - * @param {Instruction[]} instr - */ - parseAddSub(instr) { - this.parseTerm(instr) - while(this.accept(TOP, ADD_SUB_OPERATORS)) { - const op = this.current - this.parseTerm(instr) - instr.push(binaryInstruction(op.value)) - } - } - - /** - * Parses times, divide and modulo operations and pushes them into the instruction list. - * @param {Instruction[]} instr - */ - parseTerm(instr) { - this.parseFactor(instr) - while(this.accept(TOP, TERM_OPERATORS)) { - const op = this.current - this.parseFactor(instr) - instr.push(binaryInstruction(op.value)) - } - } - - /** - * Parses prefix operations (+, -, but also functions like sin or cos which don't need parentheses) - * @param {Instruction[]} instr - */ - parseFactor(instr) { - const prefixOperators = this.tokens.unaryOpsList - - this.save() - if(this.accept(TOP, prefixOperators)) { - if(this.current.value !== "-" && this.current.value !== "+") { - if(this.nextToken.type === TPAREN && this.nextToken.value === "(") { - this.restore() - this.parseExponential(instr) - return - } else if(this.nextToken.type === TCOMMA || this.nextToken.type === TEOF || (this.nextToken.type === TPAREN && this.nextToken.value === ")")) { - this.restore() - this.parseAtom(instr) - return - } - } - - const op = this.current - this.parseFactor(instr) - instr.push(unaryInstruction(op.value)) - } else { - this.parseExponential(instr) - } - } - - /** - * - * @param {Instruction[]} instr - */ - parseExponential(instr) { - this.parsePostfixExpression(instr) - while(this.accept(TOP, "^")) { - this.parseFactor(instr) - instr.push(binaryInstruction("^")) - } - } - - - /** - * Parses factorial '!' (after the expression to apply it to). - * @param {Instruction[]} instr - */ - parsePostfixExpression(instr) { - this.parseFunctionCall(instr) - while(this.accept(TOP, "!")) { - instr.push(unaryInstruction("!")) - } - } - - /** - * Parse a function (name + parentheses + arguments). - * @param {Instruction[]} instr - */ - parseFunctionCall(instr) { - const prefixOperators = this.tokens.unaryOpsList - - if(this.accept(TOP, prefixOperators)) { - const op = this.current - this.parseAtom(instr) - instr.push(unaryInstruction(op.value)) - } else { - this.parseMemberExpression(instr) - while(this.accept(TPAREN, "(")) { - if(this.accept(TPAREN, ")")) { - instr.push(new Instruction(IFUNCALL, 0)) - } else { - const argCount = this.parseArgumentList(instr) - instr.push(new Instruction(IFUNCALL, argCount)) - } - } - } - } - - /** - * Parses a list of arguments, return their quantity. - * @param {Instruction[]} instr - * @return {number} - */ - parseArgumentList(instr) { - let argCount = 0 - - while(!this.accept(TPAREN, ")")) { - this.parseExpression(instr) - ++argCount - while(this.accept(TCOMMA)) { - this.parseExpression(instr) - ++argCount - } - } - - return argCount - } - - parseMemberExpression(instr) { - this.parseAtom(instr) - while(this.accept(TOP, ".") || this.accept(TBRACKET, "[")) { - const op = this.current - - if(op.value === ".") { - if(!this.allowMemberAccess) { - throw new Error(qsTranslate("error", "Unexpected \".\": member access is not permitted")) - } - - this.expect(TNAME) - instr.push(new Instruction(IMEMBER, this.current.value)) - } else if(op.value === "[") { - if(!this.tokens.isOperatorEnabled("[")) { - throw new Error(qsTranslate("error", "Unexpected \"[]\": arrays are disabled.")) - } - - this.parseExpression(instr) - this.expect(TBRACKET, "]") - instr.push(binaryInstruction("[")) - } else { - throw new Error(qsTranslate("error", "Unexpected symbol: %1.").arg(op.value)) - } - } - } -} diff --git a/common/src/lib/expr-eval/polyfill.mjs b/common/src/lib/expr-eval/polyfill.mjs deleted file mode 100644 index 65f05c7..0000000 --- a/common/src/lib/expr-eval/polyfill.mjs +++ /dev/null @@ -1,313 +0,0 @@ -/** - * Based on ndef.parser, by Raphael Graf - * http://www.undefined.ch/mparser/index.html - * - * Ported to JavaScript and modified by Matthew Crumley - * https://silentmatt.com/javascript-expression-evaluator/ - * - * Ported to QMLJS with modifications done accordingly done by Ad5001 (https://ad5001.eu) - * - * Copyright (c) 2015 Matthew Crumley, 2021-2025 Ad5001 - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * You are free to use and modify this code in anyway you find useful. Please leave this comment in the code - * to acknowledge its original source. If you feel like it, I enjoy hearing about projects that use my code, - * but don't feel like you have to let me know or ask permission. - */ - -export function add(a, b) { - return Number(a) + Number(b) -} - -export function sub(a, b) { - return a - b -} - -export function mul(a, b) { - return a * b -} - -export function div(a, b) { - return a / b -} - -export function mod(a, b) { - return a % b -} - -export function concat(a, b) { - if(Array.isArray(a) && Array.isArray(b)) { - return a.concat(b) - } - return "" + a + b -} - -export function equal(a, b) { - return a === b -} - -export function notEqual(a, b) { - return a !== b -} - -export function greaterThan(a, b) { - return a > b -} - -export function lessThan(a, b) { - return a < b -} - -export function greaterThanEqual(a, b) { - return a >= b -} - -export function lessThanEqual(a, b) { - return a <= b -} - -export function andOperator(a, b) { - return Boolean(a && b) -} - -export function orOperator(a, b) { - return Boolean(a || b) -} - -export function inOperator(a, b) { - return b.includes(a) -} - -export function sinh(a) { - return ((Math.exp(a) - Math.exp(-a)) / 2) -} - -export function cosh(a) { - return ((Math.exp(a) + Math.exp(-a)) / 2) -} - -export function tanh(a) { - if(a === Infinity) return 1 - if(a === -Infinity) return -1 - return (Math.exp(a) - Math.exp(-a)) / (Math.exp(a) + Math.exp(-a)) -} - -export function asinh(a) { - if(a === -Infinity) return a - return Math.log(a + Math.sqrt((a * a) + 1)) -} - -export function acosh(a) { - return Math.log(a + Math.sqrt((a * a) - 1)) -} - -export function atanh(a) { - return (Math.log((1 + a) / (1 - a)) / 2) -} - -export function log10(a) { - return Math.log(a) * Math.LOG10E -} - -export function neg(a) { - return -a -} - -export function not(a) { - return !a -} - -export function trunc(a) { - return a < 0 ? Math.ceil(a) : Math.floor(a) -} - -export function random(a) { - return Math.random() * (a || 1) -} - -export function factorial(a) { // a! - return gamma(a + 1) -} - -export function isInteger(value) { - return isFinite(value) && (value === Math.round(value)) -} - -const GAMMA_G = 4.7421875 -const GAMMA_P = [ - 0.99999999999999709182, - 57.156235665862923517, -59.597960355475491248, - 14.136097974741747174, -0.49191381609762019978, - 0.33994649984811888699e-4, - 0.46523628927048575665e-4, -0.98374475304879564677e-4, - 0.15808870322491248884e-3, -0.21026444172410488319e-3, - 0.21743961811521264320e-3, -0.16431810653676389022e-3, - 0.84418223983852743293e-4, -0.26190838401581408670e-4, - 0.36899182659531622704e-5 -] - -// Gamma function from math.js -export function gamma(n) { - let t, x - - if(isInteger(n)) { - if(n <= 0) { - return isFinite(n) ? Infinity : NaN - } - - if(n > 171) { - return Infinity // Will overflow - } - - let value = n - 2 - let res = n - 1 - while(value > 1) { - res *= value - value-- - } - - if(res === 0) { - res = 1 // 0! is per definition 1 - } - - return res - } - - if(n < 0.5) { - return Math.PI / (Math.sin(Math.PI * n) * gamma(1 - n)) - } - - if(n >= 171.35) { - return Infinity // will overflow - } - - if(n > 85.0) { // Extended Stirling Approx - const twoN = n * n - const threeN = twoN * n - const fourN = threeN * n - const fiveN = fourN * n - return Math.sqrt(2 * Math.PI / n) * Math.pow((n / Math.E), n) * - (1 + (1 / (12 * n)) + (1 / (288 * twoN)) - (139 / (51840 * threeN)) - - (571 / (2488320 * fourN)) + (163879 / (209018880 * fiveN)) + - (5246819 / (75246796800 * fiveN * n))) - } - - --n - x = GAMMA_P[0] - for(let i = 1; i < GAMMA_P.length; ++i) { - x += GAMMA_P[i] / (n + i) - } - - t = n + GAMMA_G + 0.5 - return Math.sqrt(2 * Math.PI) * Math.pow(t, n + 0.5) * Math.exp(-t) * x -} - -export function stringOrArrayLength(s) { - if(Array.isArray(s)) - return s.length - return String(s).length -} - -export function hypot() { - let sum = 0 - let larg = 0 - for(let i = 0; i < arguments.length; i++) { - const arg = Math.abs(arguments[i]) - let div - if(larg < arg) { - div = larg / arg - sum = (sum * div * div) + 1 - larg = arg - } else if(arg > 0) { - div = arg / larg - sum += div * div - } else { - sum += arg - } - } - return larg === Infinity ? Infinity : larg * Math.sqrt(sum) -} - -export function condition(cond, yep, nope) { - return cond ? yep : nope -} - -/** - * Decimal adjustment of a number. - * From @escopecz. - * - * @param {number} value - The number. - * @param {Integer} exp - The exponent (the 10 logarithm of the adjustment base). - * @return {number} - The adjusted value. - */ -export function roundTo(value, exp) { - // If the exp is undefined or zero... - if(typeof exp === "undefined" || +exp === 0) { - return Math.round(value) - } - value = +value - exp = -(+exp) - // If the value is not a number or the exp is not an integer... - if(isNaN(value) || !(typeof exp === "number" && exp % 1 === 0)) { - return NaN - } - // Shift - value = value.toString().split("e") - value = Math.round(+(value[0] + "e" + (value[1] ? (+value[1] - exp) : -exp))) - // Shift back - value = value.toString().split("e") - return +(value[0] + "e" + (value[1] ? (+value[1] + exp) : exp)) -} - -export function arrayIndex(array, index) { - return array[index | 0] -} - -export function max(array) { - if(arguments.length === 1 && Array.isArray(array)) { - return Math.max.apply(Math, array) - } else if(arguments.length >= 1) { - return Math.max.apply(Math, arguments) - } else { - throw new EvalError(qsTranslate("error", "Function %1 must have at least one argument.").arg("max")) - } -} - -export function min(array) { - if(arguments.length === 1 && Array.isArray(array)) { - return Math.min.apply(Math, array) - } else if(arguments.length >= 1) { - return Math.min.apply(Math, arguments) - } else { - throw new EvalError(qsTranslate("error", "Function %1 must have at least one argument.").arg("min")) - } -} - -export function sign(x) { - return ((x > 0) - (x < 0)) || +x -} - -const ONE_THIRD = 1 / 3 - -export function cbrt(x) { - return x < 0 ? -Math.pow(-x, ONE_THIRD) : Math.pow(x, ONE_THIRD) -} - -export function expm1(x) { - return Math.exp(x) - 1 -} - -export function log1p(x) { - return Math.log(1 + x) -} - -export function log2(x) { - return Math.log(x) / Math.LN2 -} \ No newline at end of file diff --git a/common/src/lib/expr-eval/tokens.mjs b/common/src/lib/expr-eval/tokens.mjs deleted file mode 100644 index 254d30a..0000000 --- a/common/src/lib/expr-eval/tokens.mjs +++ /dev/null @@ -1,575 +0,0 @@ -/** - * Based on ndef.parser, by Raphael Graf - * http://www.undefined.ch/mparser/index.html - * - * Ported to JavaScript and modified by Matthew Crumley - * https://silentmatt.com/javascript-expression-evaluator/ - * - * Ported to QMLJS with modifications done accordingly done by Ad5001 (https://ad5001.eu) - * - * Copyright (c) 2015 Matthew Crumley, 2021-2025 Ad5001 - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * You are free to use and modify this code in anyway you find useful. Please leave this comment in the code - * to acknowledge its original source. If you feel like it, I enjoy hearing about projects that use my code, - * but don't feel like you have to let me know or ask permission. - */ - -export const TEOF = "TEOF" -export const TOP = "TOP" -export const TNUMBER = "TNUMBER" -export const TSTRING = "TSTRING" -export const TPAREN = "TPAREN" -export const TBRACKET = "TBRACKET" -export const TCOMMA = "TCOMMA" -export const TNAME = "TNAME" - - -// Additional variable characters. -export const ADDITIONAL_VARCHARS = [ - "α", "β", "γ", "δ", "ε", "ζ", "η", - "π", "θ", "κ", "λ", "μ", "ξ", "ρ", - "ς", "σ", "τ", "φ", "χ", "ψ", "ω", - "Γ", "Δ", "Θ", "Λ", "Ξ", "Π", "Σ", - "Φ", "Ψ", "Ω", "ₐ", "ₑ", "ₒ", "ₓ", - "ₕ", "ₖ", "ₗ", "ₘ", "ₙ", "ₚ", "ₛ", - "ₜ", "¹", "²", "³", "⁴", "⁵", "⁶", - "⁷", "⁸", "⁹", "⁰", "₁", "₂", "₃", - "₄", "₅", "₆", "₇", "₈", "₉", "₀", - "∞", "π" -] - -export class Token { - /** - * - * @param {string} type - Type of the token (see above). - * @param {any} value - Value of the token. - * @param {number} index - Index in the string of the token. - */ - constructor(type, value, index) { - this.type = type - this.value = value - this.index = index - } - - toString() { - return this.type + ": " + this.value - } -} - -const unicodeCodePointPattern = /^[0-9a-f]{4}$/i - -export class TokenStream { - /** - * - * @param {Parser} parser - * @param {string} expression - */ - constructor(parser, expression) { - this.pos = 0 - this.current = null - this.unaryOps = parser.unaryOps - this.unaryOpsList = parser.unaryOpsList - this.binaryOps = parser.binaryOps - this.ternaryOps = parser.ternaryOps - this.builtinConsts = parser.builtinConsts - this.expression = expression - this.savedPosition = 0 - this.savedCurrent = null - this.options = parser.options - this.parser = parser - } - - /** - * - * @param {string} type - Type of the token (see above). - * @param {any} value - Value of the token. - * @param {number} [pos] - Index in the string of the token. - */ - newToken(type, value, pos) { - return new Token(type, value, pos != null ? pos : this.pos) - } - - /** - * Saves the current position and token into the object. - */ - save() { - this.savedPosition = this.pos - this.savedCurrent = this.current - } - - - /** - * Restored the saved position and token into the current. - */ - restore() { - this.pos = this.savedPosition - this.current = this.savedCurrent - } - - /** - * Consumes the character at the current position and advance it - * until it makes a valid token, and returns it. - * @returns {Token} - */ - next() { - if(this.pos >= this.expression.length) { - return this.newToken(TEOF, "EOF") - } - - if(this.isWhitespace()) { - return this.next() - } else if(this.isRadixInteger() || - this.isNumber() || - this.isOperator() || - this.isString() || - this.isParen() || - this.isBracket() || - this.isComma() || - this.isNamedOp() || - this.isConst() || - this.isName()) { - return this.current - } else { - this.parseError(qsTranslate("error", "Unknown character \"%1\".").arg(this.expression.charAt(this.pos))) - } - } - - /** - * Checks if the character at the current position starts a string, and if so, consumes it as the current token - * and returns true. Otherwise, returns false. - * @returns {boolean} - */ - isString() { - const startPos = this.pos - const quote = this.expression.charAt(startPos) - let r = false - - if(quote === "'" || quote === "\"") { - let index = this.expression.indexOf(quote, startPos + 1) - while(index >= 0 && this.pos < this.expression.length) { - this.pos = index + 1 - if(this.expression.charAt(index - 1) !== "\\") { - const rawString = this.expression.substring(startPos + 1, index) - this.current = this.newToken(TSTRING, this.unescape(rawString), startPos) - r = true - break - } - index = this.expression.indexOf(quote, index + 1) - } - } - return r - } - - /** - * Checks if the character at the current pos is a parenthesis, and if so consumes it into current - * and returns true. Otherwise, returns false. - * @returns {boolean} - */ - isParen() { - const c = this.expression.charAt(this.pos) - if(c === "(" || c === ")") { - this.current = this.newToken(TPAREN, c) - this.pos++ - return true - } - return false - } - - /** - * Checks if the character at the current pos is a bracket, and if so consumes it into current - * and returns true. Otherwise, returns false. - * @returns {boolean} - */ - isBracket() { - const c = this.expression.charAt(this.pos) - if((c === "[" || c === "]") && this.isOperatorEnabled("[")) { - this.current = this.newToken(TBRACKET, c) - this.pos++ - return true - } - return false - } - - /** - * Checks if the character at the current pos is a comma, and if so consumes it into current - * and returns true. Otherwise, returns false. - * @returns {boolean} - */ - isComma() { - const c = this.expression.charAt(this.pos) - if(c === ",") { - this.current = this.newToken(TCOMMA, ",") - this.pos++ - return true - } - return false - } - - /** - * Checks if the current character is an identifier and makes a const, and if so, consumes it as the current token - * and returns true. Otherwise, returns false. - * @returns {boolean} - */ - isConst() { - const startPos = this.pos - let i = startPos - for(; i < this.expression.length; i++) { - const c = this.expression.charAt(i) - if(c.toUpperCase() === c.toLowerCase() && !ADDITIONAL_VARCHARS.includes(c)) { - if(i === this.pos || (c !== "_" && c !== "." && (c < "0" || c > "9"))) { - break - } - } - } - if(i > startPos) { - const str = this.expression.substring(startPos, i) - if(str in this.builtinConsts) { - this.current = this.newToken(TNUMBER, this.builtinConsts[str]) - this.pos += str.length - return true - } - } - return false - } - - /** - * Checks if the current character is an identifier and makes a function or an operator, and if so, consumes it as the current token - * and returns true. Otherwise, returns false. - * @returns {boolean} - */ - isNamedOp() { - const startPos = this.pos - let i = startPos - for(; i < this.expression.length; i++) { - const c = this.expression.charAt(i) - if(c.toUpperCase() === c.toLowerCase()) { - if(i === this.pos || (c !== "_" && (c < "0" || c > "9"))) { - break - } - } - } - if(i > startPos) { - const str = this.expression.substring(startPos, i) - if(this.isOperatorEnabled(str) && (str in this.binaryOps || str in this.unaryOps || str in this.ternaryOps)) { - this.current = this.newToken(TOP, str) - this.pos += str.length - return true - } - } - return false - } - - /** - * Checks if the current character is an identifier and makes a variable, and if so, consumes it as the current token - * and returns true. Otherwise, returns false. - * @returns {boolean} - */ - isName() { - const startPos = this.pos - let i = startPos - let hasLetter = false - for(; i < this.expression.length; i++) { - const c = this.expression.charAt(i) - if(c.toUpperCase() === c.toLowerCase() && !ADDITIONAL_VARCHARS.includes(c)) { - if(i === this.pos && (c === "$" || c === "_")) { - if(c === "_") { - hasLetter = true - } - } else if(i === this.pos || !hasLetter || (c !== "_" && (c < "0" || c > "9"))) { - break - } - } else { - hasLetter = true - } - } - if(hasLetter) { - const str = this.expression.substring(startPos, i) - this.current = this.newToken(TNAME, str) - this.pos += str.length - return true - } - return false - } - - /** - * Checks if the character at the current position is a whitespace, and if so, consumes all consecutive whitespaces - * and returns true. Otherwise, returns false. - * @returns {boolean} - * - */ - isWhitespace() { - let r = false - let c = this.expression.charAt(this.pos) - while(c === " " || c === "\t" || c === "\n" || c === "\r") { - r = true - this.pos++ - if(this.pos >= this.expression.length) { - break - } - c = this.expression.charAt(this.pos) - } - return r - } - - /** - * Checks if the current character is a zero, and checks whether it forms a radix number, and if so, consumes it as the current token - * and returns true. Otherwise, returns false. - * @returns {boolean} - */ - isRadixInteger() { - let pos = this.pos - - if(pos >= this.expression.length - 2 || this.expression.charAt(pos) !== "0") { - return false - } - ++pos - - let radix - let validDigit - if(this.expression.charAt(pos) === "x") { - radix = 16 - validDigit = /^[0-9a-f]$/i - pos++ - } else if(this.expression.charAt(pos) === "b") { - radix = 2 - validDigit = /^[01]$/i - pos++ - } else { - return false - } - - let valid = false - const startPos = pos - - while(pos < this.expression.length) { - const c = this.expression.charAt(pos) - if(validDigit.test(c)) { - pos++ - valid = true - } else { - break - } - } - - if(valid) { - this.current = this.newToken(TNUMBER, parseInt(this.expression.substring(startPos, pos), radix)) - this.pos = pos - } - return valid - } - - /** - * Checks if the current character is a digit, and checks whether it forms a number, and if so, consumes it as the current token - * and returns true. Otherwise, returns false. - * @returns {boolean} - */ - isNumber() { - const startPos = this.pos - let valid = false - let pos = startPos - let resetPos = startPos - let foundDot = false - let foundDigits = false - let c - - // Check for digit with dot. - while(pos < this.expression.length) { - c = this.expression.charAt(pos) - if((c >= "0" && c <= "9") || (!foundDot && c === ".")) { - if(c === ".") { - foundDot = true - } else { - foundDigits = true - } - pos++ - valid = foundDigits - } else { - break - } - } - - if(valid) { - resetPos = pos - } - - // Check for e exponents. - if(c === "e" || c === "E") { - pos++ - let acceptSign = true - let validExponent = false - while(pos < this.expression.length) { - c = this.expression.charAt(pos) - if(acceptSign && (c === "+" || c === "-")) { - acceptSign = false - } else if(c >= "0" && c <= "9") { - validExponent = true - acceptSign = false - } else { - break - } - pos++ - } - - if(!validExponent) { - pos = resetPos - } - } - - // Use parseFloat now that we've identified the number. - if(valid) { - this.current = this.newToken(TNUMBER, parseFloat(this.expression.substring(startPos, pos))) - this.pos = pos - } else { - this.pos = resetPos - } - return valid - } - - /** - * Checks if the current character is an operator, checks whether it's enabled and if so, consumes it as the current token - * and returns true. Otherwise, returns false. - * @return {boolean} - */ - isOperator() { - const startPos = this.pos - const c = this.expression.charAt(this.pos) - - if(c === "+" || c === "-" || c === "*" || c === "/" || c === "%" || c === "^" || c === "?" || c === ":" || c === ".") { - this.current = this.newToken(TOP, c) - } else if(c === "∙" || c === "•") { - this.current = this.newToken(TOP, "*") - } else if(c === ">") { - if(this.expression.charAt(this.pos + 1) === "=") { - this.current = this.newToken(TOP, ">=") - this.pos++ - } else { - this.current = this.newToken(TOP, ">") - } - } else if(c === "<") { - if(this.expression.charAt(this.pos + 1) === "=") { - this.current = this.newToken(TOP, "<=") - this.pos++ - } else { - this.current = this.newToken(TOP, "<") - } - } else if(c === "|") { - if(this.expression.charAt(this.pos + 1) === "|") { - this.current = this.newToken(TOP, "||") - this.pos++ - } else { - return false - } - } else if(c === "=") { - if(this.expression.charAt(this.pos + 1) === "=") { - this.current = this.newToken(TOP, "==") - this.pos++ - } else { - return false - } - } else if(c === "!") { - if(this.expression.charAt(this.pos + 1) === "=") { - this.current = this.newToken(TOP, "!=") - this.pos++ - } else { - this.current = this.newToken(TOP, c) - } - } else { - return false - } - this.pos++ - - if(this.isOperatorEnabled(this.current.value)) { - return true - } else { - this.pos = startPos - return false - } - } - - /** - * Replaces a backslash and a character by its unescaped value. - * @param {string} v - string to un escape. - */ - unescape(v) { - let index = v.indexOf("\\") - if(index < 0) { - return v - } - - let buffer = v.substring(0, index) - while(index >= 0) { - const c = v.charAt(++index) - switch(c) { - case "'": - buffer += "'" - break - case "\"": - buffer += "\"" - break - case "\\": - buffer += "\\" - break - case "/": - buffer += "/" - break - case "b": - buffer += "\b" - break - case "f": - buffer += "\f" - break - case "n": - buffer += "\n" - break - case "r": - buffer += "\r" - break - case "t": - buffer += "\t" - break - case "u": - // interpret the following 4 characters as the hex of the unicode code point - const codePoint = v.substring(index + 1, index + 5) - if(!unicodeCodePointPattern.test(codePoint)) { - this.parseError(qsTranslate("error", "Illegal escape sequence: %1.").arg("\\u" + codePoint)) - } - buffer += String.fromCharCode(parseInt(codePoint, 16)) - index += 4 - break - default: - throw this.parseError(qsTranslate("error", "Illegal escape sequence: %1.").arg("\\" + c)) - } - ++index - const backslash = v.indexOf("\\", index) - buffer += v.substring(index, backslash < 0 ? v.length : backslash) - index = backslash - } - - return buffer - } - - /** - * Shorthand for the parser's method to check if an operator is enabled. - * @param {string} op - * @return {boolean} - */ - isOperatorEnabled(op) { - return this.parser.isOperatorEnabled(op) - } - - /** - * Throws a translated error. - * @param {string} msg - */ - parseError(msg) { - throw new Error(qsTranslate("error", "Parse error [position %1]: %2").arg(this.pos).arg(msg)) - } -} diff --git a/common/src/lib/polyfills/js.mjs b/common/src/lib/polyfills/js.mjs deleted file mode 100644 index 75d149c..0000000 --- a/common/src/lib/polyfills/js.mjs +++ /dev/null @@ -1,136 +0,0 @@ -/*! - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -// JS polyfills to add because they're not implemented in the QML Scripting engine. -// CoreJS does not work well with it (as well as doubles the compiled size), so this is preferable. -function notPolyfilled(name) { - return function() { throw new Error(`${name} not polyfilled`) } -} - -/** - * @param {number} depth - * @this {Array} - * @returns {Array} - */ -function arrayFlat(depth = 1) { - const newArray = [] - for(const element of this) { - if(element instanceof Array) - newArray.push(...(depth > 1 ? element.flat(depth - 1) : element)) - else - newArray.push(element) - } - return newArray -} - -/** - * @param {function(any, number, Array): any} callbackFn - * @param {object} thisArg - * @this {Array} - * @returns {Array} - */ -function arrayFlatMap(callbackFn, thisArg) { - const newArray = [] - for(let i = 0; i < this.length; i++) { - const value = callbackFn.call(thisArg ?? this, this[i], i, this) - if(value instanceof Array) - newArray.push(...value) - else - newArray.push(value) - } - return newArray -} - -/** - * Replaces all instances of from by to. - * @param {string} from - * @param {string} to - * @this {string} - * @return {String} - */ -function stringReplaceAll(from, to) { - return this.split(from).join(to) -} - -/** - * Returns the value of an element of the array at a given index. - * Accepts negative indexes. - * @this {Array|string} - * @param {number} index - * @return {*} - */ -function arrayAt(index) { - if(typeof index !== "number") - throw new Error(`${index} is not a number`) - return index >= 0 ? this[index] : this[this.length + index] -} - - -const polyfills = { - 2017: [ - [Object, "entries", notPolyfilled("Object.entries")], - [Object, "values", notPolyfilled("Object.values")], - [Object, "getOwnPropertyDescriptors", notPolyfilled("Object.getOwnPropertyDescriptors")], - [String.prototype, "padStart", notPolyfilled("String.prototype.padStart")], - [String.prototype, "padEnd", notPolyfilled("String.prototype.padEnd")] - ], - 2018: [ - [Promise.prototype, "finally", notPolyfilled("Object.entries")] - ], - 2019: [ - [String.prototype, "trimStart", notPolyfilled("String.prototype.trimStart")], - [String.prototype, "trimEnd", notPolyfilled("String.prototype.trimEnd")], - [Object, "fromEntries", notPolyfilled("Object.fromEntries")], - [Array.prototype, "flat", arrayFlat], - [Array.prototype, "flatMap", arrayFlatMap] - ], - 2020: [ - [String.prototype, "matchAll", notPolyfilled("String.prototype.matchAll")], - [Promise, "allSettled", notPolyfilled("Promise.allSettled")] - ], - 2021: [ - [Promise, "any", notPolyfilled("Promise.any")], - [String.prototype, "replaceAll", stringReplaceAll] - ], - 2022: [ - [Array.prototype, "at", arrayAt], - [String.prototype, "at", arrayAt], - [Object, "hasOwn", notPolyfilled("Object.hasOwn")] - ], - 2023: [ - [Array.prototype, "findLast", notPolyfilled("Array.prototype.findLast")], - [Array.prototype, "toReversed", notPolyfilled("Array.prototype.toReversed")], - [Array.prototype, "toSorted", notPolyfilled("Array.prototype.toSorted")], - [Array.prototype, "toSpliced", notPolyfilled("Array.prototype.toSpliced")], - [Array.prototype, "with", notPolyfilled("Array.prototype.with")] - ], - 2025: [ - [Object, "groupBy", notPolyfilled("Object.groupBy")], - [Map, "groupBy", notPolyfilled("Map.groupBy")] - ] -} - -// Fulfill polyfill. -for(const [year, entries] of Object.entries(polyfills)) { - const defined = entries.filter(x => x[0][x[1]] !== undefined) - console.info(`ES${year} support: ${defined.length === entries.length} (${defined.length}/${entries.length})`) - // Apply polyfills - for(const [context, functionName, polyfill] of entries.filter(x => x[0][x[1]] === undefined)) { - context[functionName] = polyfill - } -} diff --git a/common/src/lib/polyfills/qt.mjs b/common/src/lib/polyfills/qt.mjs deleted file mode 100644 index 0e9a9b5..0000000 --- a/common/src/lib/polyfills/qt.mjs +++ /dev/null @@ -1,46 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -// Type polyfills for IDEs. -// Never directly imported. -// Might need to be reimplemented in other implemententations. - -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(category, 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}; - } -} \ No newline at end of file diff --git a/common/src/math/expression.mjs b/common/src/math/expression.mjs deleted file mode 100644 index 2502110..0000000 --- a/common/src/math/expression.mjs +++ /dev/null @@ -1,147 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - - -import * as Utils from "../utils/index.mjs" -import { ExprEvalExpression } from "../lib/expr-eval/expression.mjs" -import Latex from "../module/latex.mjs" -import ExprParser from "../module/expreval.mjs" -import Objects from "../module/objects.mjs" - -const NUMBER_MATCHER = /^\d*\.\d+(e[+-]\d+)?$/ - -/** - * Represents any kind of x-based or non variable based expression. - */ -export class Expression { - /** - * - * @param {string|ExprEvalExpression} expr - */ - constructor(expr) { - if(typeof expr === "string") { - this.expr = Utils.exponentsToExpression(expr) - this.calc = ExprParser.parse(this.expr).simplify() - } else if(expr instanceof ExprEvalExpression) { - // Passed an expression here directly. - this.calc = expr.simplify() - this.expr = expr.toString() - } else { - const type = expr != null ? "a " + expr.constructor.name : expr - throw new Error(`Cannot create an expression with ${type}.`) - } - this.canBeCached = this.isConstant() - this.cachedValue = null - if(this.canBeCached && this.allRequirementsFulfilled()) - this.recache() - this.latexMarkup = Latex.expression(this.calc.tokens) - } - - /** - * Return all the variables used in calc - * @return {string[]} - */ - variables() { - return this.calc.variables() - } - - /** - * Checks if the current expression is constant (does not depend on a variable, be it x or n). - * @return {boolean} - */ - isConstant() { - let vars = this.calc.variables() - return !vars.includes("x") && !vars.includes("n") - } - - /** - * Returns the list of object names this expression is dependant on. - * @return {string[]} - */ - requiredObjects() { - return this.calc.variables().filter(objName => objName !== "x" && objName !== "n") - } - - /** - * Checks if all the objects required for this expression are defined. - * @return {boolean} - */ - allRequirementsFulfilled() { - return this.requiredObjects().every(objName => objName in Objects.currentObjectsByName) - } - - /** - * Returns a list of names whose corresponding objects this expression is dependant on and are missing. - * @return {string[]} - */ - undefinedVariables() { - return this.requiredObjects().filter(objName => !(objName in Objects.currentObjectsByName)) - } - - recache() { - this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName) - } - - execute(x = 1) { - if(this.canBeCached) { - if(this.cachedValue == null) - this.recache() - return this.cachedValue - } - ExprParser.currentVars = Object.assign({ "x": x }, Objects.currentObjectsByName) - return this.calc.evaluate(ExprParser.currentVars) - } - - simplify(x) { - let expr = new Expression(this.calc.substitute("x", x).simplify()) - if(expr.allRequirementsFulfilled() && expr.execute() === 0) - expr = new Expression("0") - return expr - } - - toEditableString() { - return this.calc.toString() - } - - toString(forceSign = false) { - let str = Utils.makeExpressionReadable(this.calc.toString()) - if(str !== undefined && str.match(NUMBER_MATCHER)) { - const decimals = str.split(".")[1].split("e")[0] - const zeros = decimals.split("0").length - const nines = decimals.split("9").length - if(zeros > 7 || nines > 7) { - // Likely rounding error - str = parseFloat(str).toDecimalPrecision(8).toString() - } - } - if(str[0] === "(" && str.at(-1) === ")") - str = str.substring(1, str.length - 1) - if(str[0] !== "-" && forceSign) - str = "+" + str - return str - } -} - -/** - * Parses and executes the given expression - * @param {string} expr - * @return {number} - */ -export function executeExpression(expr) { - return (new Expression(expr.toString())).execute() -} diff --git a/common/src/math/index.mjs b/common/src/math/index.mjs deleted file mode 100644 index c517958..0000000 --- a/common/src/math/index.mjs +++ /dev/null @@ -1,40 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - - -import * as Expr from "./expression.mjs" -import * as Seq from "./sequence.mjs" -import * as Dom from "./domain.mjs" - - -export const Expression = Expr.Expression -export const executeExpression = Expr.executeExpression -export const Sequence = Seq.Sequence - -// Domains -export const Domain = Dom.Domain -export const EmptySet = Dom.EmptySet -export const Range = Dom.Range -export const SpecialDomain = Dom.SpecialDomain -export const DomainSet = Dom.DomainSet -export const UnionDomain = Dom.UnionDomain -export const IntersectionDomain = Dom.IntersectionDomain -export const MinusDomain = Dom.MinusDomain - -export const parseDomain = Dom.parseDomain -export const parseDomainSimple = Dom.parseDomainSimple diff --git a/common/src/module/canvas.mjs b/common/src/module/canvas.mjs deleted file mode 100644 index 1137ae5..0000000 --- a/common/src/module/canvas.mjs +++ /dev/null @@ -1,607 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { Module } from "./common.mjs" -import { CanvasInterface, DialogInterface } from "./interface.mjs" -import { textsup } from "../utils/index.mjs" -import { Expression } from "../math/index.mjs" -import Latex from "./latex.mjs" -import Objects from "./objects.mjs" -import History from "./history.mjs" -import Settings from "./settings.mjs" - - -class CanvasAPI extends Module { - - - /** @type {CanvasInterface} */ - #canvas = null - /** @type {CanvasRenderingContext2D} */ - #ctx = null - /** Lock to prevent asynchronous stuff from printing stuff that is outdated. */ - #redrawCount = 0 - /** @type {{show(string, string, string)}} */ - #drawingErrorDialog = null - - - constructor() { - super("Canvas", { - canvas: CanvasInterface, - drawingErrorDialog: DialogInterface - }) - - /** - * - * @type {Object.} - */ - this.axesSteps = { - x: { - expression: null, - value: -1, - maxDraw: -1 - }, - y: { - expression: null, - value: -1, - maxDraw: -1 - } - } - } - - /** - * Initialize the module. - * @param {CanvasInterface} canvas - * @param {{show(string, string, string)}} drawingErrorDialog - */ - initialize({ canvas, drawingErrorDialog }) { - super.initialize({ canvas, drawingErrorDialog }) - this.#canvas = canvas - this.#drawingErrorDialog = drawingErrorDialog - } - - get width() { - if(!this.initialized) throw new Error("Attempting width before initialize!") - return this.#canvas.width - } - - get height() { - if(!this.initialized) throw new Error("Attempting height before initialize!") - return this.#canvas.height - } - - /** - * Minimum x of the diagram, provided from settings. - * @returns {number} - */ - get xmin() { - if(!this.initialized) throw new Error("Attempting xmin before initialize!") - return Settings.xmin - } - - /** - * Zoom on the x-axis of the diagram, provided from settings. - * @returns {number} - */ - get xzoom() { - if(!this.initialized) throw new Error("Attempting xzoom before initialize!") - return Settings.xzoom - } - - /** - * Maximum y of the diagram, provided from settings. - * @returns {number} - */ - get ymax() { - if(!this.initialized) throw new Error("Attempting ymax before initialize!") - return Settings.ymax - } - - /** - * Zoom on the y-axis of the diagram, provided from settings. - * @returns {number} - */ - get yzoom() { - if(!this.initialized) throw new Error("Attempting yzoom before initialize!") - return Settings.yzoom - } - - /** - * Label used on the x-axis, provided from settings. - * @returns {string} - */ - get xlabel() { - if(!this.initialized) throw new Error("Attempting xlabel before initialize!") - return Settings.xlabel - } - - /** - * Label used on the y-axis, provided from settings. - * @returns {string} - */ - get ylabel() { - if(!this.initialized) throw new Error("Attempting ylabel before initialize!") - return Settings.ylabel - } - - /** - * Width of lines that will be drawn into the canvas, provided from settings. - * @returns {number} - */ - get linewidth() { - if(!this.initialized) throw new Error("Attempting linewidth before initialize!") - return Settings.linewidth - } - - /** - * Font size of the text that will be drawn into the canvas, provided from settings. - * @returns {number} - */ - get textsize() { - if(!this.initialized) throw new Error("Attempting textsize before initialize!") - return Settings.textsize - } - - /** - * True if the canvas should be in logarithmic mode, false otherwise. - * @returns {boolean} - */ - get logscalex() { - if(!this.initialized) throw new Error("Attempting logscalex before initialize!") - return Settings.logscalex - } - - /** - * True if the x graduation should be shown, false otherwise. - * @returns {boolean} - */ - get showxgrad() { - if(!this.initialized) throw new Error("Attempting showxgrad before initialize!") - return Settings.showxgrad - } - - /** - * True if the y graduation should be shown, false otherwise. - * @returns {boolean} - */ - get showygrad() { - if(!this.initialized) throw new Error("Attempting showygrad before initialize!") - return Settings.showygrad - } - - /** - * Max power of the logarithmic scaled on the x axis in logarithmic mode. - * @returns {number} - */ - get maxgradx() { - if(!this.initialized) throw new Error("Attempting maxgradx before initialize!") - return Math.min( - 309, // 10e309 = Infinity (beyond this land be dragons) - Math.max( - Math.ceil(Math.abs(Math.log10(this.xmin))), - Math.ceil(Math.abs(Math.log10(this.px2x(this.width)))) - ) - ) - } - - // - // Methods to draw the canvas - // - - requestPaint() { - if(!this.initialized) throw new Error("Attempting requestPaint before initialize!") - this.#canvas.requestPaint() - } - - /** - * Redraws the entire canvas - */ - redraw() { - if(!this.initialized) throw new Error("Attempting redraw before initialize!") - if(this.#ctx == null) - this.#ctx = this.#canvas.getContext("2d") - this.#redrawCount = (this.#redrawCount + 1) % 10000 - this._computeAxes() - this._reset() - this._drawGrid() - this._drawAxes() - this._drawLabels() - this.#ctx.lineWidth = this.linewidth - for(let objType in Objects.currentObjects) { - for(let obj of 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) - console.error(e) - console.log(e.stack) - this.#drawingErrorDialog.show(objType, obj.name, e.message) - History.undo() - } - } - } - this.#ctx.lineWidth = 1 - } - - /** - * Calculates information for drawing gradations for axes. - * @private - */ - _computeAxes() { - let exprY = new Expression(`x*(${Settings.yaxisstep})`) - let y1 = exprY.execute(1) - let exprX = new Expression(`x*(${Settings.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).toString().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).toString().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) { - if(this.logscalex) { - const logxmin = Math.log(this.xmin) - return (Math.log(x) - logxmin) * this.xzoom - } else - return (x - this.xmin) * this.xzoom - } - - /** - * 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.ymax - y) * this.yzoom - } - - /** - * 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) { - if(this.logscalex) { - return Math.exp(px / this.xzoom + Math.log(this.xmin)) - } else - return (px / this.xzoom + this.xmin) - } - - /** - * 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 -(px / this.yzoom - this.ymax) - } - - /** - * 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(LatexRenderResult|{width: number, height: number, source: string})} callback - */ - renderLatexImage(ltxText, color, callback) { - const currentRedrawCount = this.#redrawCount - const onRendered = (imgData) => { - if(!this.#canvas.isImageLoaded(imgData.source) && !this.#canvas.isImageLoading(imgData.source)) { - // Wait until the image is loaded to callback. - this.#canvas.loadImageAsync(imgData.source).then(() => { - if(this.#redrawCount === currentRedrawCount) - callback(imgData) - else - console.log("1. Discard render of", imgData.source, this.#redrawCount, currentRedrawCount) - }) - } else { - // Callback directly - if(this.#redrawCount === currentRedrawCount) - callback(imgData) - else - console.log("2. Discard render of", imgData.source, this.#redrawCount, currentRedrawCount) - } - } - const prerendered = Latex.findPrerendered(ltxText, this.textsize, color) - if(prerendered !== null) - onRendered(prerendered) - else - Latex.requestAsyncRender(ltxText, this.textsize, color).then(onRendered) - } - - // - // 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 default Modules.Canvas diff --git a/common/src/module/common.mjs b/common/src/module/common.mjs deleted file mode 100644 index f5fa8aa..0000000 --- a/common/src/module/common.mjs +++ /dev/null @@ -1,74 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { Interface } from "./interface.mjs" -import { BaseEventEmitter } from "../events.mjs" - -// Define Modules interface before they are imported. -globalThis.Modules = globalThis.Modules || {} - -/** - * Base class for global APIs in runtime. - */ -export class Module extends BaseEventEmitter { - /** @type {string} */ - #name - /** @type {Object.} */ - #initializationParameters - /** @type {boolean} */ - #initialized = false - - /** - * - * @param {string} name - Name of the API - * @param {Object.} initializationParameters - List of parameters for the initialize function. - */ - constructor(name, initializationParameters = {}) { - super() - console.log(`Loading module ${name}...`) - this.#name = name - this.#initializationParameters = initializationParameters - } - - get name() { - return this.#name; - } - - get initialized() { - return this.#initialized - } - - /** - * Checks if all requirements are defined. - * @param {Object.} options - */ - initialize(options) { - if(this.#initialized) - throw new Error(`Cannot reinitialize module ${this.#name}.`) - console.log(`Initializing ${this.#name}...`) - for(const [name, value] of Object.entries(this.#initializationParameters)) { - if(!options.hasOwnProperty(name)) - throw new Error(`Option '${name}' of initialize of module ${this.#name} does not exist.`) - if(typeof value === "function" && value.prototype instanceof Interface) - Interface.checkImplementation(value, options[name]) - else if(typeof value !== typeof options[name]) - throw new Error(`Option '${name}' of initialize of module ${this.#name} is not a '${value}' (${typeof options[name]}).`) - } - this.#initialized = true - } -} diff --git a/common/src/module/expreval.mjs b/common/src/module/expreval.mjs deleted file mode 100644 index dd89f4d..0000000 --- a/common/src/module/expreval.mjs +++ /dev/null @@ -1,116 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { Module } from "./common.mjs" -import { Parser } from "../lib/expr-eval/parser.mjs" - -const EVAL_VARIABLES = { - // 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 Module { - #parser = new Parser() - - constructor() { - super("ExprParser") - this.currentVars = {} - this.#parser = new Parser() - - this.#parser.consts = Object.assign({}, this.#parser.consts, EVAL_VARIABLES) - - 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, variable - if(args.length === 1) { - // Parse object - f = args[0] - if(typeof f !== "object" || !f.execute) - throw EvalError(qsTranslate("usage", "Usage:\n%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:\n%1").arg(usage2)) - f = this.#parser.parse(f).toJSFunction(variable, this.currentVars) - } else - throw EvalError(qsTranslate("usage", "Usage:\n%1\n%2").arg(usage1).arg(usage2)) - return f - } - - /** - * @param {string} expression - Expression to parse - * @returns {ExprEvalExpression} - */ - parse(expression) { - return this.#parser.parse(expression) - } - - integral(a = null, b = null, ...args) { - let usage1 = qsTranslate("usage", "integral(, , )") - let usage2 = qsTranslate("usage", "integral(, , , )") - let f = this.parseArgumentsForFunction(args, usage1, usage2) - if(typeof a !== "number" || typeof b !== "number") - throw EvalError(qsTranslate("usage", "Usage:\n%1\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(, )") - let usage2 = qsTranslate("usage", "derivative(, , )") - let x = args.pop() - let f = this.parseArgumentsForFunction(args, usage1, usage2) - if(typeof x !== "number") - throw EvalError(qsTranslate("usage", "Usage:\n%1\n%2").arg(usage1).arg(usage2)) - - let derivative_precision = 1e-8 - return (f(x + derivative_precision / 2) - f(x - derivative_precision / 2)) / derivative_precision - } -} - -/** @type {ExprParserAPI} */ -Modules.ExprParser = Modules.ExprParser || new ExprParserAPI() - -export default Modules.ExprParser - diff --git a/common/src/module/history.mjs b/common/src/module/history.mjs deleted file mode 100644 index 45a37f0..0000000 --- a/common/src/module/history.mjs +++ /dev/null @@ -1,184 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { Module } from "./common.mjs" -import { HelperInterface, NUMBER, STRING } from "./interface.mjs" -import { BaseEvent } from "../events.mjs" -import { Action, Actions } from "../history/index.mjs" - - - -class ClearedEvent extends BaseEvent { - constructor() { - super("cleared") - } -} - -class LoadedEvent extends BaseEvent { - constructor() { - super("loaded") - } -} - -class AddedEvent extends BaseEvent { - constructor(action) { - super("added") - this.action = action - } -} - -class UndoneEvent extends BaseEvent { - constructor(action) { - super("undone") - this.undid = action - } -} - -class RedoneEvent extends BaseEvent { - constructor(action) { - super("redone") - this.redid = action - } -} - -class HistoryAPI extends Module { - static emits = ["cleared", "loaded", "added", "undone", "redone"] - - #helper - - constructor() { - super("History", { - helper: HelperInterface, - themeTextColor: STRING, - imageDepth: NUMBER, - fontSize: NUMBER - }) - // History QML object - /** @type {Action[]} */ - this.undoStack = [] - /** @type {Action[]} */ - this.redoStack = [] - - this.themeTextColor = "#FF0000" - this.imageDepth = 2 - this.fontSize = 28 - } - - /** - * @param {HelperInterface} historyObj - * @param {string} themeTextColor - * @param {number} imageDepth - * @param {number} fontSize - */ - initialize({ helper, themeTextColor, imageDepth, fontSize }) { - super.initialize({ helper, themeTextColor, imageDepth, fontSize }) - this.#helper = helper - this.themeTextColor = themeTextColor - this.imageDepth = imageDepth - this.fontSize = fontSize - } - - /** - * Undoes the Action at the top of the undo stack and pushes it to the top of the redo stack. - */ - undo() { - if(!this.initialized) throw new Error("Attempting undo before initialize!") - if(this.undoStack.length > 0) { - const action = this.undoStack.pop() - action.undo() - this.redoStack.push(action) - this.emit(new UndoneEvent(action)) - } - } - - /** - * Redoes the Action at the top of the redo stack and pushes it to the top of the undo stack. - */ - redo() { - if(!this.initialized) throw new Error("Attempting redo before initialize!") - if(this.redoStack.length > 0) { - const action = this.redoStack.pop() - action.redo() - this.undoStack.push(action) - this.emit(new RedoneEvent(action)) - } - } - - /** - * Clears both undo and redo stacks completely. - */ - clear() { - if(!this.initialized) throw new Error("Attempting clear before initialize!") - this.undoStack = [] - this.redoStack = [] - this.emit(new ClearedEvent()) - } - - /** - * Adds an instance of HistoryLib.Action to history. - * @param action - */ - addToHistory(action) { - if(!this.initialized) throw new Error("Attempting addToHistory before initialize!") - if(action instanceof Action) { - console.log("Added new entry to history: " + action.getReadableString()) - this.undoStack.push(action) - if(this.#helper.getSetting("reset_redo_stack")) - this.redoStack = [] - this.emit(new AddedEvent(action)) - } - } - - /** - * Unserializes both the undo stack and redo stack from serialized content. - * @param {[string, any[]][]} undoSt - * @param {[string, any[]][]} redoSt - */ - unserialize(undoSt, redoSt) { - if(!this.initialized) throw new Error("Attempting unserialize before initialize!") - this.clear() - for(const [name, args] of undoSt) - this.undoStack.push( - new Actions[name](...args) - ) - for(const [name, args] of redoSt) - this.redoStack.push( - new Actions[name](...args) - ) - this.emit(new LoadedEvent()) - } - - /** - * Serializes history into JSON-able content. - * @return {[[string, any[]], [string, any[]]]} - */ - serialize() { - if(!this.initialized) throw new Error("Attempting serialize before initialize!") - let undoSt = [], redoSt = []; - for(const action of this.undoStack) - undoSt.push([ action.type(), action.export() ]) - for(const action of this.redoStack) - redoSt.push([ action.type(), action.export() ]) - return [undoSt, redoSt] - } -} - -/** @type {HistoryAPI} */ -Modules.History = Modules.History || new HistoryAPI() - -export default Modules.History diff --git a/common/src/module/index.mjs b/common/src/module/index.mjs deleted file mode 100644 index 6fdff97..0000000 --- a/common/src/module/index.mjs +++ /dev/null @@ -1,37 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import Objects from "./objects.mjs" -import Settings from "./settings.mjs" -import ExprParser from "./expreval.mjs" -import Latex from "./latex.mjs" -import History from "./history.mjs" -import Canvas from "./canvas.mjs" -import IO from "./io.mjs" -import Preferences from "./preferences.mjs" - -export default { - Objects, - Settings, - ExprParser, - Latex, - History, - Canvas, - IO, - Preferences -} diff --git a/common/src/module/interface.mjs b/common/src/module/interface.mjs deleted file mode 100644 index 6947acd..0000000 --- a/common/src/module/interface.mjs +++ /dev/null @@ -1,141 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * - * @author Ad5001 - * @license GPL-3.0-or-later - * @copyright (C) 2021-2025 Ad5001 - * @preserve - * - * 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 . - */ - -export const NUMBER = 0 -export const STRING = "string" -export const BOOLEAN = true -export const OBJECT = {} -export const FUNCTION = () => { - throw new Error("Cannot call function of an interface.") -} - - -export class Interface { - /** - * Checks if the class to check implements the given interface. - * Throws an error if the implementation does not conform to the interface. - * @param {typeof Interface} interface_ - * @param {object} classToCheck - */ - static checkImplementation(interface_, classToCheck) { - const properties = new interface_() - const interfaceName = interface_.name - const toCheckName = classToCheck.constructor.name - for(const [property, value] of Object.entries(properties)) - if(property !== "implement") { - if(classToCheck[property] === undefined) - // Check if the property exist - throw new Error(`Property '${property}' (${typeof value}) is present in interface ${interfaceName}, but not in implementation ${toCheckName}.`) - else if((typeof value) !== (typeof classToCheck[property])) - // Compare the types - throw new Error(`Property '${property}' of ${interfaceName} implementation ${toCheckName} is a '${typeof classToCheck[property]}' and not a '${typeof value}'.`) - else if((typeof value) === "object") - // Test type of object. - if(value instanceof Interface) - Interface.checkImplementation(value, classToCheck[property]) - else if(value.prototype && !(classToCheck[property] instanceof value)) - throw new Error(`Property '${property}' of ${interfaceName} implementation ${toCheckName} is not '${value.constructor.name}'.`) - } - } -} - - -export class CanvasInterface extends Interface { - /** @type {function(string): CanvasRenderingContext2D} */ - getContext = FUNCTION - /** @type {function(rect)} */ - markDirty = FUNCTION - /** @type {function(string): Promise} */ - loadImageAsync = FUNCTION - /** @type {function(string)} */ - isImageLoading = FUNCTION - /** @type {function(string)} */ - isImageLoaded = FUNCTION - /** @type {function()} */ - requestPaint = FUNCTION -} - -export class RootInterface extends Interface { - width = NUMBER - height = NUMBER -} - -export class DialogInterface extends Interface { - show = FUNCTION -} - -export class LatexInterface extends Interface { - supportsAsyncRender = BOOLEAN - /** - * @param {string} markup - LaTeX markup to render - * @param {number} fontSize - Font size (in pt) to render - * @param {string} color - Color of the text to render - * @returns {string} - Comma separated data of the image (source, width, height) - */ - renderSync = FUNCTION - /** - * @param {string} markup - LaTeX markup to render - * @param {number} fontSize - Font size (in pt) to render - * @param {string} color - Color of the text to render - * @returns {Promise} - Comma separated data of the image (source, width, height) - */ - renderAsync = FUNCTION - /** - * @param {string} markup - LaTeX markup to render - * @param {number} fontSize - Font size (in pt) to render - * @param {string} color - Color of the text to render - * @returns {string} - Comma separated data of the image (source, width, height) - */ - findPrerendered = FUNCTION - /** - * Checks if the Latex installation is valid - * @returns {boolean} - */ - checkLatexInstallation = FUNCTION -} - -export class HelperInterface extends Interface { - /** - * Gets a setting from the config - * @param {string} settingName - Setting (and its dot-separated namespace) to get (e.g. "default_graph.xmin") - * @returns {string|number|boolean} Value of the setting - */ - getSetting = FUNCTION - /** - * Sets a setting in the config - * @param {string} settingName - Setting (and its dot-separated namespace) to set (e.g. "default_graph.xmin") - * @param {string|number|boolean} value - */ - setSetting = FUNCTION - /** - * Sends data to be written - * @param {string} file - * @param {string} dataToWrite - just JSON encoded, requires the "LPFv1" mime to be added before writing - */ - write = FUNCTION - /** - * Requests data to be read from a file - * @param {string} file - * @returns {string} the loaded data - just JSON encoded, requires the "LPFv1" mime to be stripped - */ - load = FUNCTION -} diff --git a/common/src/module/io.mjs b/common/src/module/io.mjs deleted file mode 100644 index 4887954..0000000 --- a/common/src/module/io.mjs +++ /dev/null @@ -1,213 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { Module } from "./common.mjs" -import Objects from "./objects.mjs" -import History from "./history.mjs" -import Settings from "./settings.mjs" -import { DialogInterface, RootInterface } from "./interface.mjs" -import { BaseEvent } from "../events.mjs" - - -class LoadedEvent extends BaseEvent { - constructor() { - super("loaded") - } -} - -class SavedEvent extends BaseEvent { - constructor() { - super("saved") - } -} - -class ModifiedEvent extends BaseEvent { - constructor() { - super("modified") - } -} - -class IOAPI extends Module { - static emits = ["loaded", "saved", "modified"] - - /** @type {RootInterface} */ - #rootElement - /** @type {{show: function(string)}} */ - #alert - #saved = true - - constructor() { - super("IO", { - alert: DialogInterface, - root: RootInterface - }) - - // Settings.on("changed", this.__emitModified.bind(this)) - History.on("added undone redone", this.__emitModified.bind(this)) - } - - __emitModified() { - this.#saved = false - this.emit(new ModifiedEvent()) - } - - - /** - * True if no changes have been made since last save, false otherwise. - * @return {boolean} - */ - get saved() { return this.#saved } - - /** - * Initializes module with QML elements. - * @param {RootInterface} root - * @param {{show: function(string)}} alert - */ - initialize({ root, alert }) { - super.initialize({ root, alert }) - this.#rootElement = root - this.#alert = alert - } - - /** - * Saves the diagram to a certain \c filename. - * @param {string} filename - */ - saveDiagram(filename) { - if(!this.initialized) throw new Error("Attempting saveDiagram before initialize!") - // Add extension if necessary - if(["lpf"].indexOf(filename.split(".")[filename.split(".").length - 1]) === -1) - filename += ".lpf" - Settings.set("saveFilename", filename, false) - let objs = {} - for(let objType in Objects.currentObjects) { - objs[objType] = [] - for(let obj of Objects.currentObjects[objType]) { - objs[objType].push(obj.export()) - } - } - let settings = { - "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": 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())) - this.#saved = true - this.emit(new SavedEvent()) - } - - /** - * Loads the diagram from a certain \c filename. - * @param {string} filename - */ - async loadDiagram(filename) { - if(!this.initialized) throw new Error("Attempting loadDiagram before initialize!") - if(!History.initialized) throw new Error("Attempting loadDiagram before history is initialized!") - 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(data.hasOwnProperty("type") && data["type"] === "logplotv1") { - History.clear() - // Importing settings - Settings.set("saveFilename", filename, false) - Settings.set("xzoom", parseFloat(data["xzoom"]) || 100, false) - Settings.set("yzoom", parseFloat(data["yzoom"]) || 10, false) - Settings.set("xmin", parseFloat(data["xmin"]) || 5 / 10, false) - Settings.set("ymax", parseFloat(data["ymax"]) || 24, false) - Settings.set("xaxisstep", data["xaxisstep"] || "4", false) - Settings.set("yaxisstep", data["yaxisstep"] || "4", false) - Settings.set("xlabel", data["xaxislabel"] || "", false) - Settings.set("ylabel", data["yaxislabel"] || "", false) - Settings.set("logscalex", data["logscalex"] === true, false) - if("showxgrad" in data) - Settings.set("showxgrad", data["showxgrad"], false) - if("showygrad" in data) - Settings.set("showygrad", data["showygrad"], false) - if("linewidth" in data) - Settings.set("linewidth", data["linewidth"], false) - if("textsize" in data) - Settings.set("textsize", data["textsize"], false) - this.#rootElement.height = parseFloat(data["height"]) || 500 - this.#rootElement.width = parseFloat(data["width"]) || 1000 - - // Importing objects - Objects.currentObjects = {} - for(let key of Object.keys(Objects.currentObjectsByName)) { - 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).includes(objType)) { - Objects.currentObjects[objType] = [] - for(let objData of data["objects"][objType]) { - /** @type {DrawableObject} */ - let obj = Objects.types[objType].import(...objData) - Objects.currentObjects[objType].push(obj) - Objects.currentObjectsByName[obj.name] = obj - } - } else { - error += qsTranslate("io", "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"]) - - } else { - error = qsTranslate("io", "Invalid file provided.") - } - if(error !== "") { - console.log(error) - this.#alert.show(qsTranslate("io", "Could not load file: ") + error) - // TODO: Error handling - return - } - this.#alert.show(qsTranslate("io", "Loaded file '%1'.").arg(basename)) - this.#saved = true - this.emit(new LoadedEvent()) - } - -} - -/** @type {IOAPI} */ -Modules.IO = Modules.IO || new IOAPI() - -export default Modules.IO diff --git a/common/src/module/latex.mjs b/common/src/module/latex.mjs deleted file mode 100644 index 27fbdd4..0000000 --- a/common/src/module/latex.mjs +++ /dev/null @@ -1,374 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { Module } from "./common.mjs" -import { BaseEvent } from "../events.mjs" -import * as Instruction from "../lib/expr-eval/instruction.mjs" -import { escapeValue } from "../lib/expr-eval/expression.mjs" -import { HelperInterface, LatexInterface } from "./interface.mjs" - -const unicodechars = ["pi", "∞", - "α", "β", "γ", "δ", "ε", "ζ", "η", - "π", "θ", "κ", "λ", "μ", "ξ", "ρ", - "ς", "σ", "τ", "φ", "χ", "ψ", "ω", - "Γ", "Δ", "Θ", "Λ", "Ξ", "Π", "Σ", - "Φ", "Ψ", "Ω", "ₐ", "ₑ", "ₒ", "ₓ", - "ₕ", "ₖ", "ₗ", "ₘ", "ₙ", "ₚ", "ₛ", - "ₜ", "¹", "²", "³", "⁴", "⁵", "⁶", - "⁷", "⁸", "⁹", "⁰", "₁", "₂", "₃", - "₄", "₅", "₆", "₇", "₈", "₉", "₀" -] -const equivalchars = ["\\pi", "\\infty", - "\\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}", -] - - - - -class AsyncRenderStartedEvent extends BaseEvent { - constructor(markup, fontSize, color) { - super("async-render-started") - this.markup = markup - this.fontSize = fontSize - this.color = color - } -} - - -class AsyncRenderFinishedEvent extends BaseEvent { - constructor(markup, fontSize, color) { - super("async-render-finished") - this.markup = markup - this.fontSize = fontSize - this.color = color - } -} - -/** - * Class containing the result of a LaTeX render. - * - * @property {string} source - Exported PNG file - * @property {number} width - * @property {number} height - */ -class LatexRenderResult { - constructor(source, width, height) { - this.source = source - this.width = parseFloat(width) - this.height = parseFloat(height) - } -} - -class LatexAPI extends Module { - static emits = ["async-render-started", "async-render-finished"] - - /** @type {LatexInterface} */ - #latex = null - - constructor() { - super("Latex", { - latex: LatexInterface, - helper: HelperInterface - }) - /** - * true if latex has been enabled by the user, false otherwise. - */ - this.enabled = false - this.promises = new Set() - } - - /** - * @param {LatexInterface} latex - * @param {HelperInterface} helper - */ - initialize({ latex, helper }) { - super.initialize({ latex, helper }) - this.#latex = latex - this.enabled = helper.getSetting("enable_latex") - } - - /** - * Checks if the given markup (with given font size and color) has already been - * rendered, and if so, returns its data. Otherwise, returns null. - * - * @param {string} markup - LaTeX markup to render. - * @param {number} fontSize - Font size (in pt) to render. - * @param {string} color - Color of the text to render. - * @returns {LatexRenderResult|null} - */ - findPrerendered(markup, fontSize, color) { - if(!this.initialized) throw new Error("Attempting findPrerendered before initialize!") - const data = this.#latex.findPrerendered(markup, fontSize, color) - let ret = null - if(data !== "") - ret = new LatexRenderResult(...data.split(",")) - return ret - } - - /** - * Prepares and renders a latex string into a png file asynchronously. - * - * @param {string} markup - LaTeX markup to render. - * @param {number} fontSize - Font size (in pt) to render. - * @param {string} color - Color of the text to render. - * @returns {Promise} - */ - async requestAsyncRender(markup, fontSize, color) { - if(!this.initialized) throw new Error("Attempting requestAsyncRender before initialize!") - let render - if(this.#latex.supportsAsyncRender) { - this.emit(new AsyncRenderStartedEvent(markup, fontSize, color)) - // Storing promise so that it does not get dereferenced. - const promise = this.#latex.renderAsync(markup, fontSize, color) - this.promises.add(promise) - render = await promise - this.promises.delete(promise) - this.emit(new AsyncRenderFinishedEvent(markup, fontSize, color)) - } else { - render = this.#latex.renderSync(markup, fontSize, color) - } - const args = render.split(",") - return new LatexRenderResult(...args) - } - - /** - * Puts element within parenthesis. - * - * @param {string|number} 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|number} elem - element to put within parenthesis. - * @param {Array} contents - Array of elements to put within parenthesis. - * @returns {string} - */ - parif(elem, contents) { - elem = elem.toString() - const contains = contents.some(x => elem.indexOf(x) > 0) - if(contains && (elem[0] !== "(" || elem.at(-1) !== ")")) - return this.par(elem) - if(!contains && elem[0] === "(" && elem.at(-1) === ")") - return elem.removeEnclosure() - 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].removeEnclosure().replaceAll(args[1].removeEnclosure(), "x")}}{dx}` - else - return `\\frac{d${args[0]}}{dx}(${args[1]})` - case "integral": - if(args.length === 4) - return `\\int\\limits_{${args[0]}}^{${args[1]}}${args[2].removeEnclosure()} d${args[3].removeEnclosure()}` - else - return `\\int\\limits_{${args[0]}}^{${args[1]}}${args[2]}(t) dt` - case "sqrt": - const arg = this.parif(args.join(", "), []) - return `\\sqrt{${arg}}` - case "abs": - return `\\left|${args.join(", ")}\\right|` - case "floor": - return `\\left\\lfloor{${args.join(", ")}}\\right\\rfloor` - case "ceil": - return `\\left\\lceil{${args.join(", ")}}\\right\\rceil` - default: - return `\\mathrm{${f}}\\left(${args.join(", ")}\\right)` - } - } - - /** - * 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.replaceAll(unicodechars[i], "$" + equivalchars[i] + "$") - } - } else { - for(let i = 0; i < unicodechars.length; i++) { - if(vari.includes(unicodechars[i])) - vari = vari.replaceAll(unicodechars[i], equivalchars[i]) - } - } - return vari - } - - /** - * Converts expr-eval instructions to a latex string. - * - * @param {Instruction[]} instructions - expr-eval tokens list - * @returns {string} - */ - expression(instructions) { - let nstack = [] - let n1, n2, n3 - let f, args, argCount - for(let item of instructions) { - let type = item.type - - switch(type) { - case Instruction.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(escapeValue).join(", ") + "]") - } else { - nstack.push(escapeValue(item.value)) - } - break - case Instruction.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} " + this.parif(n2, ["+", "-", "*", "/", "!", "^"])) - break - case "[": - nstack.push(n1 + "[" + n2 + "]") - break - default: - throw new EvalError("Unknown operator " + item.value + ".") - } - break - case Instruction.IOP3: // Ternary 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 " + item.value + ".") - } - break - case Instruction.IVAR: - case Instruction.IVARNAME: - nstack.push(this.variable(item.value.toString())) - break - case Instruction.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(this.functionToLatex(f, [this.parif(n1, ["+", "-", "*", "/", "^"])])) - break - } - break - case Instruction.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 Instruction.IMEMBER: - n1 = nstack.pop() - nstack.push(n1 + "." + item.value) - break - case Instruction.IARRAY: - argCount = item.value - args = [] - while(argCount-- > 0) { - args.unshift(nstack.pop()) - } - nstack.push("[" + args.join(", ") + "]") - break - case Instruction.IEXPR: - nstack.push("(" + this.expression(item.value) + ")") - break - case Instruction.IENDSTATEMENT: - break - default: - throw new EvalError("invalid Expression") - } - } - return String(nstack[0]) - } -} - -/** @type {LatexAPI} */ -Modules.Latex = Modules.Latex || new LatexAPI() -/** @type {LatexAPI} */ -export default Modules.Latex diff --git a/common/src/module/objects.mjs b/common/src/module/objects.mjs deleted file mode 100644 index e98a15f..0000000 --- a/common/src/module/objects.mjs +++ /dev/null @@ -1,134 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { Module } from "./common.mjs" -import { textsub } from "../utils/index.mjs" - -class ObjectsAPI extends Module { - - constructor() { - super("Objects") - - /** - * List of object constructors. - * @type {Object.} - */ - this.types = {} - /** - * List of objects for each type of object. - * @type {Object.} - */ - this.currentObjects = {} - /** - * List of objects matched by their name. - * @type {Object.} - */ - this.currentObjectsByName = {} - } - - /** - * Creates a new name for an object, based on the allowedLetters. - * If variables with each of the allowedLetters is created, a subscript - * number is added to the name. - * @param {string} allowedLetters - * @param {string} prefix - Prefix to the name. - * @return {string} New unused name for a new object. - */ - getNewName(allowedLetters, prefix = "") { - // Allows to get a new name, based on the allowed letters, - // as well as adding a sub number when needs be. - let newid = 0 - let ret - do { - let letter = allowedLetters[newid % allowedLetters.length] - let num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length) - ret = prefix + letter + (num > 0 ? textsub(num - 1) : "") - newid += 1 - } while(ret in this.currentObjectsByName) - return ret - } - - /** - * 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) { - const 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) { - const 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") - return this.getExecutableTypes().flatMap( - elemType => this.currentObjects[elemType].map(obj => obj.name) - ) - 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} Newly created object. - */ - createNewRegisteredObject(objType, args = []) { - if(Object.keys(this.types).indexOf(objType) === -1) return null // Object type does not exist. - const 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 diff --git a/common/src/module/preferences.mjs b/common/src/module/preferences.mjs deleted file mode 100644 index 5d20f92..0000000 --- a/common/src/module/preferences.mjs +++ /dev/null @@ -1,40 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ -import { Module } from "./common.mjs" -import General from "../preferences/general.mjs" -import Editor from "../preferences/expression.mjs" -import DefaultGraph from "../preferences/default.mjs" - -/** - * Module for application wide settings. - */ -class PreferencesAPI extends Module { - constructor() { - super("Preferences") - - this.categories = { - [QT_TRANSLATE_NOOP("settingCategory", "general")]: General, - [QT_TRANSLATE_NOOP("settingCategory", "editor")]: Editor, - [QT_TRANSLATE_NOOP("settingCategory", "default")]: DefaultGraph - } - } -} - -/** @type {CanvasAPI} */ -Modules.Preferences = Modules.Preferences || new PreferencesAPI() -export default Modules.Preferences diff --git a/common/src/module/settings.mjs b/common/src/module/settings.mjs deleted file mode 100644 index c740383..0000000 --- a/common/src/module/settings.mjs +++ /dev/null @@ -1,186 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { Module } from "./common.mjs" -import { BaseEvent } from "../events.mjs" -import { HelperInterface } from "./interface.mjs" - - -/** - * Base event for when a setting was changed. - */ -class ChangedEvent extends BaseEvent { - /** - * - * @param {string} property - Name of the property that was chagned - * @param {string|number|boolean} oldValue - Old value of the property - * @param {string|number|boolean} newValue - Current (new) value of the property - * @param {boolean} byUser - True if the user is at the source of the change in the setting. - */ - constructor(property, oldValue, newValue, byUser) { - super("changed") - - this.property = property - this.oldValue = oldValue - this.newValue = newValue - this.byUser = byUser - } -} - -/** - * Module for graph settings. - */ -class SettingsAPI extends Module { - static emits = ["changed"] - - #nonConfigurable = ["saveFilename"] - - /** @type {Map} */ - #properties = new Map([ - ["saveFilename", ""], - ["xzoom", 100], - ["yzoom", 10], - ["xmin", .5], - ["ymax", 25], - ["xaxisstep", "4"], - ["yaxisstep", "4"], - ["xlabel", ""], - ["ylabel", ""], - ["linewidth", 1], - ["textsize", 18], - ["logscalex", true], - ["showxgrad", true], - ["showygrad", true] - ]) - - constructor() { - super("Settings", { - helper: HelperInterface - }) - } - - /** - * - * @param {HelperInterface} helper - */ - initialize({ helper }) { - super.initialize({ helper }) - // Initialize default values. - for(const key of this.#properties.keys()) - if(!this.#nonConfigurable.includes(key)) - this.set(key, helper.getSetting("default_graph."+key), false) - } - - /** - * Sets a setting to a given value - * - * @param {string} property - * @param {string|number|boolean} value - * @param {boolean} byUser - Set to true if the user is at the origin of this change. - */ - set(property, value, byUser) { - if(!this.#properties.has(property)) - throw new Error(`Property ${property} is not a setting.`) - const oldValue = this.#properties.get(property) - const propType = typeof oldValue - if(byUser) - console.debug("Setting", property, "from", oldValue, "to", value, `(${typeof value}, ${byUser})`) - if(propType !== typeof value) - throw new Error(`Value of ${property} must be a ${propType} (${typeof value} provided).`) - this.#properties.set(property, value) - const evt = new ChangedEvent(property, oldValue, value, byUser === true) - this.emit(evt) - } - - /** - * Name of the currently opened file. - * @returns {string} - */ - get saveFilename() { return this.#properties.get("saveFilename") } - - /** - * Zoom on the x axis of the diagram. - * @returns {number} - */ - get xzoom() { return this.#properties.get("xzoom") } - /** - * Zoom on the y axis of the diagram. - * @returns {number} - */ - get yzoom() { return this.#properties.get("yzoom") } - /** - * Minimum x of the diagram. - * @returns {number} - */ - get xmin() { return this.#properties.get("xmin") } - /** - * Maximum y of the diagram. - * @returns {number} - */ - get ymax() { return this.#properties.get("ymax") } - /** - * Step of the x axis graduation (expression). - * @note Only available in non-logarithmic mode. - * @returns {string} - */ - get xaxisstep() { return this.#properties.get("xaxisstep") } - /** - * Step of the y axis graduation (expression). - * @returns {string} - */ - get yaxisstep() { return this.#properties.get("yaxisstep") } - /** - * Label used on the x axis. - * @returns {string} - */ - get xlabel() { return this.#properties.get("xlabel") } - /** - * Label used on the y axis. - * @returns {string} - */ - get ylabel() { return this.#properties.get("ylabel") } - /** - * Width of lines that will be drawn into the canvas. - * @returns {number} - */ - get linewidth() { return this.#properties.get("linewidth") } - /** - * Font size of the text that will be drawn into the canvas. - * @returns {number} - */ - get textsize() { return this.#properties.get("textsize") } - /** - * true if the canvas should be in logarithmic mode, false otherwise. - * @returns {boolean} - */ - get logscalex() { return this.#properties.get("logscalex") } - /** - * true if the x graduation should be shown, false otherwise. - * @returns {boolean} - */ - get showxgrad() { return this.#properties.get("showxgrad") } - /** - * true if the y graduation should be shown, false otherwise. - * @returns {boolean} - */ - get showygrad() { return this.#properties.get("showygrad") } -} - -Modules.Settings = Modules.Settings || new SettingsAPI() -export default Modules.Settings - diff --git a/common/src/objs/autoload.mjs b/common/src/objs/autoload.mjs deleted file mode 100644 index 7ecac04..0000000 --- a/common/src/objs/autoload.mjs +++ /dev/null @@ -1,57 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import Objects from "../module/objects.mjs" -import { DrawableObject } from "./common.mjs" -import Point from "./point.mjs" -import Text from "./text.mjs" -import Function from "./function.mjs" -import BodeMagnitude from "./bodemagnitude.mjs" -import BodePhase from "./bodephase.mjs" -import BodeMagnitudeSum from "./bodemagnitudesum.mjs" -import BodePhaseSum from "./bodephasesum.mjs" -import XCursor from "./xcursor.mjs" -import Sequence from "./sequence.mjs" -import DistributionFunction from "./distribution.mjs" - -/** - * Registers the object obj in the object list. - * @param {DrawableObject} obj - Object to be registered. - */ -function registerObject(obj) { - // Registers an object to be used in LogarithmPlotter. - if(obj.prototype instanceof DrawableObject) { - if(!Objects.types[obj.type()]) - Objects.types[obj.type()] = obj - } else { - console.error("Could not register object " + (obj?.type() ?? obj.constructor.name) + ", as it isn't a DrawableObject.") - } -} - -if(Object.keys(Objects.types).length === 0) { - registerObject(Point) - registerObject(Text) - registerObject(Function) - registerObject(BodeMagnitude) - registerObject(BodePhase) - registerObject(BodeMagnitudeSum) - registerObject(BodePhaseSum) - registerObject(XCursor) - registerObject(Sequence) - registerObject(DistributionFunction) -} diff --git a/common/src/objs/bodemagnitude.mjs b/common/src/objs/bodemagnitude.mjs deleted file mode 100644 index 4dbcd28..0000000 --- a/common/src/objs/bodemagnitude.mjs +++ /dev/null @@ -1,162 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { parseDomain, executeExpression, Expression, EmptySet, Domain } from "../math/index.mjs" -import { CreateNewObject } from "../history/index.mjs" -import * as P from "../parameters.mjs" -import Objects from "../module/objects.mjs" -import Latex from "../module/latex.mjs" -import History from "../module/history.mjs" - -import { ExecutableObject } from "./common.mjs" -import Function from "./function.mjs" - -export default class BodeMagnitude extends ExecutableObject { - static type() { - return "Gain Bode" - } - - static displayType() { - return qsTranslate("bodemagnitude", "Bode Magnitude") - } - - static displayTypeMultiple() { - return qsTranslate("bodemagnitude", "Bode Magnitudes") - } - - static properties() { - return { - [QT_TRANSLATE_NOOP("prop", "om_0")]: new P.ObjectType("Point"), - [QT_TRANSLATE_NOOP("prop", "pass")]: P.Enum.BodePass, - [QT_TRANSLATE_NOOP("prop", "gain")]: new P.Expression(), - [QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position, - [QT_TRANSLATE_NOOP("prop", "labelX")]: "number", - [QT_TRANSLATE_NOOP("prop", "omGraduation")]: "boolean" - } - } - - constructor(name = null, visible = true, color = null, labelContent = "name + value", - om_0 = "", pass = "high", gain = "20", labelPosition = "above", labelX = 1, omGraduation = false) { - if(name == null) name = Objects.getNewName("G") - if(name === "G") name = "G₀" // G is reserved for sum of BODE magnitudes (Somme gains Bode). - super(name, visible, color, labelContent) - if(typeof om_0 == "string") { - // Point name or create one - om_0 = Objects.currentObjectsByName[om_0] - if(om_0 == null) { - // Create new point - om_0 = Objects.createNewRegisteredObject("Point", [Objects.getNewName("ω"), true, this.color, "name"]) - History.addToHistory(new CreateNewObject(om_0.name, "Point", om_0.export())) - om_0.update() - labelPosition = "below" - } - om_0.requiredBy.push(this) - } - /** @type {Point} */ - this.om_0 = om_0 - this.pass = pass - if(typeof gain == "number" || typeof gain == "string") gain = new Expression(gain.toString()) - this.gain = gain - this.labelPosition = labelPosition - this.labelX = labelX - this.omGraduation = omGraduation - } - - getReadableString() { - let pass = this.pass === "low" ? qsTranslate("bodemagnitude", "low-pass") : qsTranslate("bodemagnitude", "high-pass") - return `${this.name}: ${pass}; ${this.om_0.name} = ${this.om_0.x}\n ${" ".repeat(this.name.length)}${this.gain.toString(true)} dB/dec` - } - - getLatexString() { - let pass = this.pass === "low" ? qsTranslate("bodemagnitude", "low-pass") : qsTranslate("bodemagnitude", "high-pass") - return `\\mathrm{${Latex.variable(this.name)}:}\\begin{array}{l} - \\textsf{${pass}};${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup} \\\\ - ${this.gain.latexMarkup}\\textsf{ dB/dec} - \\end{array}` - } - - execute(x = 1) { - if(typeof x == "string") x = executeExpression(x) - if((this.pass === "high" && x < this.om_0.x) || (this.pass === "low" && x > this.om_0.x)) { - let dbfn = new Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`) - return dbfn.execute(x) - } else { - return this.om_0.y.execute() - } - } - - simplify(x = 1) { - let xval = x - if(typeof x == "string") xval = executeExpression(x) - if((this.pass === "high" && xval < this.om_0.x.execute()) || (this.pass === "low" && xval > this.om_0.x.execute())) { - let dbfn = new Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`) - return dbfn.simplify(x) - } else { - return this.om_0.y.toString() - } - } - - canExecute(x = 1) { - return true - } - - draw(canvas) { - let base = [canvas.x2px(this.om_0.x.execute()), canvas.y2px(this.om_0.y.execute())] - let dbfn = new Expression(`${this.gain.execute()}*(log10(x)-log10(${this.om_0.x}))+${this.om_0.y}`) - let inDrawDom - - if(this.pass === "high") { - // High pass, linear line from beginning, then constant to the end. - canvas.drawLine(base[0], base[1], canvas.width, base[1]) - inDrawDom = parseDomain(`]-inf;${this.om_0.x}[`) - } else { - // Low pass, constant from the beginning, linear line to the end. - canvas.drawLine(base[0], base[1], 0, base[1]) - inDrawDom = parseDomain(`]${this.om_0.x};+inf[`) - } - Function.drawFunction(canvas, dbfn, inDrawDom, Domain.R) - // Dashed line representing break in function - let xpos = canvas.x2px(this.om_0.x.execute()) - let dashPxSize = 10 - for(let i = 0; i < canvas.height && this.omGraduation; i += dashPxSize * 2) - canvas.drawLine(xpos, i, xpos, i + dashPxSize) - - // Label - this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) - } - - update() { - super.update() - /** @type {BodeMagnitudeSum[]} */ - let sumObjs = Objects.currentObjects["Somme gains Bode"] - if(sumObjs !== undefined && sumObjs.length > 0) { - sumObjs[0].recalculateCache() - } else { - Objects.createNewRegisteredObject("Somme gains Bode") - } - } - - delete() { - super.delete() - /** @type {BodeMagnitudeSum[]} */ - let sumObjs = Objects.currentObjects["Somme gains Bode"] - if(sumObjs !== undefined && sumObjs.length > 0) { - sumObjs[0].recalculateCache() - } - } -} diff --git a/common/src/objs/bodemagnitudesum.mjs b/common/src/objs/bodemagnitudesum.mjs deleted file mode 100644 index 701a4b9..0000000 --- a/common/src/objs/bodemagnitudesum.mjs +++ /dev/null @@ -1,158 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { Range, Expression, Domain } from "../math/index.mjs" -import * as P from "../parameters.mjs" -import Objects from "../module/objects.mjs" -import Latex from "../module/latex.mjs" - -import { ExecutableObject } from "./common.mjs" -import Function from "./function.mjs" - - -export default class BodeMagnitudeSum extends ExecutableObject { - static type() { - return "Somme gains Bode" - } - - static displayType() { - return qsTranslate("bodemagnitudesum", "Bode Magnitudes Sum") - } - - static displayTypeMultiple() { - return qsTranslate("bodemagnitudesum", "Bode Magnitudes Sum") - } - - static createable() { - return false - } - - static properties() { - return { - [QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position, - [QT_TRANSLATE_NOOP("prop", "labelX")]: "number" - } - } - - constructor(name = null, visible = true, color = null, labelContent = "name + value", - labelPosition = "above", labelX = 1) { - if(name == null) name = "G" - super(name, visible, color, labelContent) - this.labelPosition = labelPosition - this.labelX = labelX - this.recalculateCache() - } - - getReadableString() { - return `${this.name} = ${Objects.getObjectsName("Gain Bode").join(" + ")}` - } - - getLatexString() { - return `${Latex.variable(this.name)} = ${Objects.getObjectsName("Gain Bode").map(name => Latex.variable(name)).join(" + ")}` - } - - execute(x = 0) { - for(let [limitedDrawFunction, inDrawDom] of this.cachedParts) { - if(inDrawDom.includes(x)) { - return limitedDrawFunction.execute(x) - } - } - return null - } - - canExecute(x = 1) { - return true // Should always be true - } - - simplify(x = 1) { - for(let [limitedDrawFunction, inDrawDom] of this.cachedParts) { - if(inDrawDom.includes(x)) { - return limitedDrawFunction.simplify(x) - } - } - return "" - } - - recalculateCache() { - this.cachedParts = [] - // Calculating this is fairly resource expansive so it's cached. - let magnitudeObjects = Objects.currentObjects["Gain Bode"] - if(magnitudeObjects === undefined || magnitudeObjects.length < 1) { - Objects.deleteObject(this.name) - } else { - console.log("Recalculating cache gain") - // Minimum to draw (can be expended if needed, just not infinite or it'll cause issues. - const MIN_DRAW = 1e-20 - // Format: [[x value of where the filter transitions, magnitude, high-pass (bool)]] - const magnitudes = [] - const XVALUE = 0 - const MAGNITUDE = 1 - const PASS = 2 - magnitudes.push([Number.MAX_VALUE, 0, true]) // Draw the ending section - // Collect data from current magnitude (or gain in French) objects. - let baseY = 0 - for(/** @type {BodeMagnitude} */ let magnitudeObj of magnitudeObjects) { // Sorting by their om_0 position. - const om0x = magnitudeObj.om_0.x.execute() - magnitudes.push([om0x, magnitudeObj.gain.execute(), magnitudeObj.pass === "high"]) - baseY += magnitudeObj.execute(MIN_DRAW) - } - // Sorting the data by their x transitions value - magnitudes.sort((a, b) => a[XVALUE] - b[XVALUE]) - // Adding the total gains. - let magnitudesBeforeTransition = [] - let magnitudesAfterTransition = [] - let totalMagnitudeAtStart = 0 // Magnitude at the lowest x value (sum of all high-pass magnitudes) - for(let [om0x, magnitude, highpass] of magnitudes) { - if(highpass) { - magnitudesBeforeTransition.push(magnitude) - magnitudesAfterTransition.push(0) - totalMagnitudeAtStart += magnitude - } else { - magnitudesBeforeTransition.push(0) - magnitudesAfterTransition.push(magnitude) - } - } - // Calculating parts - let previousTransitionX = MIN_DRAW - let currentMagnitude = totalMagnitudeAtStart - for(let transitionID = 0; transitionID < magnitudes.length; transitionID++) { - const transitionX = magnitudes[transitionID][XVALUE] - // Create draw function that will be used during drawing. - const limitedDrawFunction = new Expression(`${currentMagnitude}*(ln(x)-ln(${previousTransitionX}))/ln(10)+${baseY}`) - const drawDomain = new Range(previousTransitionX, transitionX, true, false) - this.cachedParts.push([limitedDrawFunction, drawDomain]) - // Prepare default values for next function. - previousTransitionX = transitionX - baseY = limitedDrawFunction.execute(transitionX) - currentMagnitude += magnitudesAfterTransition[transitionID] - magnitudesBeforeTransition[transitionID] - } - } - } - - draw(canvas) { - if(this.cachedParts.length > 0) { - for(let [limitedDrawFunction, drawDomain] of this.cachedParts) { - Function.drawFunction(canvas, limitedDrawFunction, drawDomain, Domain.R) - // Check if necessary to draw label - if(drawDomain.includes(this.labelX)) { - this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) - } - } - } - } -} diff --git a/common/src/objs/bodephase.mjs b/common/src/objs/bodephase.mjs deleted file mode 100644 index 75776b1..0000000 --- a/common/src/objs/bodephase.mjs +++ /dev/null @@ -1,147 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { executeExpression, Expression } from "../math/index.mjs" -import { CreateNewObject } from "../history/index.mjs" -import * as P from "../parameters.mjs" -import Objects from "../module/objects.mjs" -import History from "../module/history.mjs" -import Latex from "../module/latex.mjs" - -import { ExecutableObject } from "./common.mjs" - - -export default class BodePhase extends ExecutableObject { - static type() { - return "Phase Bode" - } - - static displayType() { - return qsTranslate("bodephase", "Bode Phase") - } - - static displayTypeMultiple() { - return qsTranslate("bodephase", "Bode Phases") - } - - static properties() { - return { - [QT_TRANSLATE_NOOP("prop", "om_0")]: new P.ObjectType("Point"), - [QT_TRANSLATE_NOOP("prop", "phase")]: new P.Expression(), - [QT_TRANSLATE_NOOP("prop", "unit")]: new P.Enum("°", "deg", "rad"), - [QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position, - [QT_TRANSLATE_NOOP("prop", "labelX")]: "number" - } - } - - constructor(name = null, visible = true, color = null, labelContent = "name + value", - om_0 = "", phase = 90, unit = "°", labelPosition = "above", labelX = 1) { - if(name == null) name = Objects.getNewName("φ") - if(name === "φ") name = "φ₀" // φ is reserved for sum of Bode phases. - super(name, visible, color, labelContent) - if(typeof phase == "number" || typeof phase == "string") phase = new Expression(phase.toString()) - this.phase = phase - if(typeof om_0 == "string") { - // Point name or create one - om_0 = Objects.currentObjectsByName[om_0] - if(om_0 == null) { - // Create new point - om_0 = Objects.createNewRegisteredObject("Point", [Objects.getNewName("ω"), this.color, "name"]) - om_0.labelPosition = this.phase.execute() >= 0 ? "above" : "below" - History.addToHistory(new CreateNewObject(om_0.name, "Point", om_0.export())) - labelPosition = "below" - } - om_0.requiredBy.push(this) - } - /** @type {Point} */ - this.om_0 = om_0 - this.unit = unit - this.labelPosition = labelPosition - this.labelX = labelX - } - - getReadableString() { - return `${this.name}: ${this.phase.toString(true)}${this.unit} at ${this.om_0.name} = ${this.om_0.x}` - } - - getLatexString() { - return `${Latex.variable(this.name)}: ${this.phase.latexMarkup}\\textsf{${this.unit} at }${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup}` - } - - execute(x = 1) { - if(typeof x == "string") x = executeExpression(x) - if(x < this.om_0.x) { - return this.om_0.y.execute() - } else { - return this.om_0.y.execute() + this.phase.execute() - } - } - - simplify(x = 1) { - let xval = x - if(typeof x == "string") xval = executeExpression(x) - if(xval < this.om_0.x) { - return this.om_0.y.toString() - } else { - let newExp = this.om_0.y.toEditableString() + " + " + this.phase.toEditableString() - return new Expression(newExp) - } - } - - canExecute(x = 1) { - return true - } - - draw(canvas) { - let baseX = canvas.x2px(this.om_0.x.execute()) - let omy = this.om_0.y.execute() - let augmt = this.phase.execute() - let baseY = canvas.y2px(omy) - let augmtY = canvas.y2px(omy + augmt) - // Before change line. - canvas.drawLine(0, baseY, Math.min(baseX, canvas.height), baseY) - // Transition line. - canvas.drawLine(baseX, baseY, baseX, augmtY) - // After change line - canvas.drawLine(Math.max(0, baseX), augmtY, canvas.width, augmtY) - - // Label - this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) - } - - update() { - super.update() - /** @type {BodePhaseSum[]} */ - let sumObjs = Objects.currentObjects["Somme phases Bode"] - if(sumObjs !== undefined && sumObjs.length > 0) { - sumObjs[0].recalculateCache() - } else { - Objects.createNewRegisteredObject("Somme phases Bode") - } - } - - delete() { - super.update() - /** @type {BodePhaseSum[]} */ - let sumObjs = Objects.currentObjects["Somme phases Bode"] - if(sumObjs !== undefined && sumObjs.length > 0) { - sumObjs[0].recalculateCache() - } - } -} - diff --git a/common/src/objs/bodephasesum.mjs b/common/src/objs/bodephasesum.mjs deleted file mode 100644 index 55b3b6c..0000000 --- a/common/src/objs/bodephasesum.mjs +++ /dev/null @@ -1,139 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { executeExpression, Expression } from "../math/index.mjs" -import * as P from "../parameters.mjs" -import Objects from "../module/objects.mjs" -import Latex from "../module/latex.mjs" - -import { ExecutableObject } from "./common.mjs" - -export default class BodePhaseSum extends ExecutableObject { - static type() { - return "Somme phases Bode" - } - - static displayType() { - return qsTranslate("bodephasesum", "Bode Phases Sum") - } - - static displayTypeMultiple() { - return qsTranslate("bodephasesum", "Bode Phases Sum") - } - - static createable() { - return false - } - - static properties() { - return { - [QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position, - [QT_TRANSLATE_NOOP("prop", "labelX")]: "number" - } - } - - constructor(name = null, visible = true, color = null, labelContent = "name + value", - labelPosition = "above", labelX = 1) { - if(name == null) name = "φ" - super(name, visible, color, labelContent) - this.labelPosition = labelPosition - this.labelX = labelX - this.recalculateCache() - } - - getReadableString() { - return `${this.name} = ${Objects.getObjectsName("Phase Bode").join(" + ")}` - } - - getLatexString() { - return `${Latex.variable(this.name)} = ${Objects.getObjectsName("Phase Bode").map(name => Latex.variable(name)).join(" + ")}` - } - - execute(x = 1) { - if(typeof x == "string") x = executeExpression(x) - for(let i = 0; i < this.om0xList.length - 1; i++) { - if(x >= this.om0xList[i] && x < this.om0xList[i + 1]) return this.phasesList[i] - } - } - - simplify(x = 1) { - let xval = x - if(typeof x == "string") xval = executeExpression(x) - for(let i = 0; i < this.om0xList.length - 1; i++) { - if(xval >= this.om0xList[i] && xval < this.om0xList[i + 1]) { - return (new Expression(this.phasesExprList[i])).simplify() - } - } - return "0" - } - - canExecute(x = 1) { - return true - } - - recalculateCache() { - // Minimum to draw (can be expended if needed, just not infinite or it'll cause issues. - let drawMin = 1e-20 - let drawMax = 1e20 - this.om0xList = [drawMin, drawMax] - this.phasesList = [0] - this.phasesExprList = ["0"] - let phasesDict = new Map() - let phasesExprDict = new Map() - phasesDict.set(drawMax, 0) - - let phaseObjects = Objects.currentObjects["Phase Bode"] - if(phaseObjects === undefined || phaseObjects.length < 1) { - Objects.deleteObject(this.name) - } else { - console.log("Recalculating cache phase") - for(/** @type {BodePhase} */ let obj of phaseObjects) { - this.om0xList.push(obj.om_0.x.execute()) - if(!phasesDict.has(obj.om_0.x.execute())) { - phasesDict.set(obj.om_0.x.execute(), obj.phase.execute()) - phasesExprDict.set(obj.om_0.x.execute(), obj.phase.toEditableString()) - } else { - phasesDict.set(obj.om_0.x.execute(), obj.phase.execute() + phasesDict.get(obj.om_0.x.execute())) - phasesExprDict.set(obj.om_0.x.execute(), obj.phase.toEditableString() + "+" + phasesExprDict.get(obj.om_0.x.execute())) - } - this.phasesList[0] += obj.om_0.y.execute() - this.phasesExprList[0] += "+" + obj.om_0.y.toEditableString() - } - this.om0xList.sort((a, b) => a - b) - for(let i = 1; i < this.om0xList.length; i++) { - this.phasesList[i] = this.phasesList[i - 1] + phasesDict.get(this.om0xList[i]) - this.phasesExprList[i] = this.phasesExprList[i - 1] + "+" + phasesDict.get(this.om0xList[i]) - } - } - } - - draw(canvas) { - for(let i = 0; i < this.om0xList.length - 1; i++) { - let om0xBegin = canvas.x2px(this.om0xList[i]) - let om0xEnd = canvas.x2px(this.om0xList[i + 1]) - let baseY = canvas.y2px(this.phasesList[i]) - let nextY = canvas.y2px(this.phasesList[i + 1]) - canvas.drawLine(om0xBegin, baseY, om0xEnd, baseY) - canvas.drawLine(om0xEnd, baseY, om0xEnd, nextY) - } - - // Label - this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) - } -} - diff --git a/common/src/objs/common.mjs b/common/src/objs/common.mjs deleted file mode 100644 index 602856a..0000000 --- a/common/src/objs/common.mjs +++ /dev/null @@ -1,418 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import Objects from "../module/objects.mjs" -import Latex from "../module/latex.mjs" -import { getRandomColor } from "../utils/index.mjs" -import { ensureTypeSafety, serializesByPropertyType } from "../parameters.mjs" - -// This file contains the default data to be imported from all other objects - -/** - * Class to extend for every type of object that - * can be drawn on the canvas. - */ -export class DrawableObject { - /** - * Base name of the object. Needs to be constant over time. - * @return {string} Type of the object. - */ - static type() { - return "Unknown" - } - - /** - * Translated name of the object to be shown to the user. - * @return {string} - */ - static displayType() { - return "Unknown" - } - - /** - * Translated name of the object in plural form to be shown to the user. - * @return {string} - */ - static displayTypeMultiple() { - return "Unknowns" - } - - /** - * True if this object can be created by the user, false if it can only - * be instantiated by other objects - * @return {boolean} - */ - static createable() { - return true - } - - /** - * List of properties used in the Object Editor. - * - * Properties are set with key as property name and - * value as it's type name (e.g 'numbers', 'string'...), - * an Enum for enumerations, an ObjectType for DrawableObjects - * with a specific type, a List instance for lists, a - * Dictionary instance for dictionaries, an Expression for expressions... - * - * If the property is to be translated, the key should be passed - * through the QT_TRANSLATE_NOOP macro in that form: - * [QT_TRANSLATE_NOOP('prop','key')] - * Enums that are translated should be indexed in parameters.mjs and - * then be linked directly here. - * - * @return {Object.} - */ - static properties() { - return {} - } - - /** - * True if this object can be executed, so that an y value might be computed - * for an x value. Only ExecutableObjects have that property set to true. - * @return {boolean} - */ - 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. - * @param {string} name - Name of the object - * @param {boolean} visible - true if the object is visible, false otherwise. - * @param {color|string} color - Color of the object (can be string or QColor) - * @param {"null"|"name"|"name + value"} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label. - * @constructor - */ - constructor(name, visible = true, color = null, labelContent = "name + value") { - if(color == null) color = getRandomColor() - this.type = this.constructor.type() - this.name = name - this.visible = visible - this.color = color - this.labelContent = labelContent // "null", "name", "name + value" - this.requiredBy = [] - this.requires = [] - } - - /** - * Serializes the object in an array that can be JSON serialized. - * These parameters will be re-entered in the constructor when restored. - * @return {array} - */ - export() { - let exportList = [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 - } - - /** - * String representing the object that will be displayed to the user. - * It allows for 2 lines and translated text, but it shouldn't be too long. - * @return {string} - */ - getReadableString() { - return `${this.name} = Unknown` - } - - /** - * Latex markuped version of the readable string. - * Every non latin character should be passed as latex symbols and formulas - * should be in latex form. - * See ../latex.mjs for helper methods. - * @return {string} - */ - getLatexString() { - return this.getReadableString() - } - - /** - * Readable string content of the label depending on the value of the latexContent. - * @return {string} - */ - getLabel() { - switch(this.labelContent) { - case "name": - return this.name - case "name + value": - return this.getReadableString() - case "null": - return "" - - } - } - - /** - * 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 - * should be in latex form. - * See ../latex.mjs for helper methods. - * @return {string} - */ - getLatexLabel() { - switch(this.labelContent) { - case "name": - return Latex.variable(this.name) - case "name + value": - return this.getLatexString() - case "null": - return "" - - } - } - - /** - * Returns the recursive list of objects this one depends on. - * @return {array} - */ - getDependenciesList() { - let dependencies = this.requires.map(obj => obj) - for(let obj of this.requires) - dependencies = dependencies.concat(obj.getDependenciesList()) - return dependencies - } - - /** - * Callback method when one of the properties of the object is updated. - */ - update() { - // Refreshing dependencies. - for(let obj of this.requires) - obj.requiredBy = obj.requiredBy.filter(dep => dep !== this) - // Checking objects this one depends on - this.requires = [] - let currentObjectsByName = Objects.currentObjectsByName - let properties = this.constructor.properties() - for(let property in properties) - if(typeof properties[property] == "object" && "type" in properties[property]) - if(properties[property].type === "Expression" && this[property] != null) { - // Expressions with dependencies - for(let objName of this[property].requiredObjects()) { - if(objName in currentObjectsByName && !this.requires.includes(currentObjectsByName[objName])) { - this.requires.push(currentObjectsByName[objName]) - currentObjectsByName[objName].requiredBy.push(this) - } - } - if(this[property].canBeCached && this[property].requiredObjects().length > 0) - // Recalculate - this[property].recache() - - } else if(properties[property].type === "ObjectType" && this[property] != null) { - // Object dependency - this.requires.push(this[property]) - this[property].requiredBy.push(this) - } - // Updating objects dependent on this one - for(let req of this.requiredBy) - req.update() - } - - /** - * Callback method when the object is about to get deleted. - */ - delete() { - for(let toRemove of this.requiredBy) { // Normally, there should be none here, but better leave nothing just in case. - 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 canvas with the. - * @param {CanvasAPI} canvas - */ - draw(canvas) { - } - - /** - * Applicates a drawFunction with two position arguments depending on - * both the posX and posY of where the label should be displayed, - * and the labelPosition which declares the label should be displayed - * relatively to that 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 {{width: number, height: number}} size - Size of the label item, containing two properties, "width" and "height" - * @param {number} posX - Component of the marked position on the x-axis - * @param {number} 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 - */ - drawPositionDivergence(labelPosition, offset, size, posX, posY, drawFunction) { - switch(labelPosition) { - case "center": - drawFunction(posX - size.width / 2, posY - size.height / 2) - break - case "top": - case "above": - drawFunction(posX - size.width / 2, posY - size.height - offset) - break - case "bottom": - case "below": - drawFunction(posX - size.width / 2, posY + offset) - break - case "left": - drawFunction(posX - size.width - offset, posY - size.height / 2) - break - case "right": - drawFunction(posX + offset, posY - size.height / 2) - break - case "top-left": - case "above-left": - drawFunction(posX - size.width, posY - size.height - offset) - break - case "top-right": - case "above-right": - drawFunction(posX + offset, posY - size.height - offset) - break - case "bottom-left": - case "below-left": - drawFunction(posX - size.width - offset, posY + offset) - break - case "bottom-right": - case "below-right": - drawFunction(posX + offset, posY + offset) - break - } - } - - /** - * Automatically draw text (by default the label of the object on the canvas - * depending on user settings. - * This method takes into account both the posX and posY of where the label - * should be displayed, including the labelPosition relative to it. - * The text is get both through the getLatexFunction and getTextFunction - * depending on whether to use latex. - * Then, it's displayed using the drawFunctionLatex (x,y,imageData) and - * drawFunctionText (x,y,text) depending on whether to use latex. - * - * @param {CanvasAPI} canvas - * @param {string|Enum} labelPosition - Position of the label relative to the marked position - * @param {number} posX - Component of the marked position on the x-axis - * @param {number} posY - Component of the marked position on the y-axis - * @param {boolean} forceText - Force the rendering of the label as text - * @param {function|null} getLatexFunction - Function (no argument) to get the latex markup to be displayed - * @param {function|null} getTextFunction - Function (no argument) to get the text to be displayed - * @param {function|null} drawFunctionLatex - Function (x,y,imageData) to display the latex image - * @param {function|null} drawFunctionText - Function (x,y,text,textSize) to display the text - */ - drawLabel(canvas, labelPosition, posX, posY, forceText = false, - getLatexFunction = null, getTextFunction = null, drawFunctionLatex = null, drawFunctionText = null) { - // Default functions - if(getLatexFunction == null) - getLatexFunction = this.getLatexLabel.bind(this) - if(getTextFunction == null) - getTextFunction = this.getLabel.bind(this) - if(drawFunctionLatex == null) - drawFunctionLatex = (x, y, ltxImg) => canvas.drawVisibleImage(ltxImg.source, x, y, ltxImg.width, ltxImg.height) - if(drawFunctionText == null) - drawFunctionText = (x, y, text, textSize) => canvas.drawVisibleText(text, x, y + textSize.height) // Positioned from left bottom - // Drawing the label - if(!forceText && Latex.enabled) { - // With latex - let drawLblCb = ((ltxImg) => { - this.drawPositionDivergence(labelPosition, 8 + canvas.linewidth / 2, ltxImg, posX, posY, (x, y) => drawFunctionLatex(x, y, ltxImg)) - }) - let ltxLabel = getLatexFunction() - if(ltxLabel !== "") - canvas.renderLatexImage(ltxLabel, this.color, drawLblCb) - } else { - // Without latex - let text = getTextFunction() - canvas.font = `${canvas.textsize}px sans-serif` - let textSize = canvas.measureText(text) - this.drawPositionDivergence(labelPosition, 8 + canvas.linewidth / 2, textSize, posX, posY, (x, y) => drawFunctionText(x, y, text, textSize)) - } - } - - toString() { - return this.name - } -} - - -/** - * Class to be extended for every object on which - * an y for a x can be computed with the execute function. - * If a value cannot be found during execute, it will - * return null. However, theses same x values will - * return false when passed to canExecute. - */ -export class ExecutableObject extends DrawableObject { - /** - * Returns the corresponding y value for an x value. - * If the object isn't defined on the given x, then - * this function will return null. - * - * @param {number} x - * @returns {number|null} - */ - execute(x = 1) { - return 0 - } - - /** - * Returns false if the object isn't defined on the given x, true otherwise. - * - * @param {number} x - * @returns {boolean} - */ - canExecute(x = 1) { - return true - } - - /** - * Returns the simplified expression string for a given x. - * - * @param {number} x - * @returns {string|Expression} - */ - simplify(x = 1) { - return "0" - } - - /** - * True if this object can be executed, so that an y value might be computed - * for an x value. Only ExecutableObjects have that property set to true. - * @return {boolean} - */ - static executable() { - return true - } -} - - - - diff --git a/common/src/objs/distribution.mjs b/common/src/objs/distribution.mjs deleted file mode 100644 index b63717c..0000000 --- a/common/src/objs/distribution.mjs +++ /dev/null @@ -1,159 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import * as P from "../parameters.mjs" -import Objects from "../module/objects.mjs" -import Latex from "../module/latex.mjs" - -import { ExecutableObject } from "./common.mjs" - - -export default class DistributionFunction extends ExecutableObject { - static type() { - return "Repartition" - } - - static displayType() { - return qsTranslate("distribution", "Repartition") - } - - static displayTypeMultiple() { - return qsTranslate("distribution", "Repartition functions") - } - - static properties() { - return { - "comment1": QT_TRANSLATE_NOOP( - "comment", - "Note: Specify the probability for each value." - ), - [QT_TRANSLATE_NOOP("prop", "probabilities")]: new P.Dictionary("string", "double", /^-?[\d.,]+$/, /^-?[\d.,]+$/, "P({name_} = ", ") = "), - [QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position, - [QT_TRANSLATE_NOOP("prop", "labelX")]: "number" - } - } - - static import(name, visible, color, labelContent, ...args) { - 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", - probabilities = { "0": "0" }, labelPosition = "above", labelX = 1) { - if(name == null) name = Objects.getNewName("XYZUVW", "F_") - super(name, visible, color, labelContent) - this.probabilities = probabilities - this.labelPosition = labelPosition - this.labelX = labelX - this.update() - } - - getReadableString() { - let keys = Object.keys(this.probabilities).sort((a, b) => a - b) - let varname = this.name.substring(this.name.indexOf("_") + 1) - return `${this.name}(x) = P(${varname} ≤ x)\n` + keys.map(idx => `P(${varname}=${idx})=${this.probabilities[idx]}`).join("; ") - } - - getLatexString() { - let keys = Object.keys(this.probabilities).sort((a, b) => a - b) - let funcName = Latex.variable(this.name) - let varName = Latex.variable(this.name.substring(this.name.indexOf("_") + 1)) - return `\\begin{array}{l}{${funcName}}(x) = P(${varName} \\le x)\\\\` + keys.map(idx => `P(${varName}=${idx})=${this.probabilities[idx]}`).join("; ") + "\\end{array}" - } - - execute(x = 1) { - let ret = 0 - Object.keys(this.probabilities).sort((a, b) => a - b).forEach(idx => { - if(x >= idx) ret += parseFloat(this.probabilities[idx].replace(/,/g, ".")) - }) - return ret - } - - canExecute(x = 1) { - return true - } - - // Simplify returns the simplified string of the expression. - simplify(x = 1) { - return this.execute(x).toString() - } - - getLabel() { - switch(this.labelContent) { - case "name": - return `${this.name}(x)` - case "name + value": - return this.getReadableString() - case "null": - return "" - } - } - - draw(canvas) { - let currentY = 0 - let keys = Object.keys(this.probabilities).map(idx => parseInt(idx)).sort((a, b) => a - b) - if(canvas.isVisible(keys[0], this.probabilities[keys[0]].replace(/,/g, "."))) { - canvas.drawLine(0, canvas.y2px(0), canvas.x2px(keys[0]), canvas.y2px(0)) - if(canvas.isVisible(keys[0], 0)) { - canvas.arc(canvas.x2px(keys[0]) + 4, canvas.y2px(0), 4, Math.PI / 2, 3 * Math.PI / 2) - } - } - for(let i = 0; i < keys.length - 1; i++) { - let idx = keys[i] - currentY += parseFloat(this.probabilities[idx].replace(/,/g, ".")) - if(canvas.isVisible(idx, currentY) || canvas.isVisible(keys[i + 1], currentY)) { - canvas.drawLine( - Math.max(0, canvas.x2px(idx)), - canvas.y2px(currentY), - Math.min(canvas.width, canvas.x2px(keys[i + 1])), - canvas.y2px(currentY) - ) - if(canvas.isVisible(idx, currentY)) { - canvas.disc(canvas.x2px(idx), canvas.y2px(currentY), 4) - } - if(canvas.isVisible(keys[i + 1], currentY)) { - canvas.arc(canvas.x2px(keys[i + 1]) + 4, canvas.y2px(currentY), 4, Math.PI / 2, 3 * Math.PI / 2) - } - } - } - if(canvas.isVisible(keys[keys.length - 1], currentY + parseFloat(this.probabilities[keys[keys.length - 1]]))) { - canvas.drawLine( - Math.max(0, canvas.x2px(keys[keys.length - 1])), - canvas.y2px(currentY + parseFloat(this.probabilities[keys[keys.length - 1]].replace(/,/g, "."))), - canvas.width, - canvas.y2px(currentY + parseFloat(this.probabilities[keys[keys.length - 1]].replace(/,/g, "."))) - ) - canvas.disc( - canvas.x2px(keys[keys.length - 1]), - canvas.y2px( - currentY + parseFloat(this.probabilities[keys[keys.length - 1]].replace(/,/g, ".")) - ), - 4 - ) - } - - // Label - this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) - } -} - diff --git a/common/src/objs/function.mjs b/common/src/objs/function.mjs deleted file mode 100644 index fec90ac..0000000 --- a/common/src/objs/function.mjs +++ /dev/null @@ -1,203 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { textsub } from "../utils/index.mjs" -import Objects from "../module/objects.mjs" -import { ExecutableObject } from "./common.mjs" -import { parseDomain, Expression, SpecialDomain } from "../math/index.mjs" -import * as P from "../parameters.mjs" -import Latex from "../module/latex.mjs" - - -export default class Function extends ExecutableObject { - static type() { - return "Function" - } - - static displayType() { - return qsTranslate("function", "Function") - } - - static displayTypeMultiple() { - return qsTranslate("function", "Functions") - } - - static properties() { - return { - [QT_TRANSLATE_NOOP("prop", "expression")]: new P.Expression("x"), - [QT_TRANSLATE_NOOP("prop", "definitionDomain")]: "Domain", - [QT_TRANSLATE_NOOP("prop", "destinationDomain")]: "Domain", - "comment1": QT_TRANSLATE_NOOP( - "comment", - "Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5}" - ), - [QT_TRANSLATE_NOOP("prop", "displayMode")]: P.Enum.FunctionDisplayType, - [QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position, - [QT_TRANSLATE_NOOP("prop", "labelX")]: "number", - "comment2": QT_TRANSLATE_NOOP( - "comment", - "The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...)" - ), - [QT_TRANSLATE_NOOP("prop", "drawPoints")]: "boolean", - [QT_TRANSLATE_NOOP("prop", "drawDashedLines")]: "boolean" - } - } - - constructor(name = null, visible = true, color = null, labelContent = "name + value", - expression = "x", definitionDomain = "RPE", destinationDomain = "R", - displayMode = "application", labelPosition = "above", labelX = 1, - drawPoints = true, drawDashedLines = true) { - if(name == null) name = Objects.getNewName("fghjqlmnopqrstuvwabcde") - super(name, visible, color, labelContent) - if(typeof expression == "number" || typeof expression == "string") expression = new Expression(expression.toString()) - this.expression = expression - if(typeof definitionDomain == "string") definitionDomain = parseDomain(definitionDomain) - this.definitionDomain = definitionDomain - if(typeof destinationDomain == "string") destinationDomain = parseDomain(destinationDomain) - this.destinationDomain = destinationDomain - this.displayMode = displayMode - this.labelPosition = labelPosition - this.labelX = labelX - this.drawPoints = drawPoints - this.drawDashedLines = drawDashedLines - } - - getReadableString() { - if(this.displayMode === "application") { - return `${this.name}: ${this.definitionDomain} ⟶ ${this.destinationDomain}\n ${" ".repeat(this.name.length)}x ⟼ ${this.expression.toString()}` - } else { - return `${this.name}(x) = ${this.expression.toString()}\nD${textsub(this.name)} = ${this.definitionDomain}` - } - } - - getLatexString() { - if(this.displayMode === "application") { - return `${Latex.variable(this.name)}:\\begin{array}{llll}${this.definitionDomain.latexMarkup}\\textrm{ } & \\rightarrow & \\textrm{ }${this.destinationDomain.latexMarkup}\\\\ - x\\textrm{ } & \\mapsto & \\textrm{ }${this.expression.latexMarkup}\\end{array}` - } else { - return `\\begin{array}{l}${Latex.variable(this.name)}(x) = ${this.expression.latexMarkup}\\\\ D_{${this.name}} = ${this.definitionDomain.latexMarkup}\\end{array}` - } - } - - execute(x = 1) { - if(this.definitionDomain.includes(x)) - return this.expression.execute(x) - return null - } - - canExecute(x = 1) { - return this.definitionDomain.includes(x) - } - - simplify(x = 1) { - if(this.definitionDomain.includes(x)) - return this.expression.simplify(x) - return "" - } - - draw(canvas) { - Function.drawFunction(canvas, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines) - // Label - this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) - } - - /** - * Reusable in other objects. - * Drawing small traits every few pixels - */ - static drawFunction(canvas, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) { - let pxprecision = 10 - const startDrawFrom = canvas.x2px(1)%pxprecision-pxprecision - let previousX = canvas.px2x(startDrawFrom) - // console.log("Starting draw from", previousX, startDrawFrom, canvas.x2px(1)) - let previousY = null - if(definitionDomain instanceof SpecialDomain && definitionDomain.moveSupported) { - // Point based functions. - previousX = definitionDomain.next(previousX) - if(previousX === null) previousX = definitionDomain.next(canvas.px2x(0)) - previousY = expr.execute(previousX) - if(!drawPoints && !drawDash) return - while(previousX !== null && canvas.x2px(previousX) < canvas.width) { - // Reconverted for canvas to fix for logarithmic scales. - let currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX) + pxprecision)) - let currentY = expr.execute(currentX) - if(currentX === null) break - if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) && - (destinationDomain.includes(currentY) || destinationDomain.includes(previousY))) { - if(drawDash) - canvas.drawDashedLine(canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY)) - if(drawPoints) { - canvas.fillRect(canvas.x2px(previousX) - 5, canvas.y2px(previousY) - 1, 10, 2) - canvas.fillRect(canvas.x2px(previousX) - 1, canvas.y2px(previousY) - 5, 2, 10) - } - } - previousX = currentX - previousY = currentY - } - if(drawPoints) { - // Drawing the last cross - canvas.fillRect(canvas.x2px(previousX) - 5, canvas.y2px(previousY) - 1, 10, 2) - canvas.fillRect(canvas.x2px(previousX) - 1, canvas.y2px(previousY) - 5, 2, 10) - } - } else { - // Use max precision if function is trigonometrical on log scale. - let exprString = expr.expr - if(exprString.includes("sin") || exprString.includes("cos") || exprString.includes("tan")) - pxprecision = (canvas.logscalex || exprString.includes("tan")) ? 1 : 3 - // Calculate the previousY at the start of the canvas - if(definitionDomain.includes(previousX)) - previousY = expr.execute(previousX) - for(let px = pxprecision; px-pxprecision < 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) && currentX > 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)) - } - } - previousY = currentY - } else { - previousY = null // Last y was invalid, so let's not draw anything from it. - } - previousX = canvas.px2x(px) - } - } - } -} diff --git a/common/src/objs/point.mjs b/common/src/objs/point.mjs deleted file mode 100644 index 841308e..0000000 --- a/common/src/objs/point.mjs +++ /dev/null @@ -1,87 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { Expression } from "../math/index.mjs" -import * as P from "../parameters.mjs" -import Objects from "../module/objects.mjs" -import Latex from "../module/latex.mjs" - -import { DrawableObject } from "./common.mjs" - - -export default class Point extends DrawableObject { - static type() { - return "Point" - } - - static displayType() { - return qsTranslate("point", "Point") - } - - static displayTypeMultiple() { - return qsTranslate("point", "Points") - } - - static properties() { - return { - [QT_TRANSLATE_NOOP("prop", "x")]: new P.Expression(), - [QT_TRANSLATE_NOOP("prop", "y")]: new P.Expression(), - [QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position, - [QT_TRANSLATE_NOOP("prop", "pointStyle")]: new P.Enum("●", "✕", "+") - } - } - - constructor(name = null, visible = true, color = null, labelContent = "name + value", - x = 1, y = 0, labelPosition = "above", pointStyle = "●") { - if(name == null) name = Objects.getNewName("ABCDEFJKLMNOPQRSTUVW") - super(name, visible, color, labelContent) - if(typeof x == "number" || typeof x == "string") x = new Expression(x.toString()) - this.x = x - if(typeof y == "number" || typeof y == "string") y = new Expression(y.toString()) - this.y = y - this.labelPosition = labelPosition - this.pointStyle = pointStyle - } - - getReadableString() { - return `${this.name} = (${this.x}, ${this.y})` - } - - getLatexString() { - return `${Latex.variable(this.name)} = \\left(${this.x.latexMarkup}, ${this.y.latexMarkup}\\right)` - } - - draw(canvas) { - let [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())] - let pointSize = 8 + (canvas.linewidth * 2) - switch(this.pointStyle) { - case "●": - canvas.disc(canvasX, canvasY, pointSize / 2) - break - case "✕": - canvas.drawLine(canvasX - pointSize / 2, canvasY - pointSize / 2, canvasX + pointSize / 2, canvasY + pointSize / 2) - canvas.drawLine(canvasX - pointSize / 2, canvasY + pointSize / 2, canvasX + pointSize / 2, canvasY - pointSize / 2) - break - case "+": - canvas.fillRect(canvasX - pointSize / 2, canvasY - 1, pointSize, 2) - canvas.fillRect(canvasX - 1, canvasY - pointSize / 2, 2, pointSize) - break - } - this.drawLabel(canvas, this.labelPosition, canvasX, canvasY) - } -} diff --git a/common/src/objs/sequence.mjs b/common/src/objs/sequence.mjs deleted file mode 100644 index b424ac0..0000000 --- a/common/src/objs/sequence.mjs +++ /dev/null @@ -1,140 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { Sequence as MathSequence, Domain } from "../math/index.mjs" -import * as P from "../parameters.mjs" -import Latex from "../module/latex.mjs" -import Objects from "../module/objects.mjs" - -import { ExecutableObject } from "./common.mjs" -import Function from "./function.mjs" - - -export default class Sequence extends ExecutableObject { - static type() { - return "Sequence" - } - - static displayType() { - return qsTranslate("sequence", "Sequence") - } - - static displayTypeMultiple() { - return qsTranslate("sequence", "Sequences") - } - - static properties() { - return { - [QT_TRANSLATE_NOOP("prop", "drawPoints")]: "boolean", - [QT_TRANSLATE_NOOP("prop", "drawDashedLines")]: "boolean", - [QT_TRANSLATE_NOOP("prop", "defaultExpression")]: new P.Dictionary("string", "int", /^.+$/, /^\d+$/, "{name}[n+", "] = ", true), - "comment1": QT_TRANSLATE_NOOP( - "comment", - "Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁..." - ), - [QT_TRANSLATE_NOOP("prop", "baseValues")]: new P.Dictionary("string", "int", /^.+$/, /^\d+$/, "{name}[", "] = "), - [QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position, - [QT_TRANSLATE_NOOP("prop", "labelX")]: "number" - } - } - - constructor(name = null, visible = true, color = null, labelContent = "name + value", - drawPoints = true, drawDashedLines = true, defaultExp = { 1: "n" }, - baseValues = { 0: 0 }, labelPosition = "above", labelX = 1) { - if(name == null) name = Objects.getNewName("uvwPSUVWabcde") - super(name, visible, color, labelContent) - this.drawPoints = drawPoints - this.drawDashedLines = drawDashedLines - this.defaultExpression = defaultExp - this.baseValues = baseValues - this.labelPosition = labelPosition - this.labelX = labelX - this.update() - } - - update() { - console.log("Updating sequence", this.sequence) - super.update() - if( - this.sequence == null || this.baseValues !== this.sequence.baseValues || - this.sequence.name !== this.name || - this.sequence.expr !== Object.values(this.defaultExpression)[0] || - this.sequence.valuePlus.toString() !== Object.keys(this.defaultExpression)[0] - ) - this.sequence = new MathSequence( - this.name, this.baseValues, - parseFloat(Object.keys(this.defaultExpression)[0]), - Object.values(this.defaultExpression)[0] - ) - } - - getReadableString() { - return this.sequence.toString() - } - - getLatexString() { - return this.sequence.toLatexString() - } - - execute(x = 1) { - if(x % 1 === 0) - return this.sequence.execute(x) - return null - } - - canExecute(x = 1) { - return x % 1 === 0 - } - - // Simplify returns the simplified string of the expression. - simplify(x = 1) { - if(x % 1 === 0) - return this.sequence.simplify(x) - return null - } - - getLabel() { - switch(this.labelContent) { - case "name": - return `(${this.name}ₙ)` - case "name + value": - return this.getReadableString() - case "null": - return "" - } - } - - getLatexLabel() { - switch(this.labelContent) { - case "name": - return `(${Latex.variable(this.name)}_n)` - case "name + value": - return this.getLatexString() - case "null": - return "" - } - } - - draw(canvas) { - Function.drawFunction(canvas, this.sequence, canvas.logscalex ? Domain.NE : Domain.N, Domain.R, this.drawPoints, this.drawDashedLines) - - // Label - this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) - } -} - diff --git a/common/src/objs/text.mjs b/common/src/objs/text.mjs deleted file mode 100644 index f4dd993..0000000 --- a/common/src/objs/text.mjs +++ /dev/null @@ -1,108 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { Expression } from "../math/index.mjs" -import * as P from "../parameters.mjs" -import Objects from "../module/objects.mjs" -import Latex from "../module/latex.mjs" - -import { DrawableObject } from "./common.mjs" - - -export default class Text extends DrawableObject { - static type() { - return "Text" - } - - static displayType() { - return qsTranslate("text", "Text") - } - - static displayTypeMultiple() { - return qsTranslate("text", "Texts") - } - - static properties() { - return { - [QT_TRANSLATE_NOOP("prop", "x")]: new P.Expression(), - [QT_TRANSLATE_NOOP("prop", "y")]: new P.Expression(), - [QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Positioning, - [QT_TRANSLATE_NOOP("prop", "text")]: "string", - "comment1": QT_TRANSLATE_NOOP( - "comment", - "If you have latex enabled, you can use use latex markup in between $$ to create equations." - ), - [QT_TRANSLATE_NOOP("prop", "disableLatex")]: "boolean" - } - } - - constructor(name = null, visible = true, color = null, labelContent = "null", - x = 1, y = 0, labelPosition = "center", text = "New text", disableLatex = false) { - if(name == null) name = Objects.getNewName("t") - super(name, visible, color, labelContent) - if(typeof x == "number" || typeof x == "string") x = new Expression(x.toString()) - this.x = x - if(typeof y == "number" || typeof y == "string") y = new Expression(y.toString()) - this.y = y - this.labelPosition = labelPosition - this.text = text - this.disableLatex = disableLatex - } - - getReadableString() { - return `${this.name} = "${this.text}"` - } - - latexMarkupText() { - // Check whether the text contains latex escaped elements. - let txt = [] - this.text.split("$$").forEach(function(t) { - txt = txt.concat(Latex.variable(t, true).replace(/\$\$/g, "").split("$")) - }) - let newTxt = txt[0] - let i - // Split between normal text and latex escaped. - for(i = 0; i < txt.length - 1; i++) - if(i & 0x01) // Every odd number - newTxt += "\\textsf{" + Latex.variable(txt[i + 1]) - else - newTxt += "}" + txt[i + 1] - // Finished by a } - if(i & 0x01) - newTxt += "{" - return newTxt - } - - getLatexString() { - return `${Latex.variable(this.name)} = "\\textsf{${this.latexMarkupText()}}"` - } - - getLabel() { - return this.text - } - - getLatexLabel() { - return `\\textsf{${this.latexMarkupText()}}` - } - - draw(canvas) { - let yOffset = this.disableLatex ? canvas.textsize - 4 : 0 - this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute()) + yOffset, this.disableLatex) - } -} - diff --git a/common/src/parameters.mjs b/common/src/parameters.mjs deleted file mode 100644 index 755d4aa..0000000 --- a/common/src/parameters.mjs +++ /dev/null @@ -1,380 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ -import { parseDomain, Expression as Expr, Domain } from "./math/index.mjs" -import Objects from "./module/objects.mjs" - -const NONE = class Empty { -} - -let stringValuesValidators = { - "int": [parseInt, (x) => !isNaN(x)], - "double": [parseFloat, (x) => !isNaN(x)], - "string": [(x) => x, () => true] -} -let stringValidatorTypes = Object.keys(stringValuesValidators) - - -class PropertyType { - /** - * Validates if a value corresponds to the current property type, and if so, returns it. - * @param value - * @returns {null|object} - */ - parse(value) { - throw new TypeError(`validate function of ${typeof this} has not been implemented.`) - } - - /** - * Exports the value of this property type. - * @param value - * @returns {string|number|bool|object} - */ - export(value) { - throw new TypeError(`export function of ${typeof this} has not been implemented.`) - } -} - - -export class Expression extends PropertyType { - constructor(...variables) { - super() - this.type = "Expression" - this.variables = variables - } - - toString() { - return this.variables.length === 0 ? "Number" : `Expression(${this.variables.join(", ")})` - } - - parse(value) { - let result = NONE - if(typeof value == "string") - try { - result = new Expr(value) - } catch(e) { - // Silently error and return null - console.trace() - console.log(`Error parsing expression ${value}:`) - console.error(e) - } - return result - } - - export(value) { - if(value instanceof Expr) - return value.toEditableString() - else - throw new TypeError(`Exportation error: ${value} is not an expression.`) - } -} - - -export class Enum extends PropertyType { - constructor(...values) { - super() - this.type = "Enum" - this.values = values - this.legacyValues = {} - this.translatedValues = values.map(x => qsTranslate("parameters", x)) - } - - toString() { - return `${this.type}(${this.values.join(", ")})` - } - - parse(value) { - let result = NONE - if(this.values.includes(value)) - result = value - else if(this.legacyValues[value]) - result = this.legacyValues[value] - return result - } - - export(value) { - if(this.values.includes(value)) - return value - else if(this.legacyValues[value]) - return this.legacyValues[value] - else - throw new TypeError(`Exportation error: ${value} not one of ${this.values.join(", ")}.`) - } -} - - -export class ObjectType extends PropertyType { - constructor(objType, allowNull = false) { - super() - this.type = "ObjectType" - this.objType = objType - this.allowNull = allowNull - } - - toString() { - return this.objType - } - - parse(name) { - let result = NONE - if(typeof name == "string" && name in Objects.currentObjectsByName) { - let obj = Objects.currentObjectsByName[name] - if(obj.type === this.objType || (this.objType === "ExecutableObject" && obj.execute)) { - result = obj - } else { - // Silently error and return null - console.trace() - console.error(new TypeError(`Object ${name} is of not of type ${this.objType}:`)) - } - } else if(this.allowNull && (name == null || name === "null")) - result = null - return result - } - - export(value) { - if(value == null && this.allowNull) - return null - else if(value.type === this.objType || (this.objType === "ExecutableObject" && value.execute)) - return value.name - else - throw new TypeError(`Exportation error: ${value} not a ${this.objType}.`) - } -} - - -export class List extends PropertyType { - constructor(type, format = /^.+$/, label = "", forbidAdding = false) { - super() - // type can be string, int and double. - this.type = "List" - this.valueType = type - if(!stringValidatorTypes.includes(this.valueType)) - throw new TypeError(`${this.valueType} must be one of ${stringValidatorTypes.join(", ")}.`) - this.format = format - this.label = label - this.forbidAdding = forbidAdding - } - - toString() { - return `${this.type}(${this.valueType}:${this.format})` - } - - parse(value) { - let result = NONE - if(typeof value == "object" && value.__proto__ === Array) { - let valid = 0 - for(let v of value) { - if(this.format.test(v)) { - v = stringValuesValidators[this.valueType][0](v) - if(stringValuesValidators[this.valueType][1](v)) - valid++ - } - } - if(valid === value.length) // Ensure every value is valid. - result = value - } - return result - } - - export(value) { - if(typeof value == "object" && value.__proto__ === Array) - return value - else - throw new TypeError(`Exportation error: ${value} not a list.`) - - } -} - - -export class Dictionary extends PropertyType { - constructor(type, keyType = "string", format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = "", postKeyLabel = ": ", forbidAdding = false) { - super() - // type & keyType can be string, int and double. - this.type = "Dict" - this.valueType = type - this.keyType = keyType - this.format = format - this.keyFormat = keyFormat - this.preKeyLabel = preKeyLabel - this.postKeyLabel = postKeyLabel - this.forbidAdding = forbidAdding - } - - toString() { - return `${this.type}(${this.keyType}:${this.keyFormat}: ${this.valueType}:${this.format})` - } - - parse(value) { - let result = NONE - if(typeof value == "object" && value.__proto__ !== Array) { - let dict = [] - for(let [k, v] of Object.entries(value)) { - if(this.format.test(v) && this.keyFormat.test(k)) { - k = stringValuesValidators[this.keyType][0](k) - v = stringValuesValidators[this.valueType][0](v) - if(stringValuesValidators[this.keyType][1](k)) - if(stringValuesValidators[this.valueType][1](v)) - dict[k] = v - } - } - if(Object.keys(dict).length === Object.keys(value).length) - result = value - } - return result - } - - export(value) { - if(typeof value == "object" && value.__proto__ !== Array) - return value - else - throw new TypeError(`Exportation error: ${value} not a dictionary.`) - } -} - -// Common parameters for Enums - -Enum.Position = new Enum( - QT_TRANSLATE_NOOP("parameters", "above"), - QT_TRANSLATE_NOOP("parameters", "below"), - QT_TRANSLATE_NOOP("parameters", "left"), - QT_TRANSLATE_NOOP("parameters", "right"), - QT_TRANSLATE_NOOP("parameters", "above-left"), - QT_TRANSLATE_NOOP("parameters", "above-right"), - QT_TRANSLATE_NOOP("parameters", "below-left"), - QT_TRANSLATE_NOOP("parameters", "below-right") -) -Enum.Position.legacyValues = { - "top": "above", - "bottom": "below", - "top-left": "above-left", - "top-right": "above-right", - "bottom-left": "below-left", - "bottom-right": "below-right" -} - -Enum.Positioning = new Enum( - QT_TRANSLATE_NOOP("parameters", "center"), - QT_TRANSLATE_NOOP("parameters", "top"), - QT_TRANSLATE_NOOP("parameters", "bottom"), - QT_TRANSLATE_NOOP("parameters", "left"), - QT_TRANSLATE_NOOP("parameters", "right"), - QT_TRANSLATE_NOOP("parameters", "top-left"), - QT_TRANSLATE_NOOP("parameters", "top-right"), - QT_TRANSLATE_NOOP("parameters", "bottom-left"), - QT_TRANSLATE_NOOP("parameters", "bottom-right") -) - -Enum.FunctionDisplayType = new Enum( - QT_TRANSLATE_NOOP("parameters", "application"), - QT_TRANSLATE_NOOP("parameters", "function") -) - -Enum.BodePass = new Enum( - QT_TRANSLATE_NOOP("parameters", "high"), - QT_TRANSLATE_NOOP("parameters", "low") -) - - -Enum.XCursorValuePosition = new Enum( - QT_TRANSLATE_NOOP("parameters", "Next to target"), - QT_TRANSLATE_NOOP("parameters", "With label"), - QT_TRANSLATE_NOOP("parameters", "Hidden") -) - -/** - * Ensures whether a provided value is of the corresponding type. - * @param {string|PropertyType} propertyType - * @param {string|number|boolean|array|object}value - * @returns {Object} - */ -export function ensureTypeSafety(propertyType, value) { - let result - let error = false - if(typeof propertyType == "string") - switch(propertyType) { - case "string": - result = value - error = typeof value !== "string" - break - case "number": - result = parseFloat(value) - error = isNaN(result) - break - case "boolean": - result = value - error = value !== true && value !== false - break - case "Domain": - try { - error = typeof value !== "string" - if(!error) - result = parseDomain(value) - } catch(e) { - // Parse domain sometimes returns an empty set when it cannot parse a domain. - // It's okay though, it shouldn't be user parsed value, so returning an empty set - // is okay for a corrupted file, rather than erroring. - console.trace() - console.log(`Error parsing domain ${value}:`) - console.error(e) - error = true - } - break - } - else if(propertyType instanceof PropertyType) { - result = propertyType.parse(value) - error = result === NONE - } else - throw new TypeError(`Importation error: Unknown property type ${propertyType}.`) - if(error) { - console.trace() - throw new TypeError(`Importation error: Couldn't parse ${JSON.stringify(value)} as ${propertyType}.`) - } - return result -} - -/** - * Serializes a property by its type to export into JSON. - * @param {string|PropertyType} propertyType - * @param value - * @returns {Object} - */ -export function serializesByPropertyType(propertyType, value) { - let result - if(typeof propertyType == "string") - switch(propertyType) { - case "string": - result = value.toString() - break - case "number": - result = parseFloat(value) - break - case "boolean": - result = value === true - break - case "Domain": - if(value instanceof Domain) - result = value.toString() - else - throw new TypeError(`Exportation error: ${value} is not a domain.`) - break - } - else if(propertyType instanceof PropertyType) { - result = propertyType.export(value) - } else - throw new TypeError(`Exportation error: Unknown property type ${propertyType}.`) - return result -} diff --git a/common/src/parsing/README.md b/common/src/parsing/README.md deleted file mode 100644 index 8fa8a93..0000000 --- a/common/src/parsing/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# General information - -Here lies the potential new, abandoned, cleaner implementation of the parsing system that was supposed to replace expr-eval.js, but never came to be because it's unfinished. If somebody has the will to finish this, you're welcome to try, as I won't. - -Currently, the tokenizer is complete in use to provide tokens for the syntax highlighting, and the reference to provide usage. diff --git a/common/src/parsing/reference.mjs b/common/src/parsing/reference.mjs deleted file mode 100644 index 80365aa..0000000 --- a/common/src/parsing/reference.mjs +++ /dev/null @@ -1,174 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import * as Polyfill from "../lib/expr-eval/polyfill.mjs" - -export const CONSTANTS = { - "π": Math.PI, - "pi": Math.PI, - "inf": Infinity, - "infinity": Infinity, - "∞": Infinity, - "e": Math.E -} -export const CONSTANTS_LIST = Object.keys(CONSTANTS) - -export const FUNCTIONS = { - // The functions commented are the one either not implemented - // in the parser, or not to be used for autocompletion. - // Unary operators - //'+': Number, - //'-': (x) => -x, - //'!' - // Other operations - "length": (s) => Array.isArray(s) ? s.length : String(s).length, - // Boolean functions - "not": (x) => !x, - // Math functions - "abs": Math.abs, - "acos": Math.acos, - "acosh": Math.acosh, - "asin": Math.asin, - "asinh": Math.asinh, - "atan": Math.atan, - "atan2": Math.atan2, - "atanh": Math.atanh, - "cbrt": Math.cbrt, - "ceil": Math.ceil, - //'clz32': Math.clz32, - "cos": Math.cos, - "cosh": Math.cosh, - "exp": Math.exp, - "expm1": Math.expm1, - "floor": Math.floor, - //'fround': Math.fround, - "hypot": Math.hypot, - //'imul': Math.imul, - "lg": Math.log10, - "ln": Math.log, - "log": Math.log, - "log10": Math.log10, - "log1p": Math.log1p, - "log2": Math.log2, - "max": Math.max, - "min": Math.min, - "pow": Math.log2, - "random": Math.random, - "round": Math.round, - "sign": Math.sign, - "sin": Math.sin, - "sinh": Math.sinh, - "sqrt": Math.sqrt, - "tan": Math.tan, - "tanh": Math.tanh, - "trunc": Math.trunc, - // Functions in expr-eval, ported here. - "fac": Polyfill.factorial, - "gamma": Polyfill.gamma, - "Γ": Polyfill.gamma, - "roundTo": (x, exp) => Number(x).toFixed(exp), - // 'map': Polyfill.arrayMap, - // 'fold': Polyfill.arrayFold, - // 'filter': Polyfill.arrayFilter, - // 'indexOf': Polyfill.indexOf, - // 'join': Polyfill.arrayJoin, - // Integral & derivative (only here for autocomplete). - "integral": () => 0, // TODO: Implement - "derivative": () => 0 -} -export const FUNCTIONS_LIST = Object.keys(FUNCTIONS) - -export class P { - // Parameter class. - constructor(type, name = "", optional = false, multipleAllowed = false) { - this.name = name - this.type = type - this.optional = optional - this.multipleAllowed = multipleAllowed - } - - toString() { - let base_string = this.type - if(this.name !== "") - base_string = `${this.name}: ${base_string}` - if(this.multipleAllowed) - base_string += "..." - if(!this.optional) - base_string = `<${base_string}>` - else - base_string = `[${base_string}]` - return base_string - } -} - -export let string = new P("string") -export let bool = new P("bool") -export let number = new P("number") -export let array = new P("array") - -export const FUNCTIONS_USAGE = { - "length": [string], - "not": [bool], - // Math functions - "abs": [number], - "acos": [number], - "acosh": [number], - "asin": [number], - "asinh": [number], - "atan": [number], - "atan2": [number], - "atanh": [number], - "cbrt": [number], - "ceil": [number], - //'clz32': [number], - "cos": [number], - "cosh": [number], - "exp": [number], - "expm1": [number], - "floor": [number], - //'fround': [number], - "hypot": [number], - //'imul': [number], - "lg": [number], - "ln": [number], - "log": [number], - "log10": [number], - "log1p": [number], - "log2": [number], - "max": [number, number, new P("numbers", "", true, true)], - "min": [number, number, new P("numbers", "", true, true)], - "pow": [number, new P("number", "exp")], - "random": [number, number], - "round": [number], - "sign": [number], - "sin": [number], - "sinh": [number], - "sqrt": [number], - "tan": [number], - "tanh": [number], - "trunc": [number], - // Functions in expr-eval, ported here. - "fac": [number], - "gamma": [number], - "Γ": [number], - "roundTo": [number, new P("number")], - // Function manipulation - "derivative": [new P("f"), new P("string", "var", true), number], - "integral": [new P("from"), new P("to"), new P("f"), new P("string", "var", true)] -} - diff --git a/common/src/parsing/tokenizer.mjs b/common/src/parsing/tokenizer.mjs deleted file mode 100644 index 8725bf3..0000000 --- a/common/src/parsing/tokenizer.mjs +++ /dev/null @@ -1,174 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - - -import * as Reference from "./reference.mjs" - -const WHITESPACES = " \t\n\r" -const STRING_LIMITERS = "\"'`" -const OPERATORS = "+-*/^%?:=!><" -const PUNCTUATION = "()[]{},." -const NUMBER_CHARS = "0123456789" -const IDENTIFIER_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_₀₁₂₃₄₅₆₇₈₉αβγδεζηθκλμξρςστφχψωₐₑₒₓₔₕₖₗₘₙₚₛₜ" - -export const TokenType = { - // Expression type - "WHITESPACE": "WHITESPACE", - "VARIABLE": "VARIABLE", - "CONSTANT": "CONSTANT", - "FUNCTION": "FUNCTION", - "OPERATOR": "OPERATOR", - "PUNCT": "PUNCT", - "NUMBER": "NUMBER", - "STRING": "STRING", - "UNKNOWN": "UNKNOWN" -} - -export class Token { - constructor(type, value, startPosition) { - this.type = type - this.value = value - this.startPosition = startPosition - } -} - -export class ExpressionTokenizer { - /** - * - * @param {InputExpression} input - * @param {boolean} tokenizeWhitespaces - * @param {boolean} errorOnUnknown - */ - constructor(input, tokenizeWhitespaces = false, errorOnUnknown = true) { - this.input = input - this.currentToken = null - this.tokenizeWhitespaces = tokenizeWhitespaces - this.errorOnUnknown = errorOnUnknown - } - - skipWhitespaces() { - while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek())) - this.input.next() - } - - readWhitespaces() { - let included = "" - while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek())) { - included += this.input.next() - } - return new Token(TokenType.WHITESPACE, included, this.input.position - included.length) - } - - readString() { - let delimitation = this.input.peek() - if(STRING_LIMITERS.includes(delimitation)) { - this.input.skip(delimitation) - let included = "" - let justEscaped = false - while(!this.input.atEnd() && (!STRING_LIMITERS.includes(this.input.peek()) || justEscaped)) { - justEscaped = this.input.peek() === "\\" - if(!justEscaped) - included += this.input.next() - } - this.input.skip(delimitation) - let token = new Token(TokenType.STRING, included, this.input.position - included.length) - token.limitator = delimitation - return token - } else { - this.input.raise("Unexpected " + delimitation + ". Expected string delimitator") - } - } - - readNumber() { - let included = "" - let hasDot = false - while(!this.input.atEnd() && (NUMBER_CHARS.includes(this.input.peek()) || this.input.peek() === ".")) { - if(this.input.peek() === ".") { - if(hasDot) this.input.raise("Unexpected '.'. Expected digit") - hasDot = true - } - included += this.input.next() - } - return new Token(TokenType.NUMBER, included, this.input.position - included.length) - } - - readOperator() { - let included = "" - while(!this.input.atEnd() && OPERATORS.includes(this.input.peek())) { - included += this.input.next() - } - return new Token(TokenType.OPERATOR, included, this.input.position - included.length) - } - - readIdentifier() { - let identifier = "" - while(!this.input.atEnd() && IDENTIFIER_CHARS.includes(this.input.peek().toLowerCase())) { - identifier += this.input.next() - } - if(Reference.CONSTANTS_LIST.includes(identifier.toLowerCase())) { - return new Token(TokenType.CONSTANT, identifier.toLowerCase(), this.input.position - identifier.length) - } else if(Reference.FUNCTIONS_LIST.includes(identifier.toLowerCase())) { - return new Token(TokenType.FUNCTION, identifier.toLowerCase(), this.input.position - identifier.length) - } else { - return new Token(TokenType.VARIABLE, identifier, this.input.position - identifier.length) - } - } - - readNextToken() { - if(!this.tokenizeWhitespaces) - this.skipWhitespaces() - if(this.input.atEnd()) return null - let c = this.input.peek() - if(this.tokenizeWhitespaces && WHITESPACES.includes(c)) return this.readWhitespaces() - if(STRING_LIMITERS.includes(c)) return this.readString() - if(NUMBER_CHARS.includes(c)) return this.readNumber() - if(IDENTIFIER_CHARS.includes(c.toLowerCase())) return this.readIdentifier() - if(OPERATORS.includes(c)) return this.readOperator() - if(Reference.CONSTANTS_LIST.includes(c)) return new Token(TokenType.CONSTANT, this.input.next(), this.input.position - 1) - if(PUNCTUATION.includes(c)) return new Token(TokenType.PUNCT, this.input.next(), this.input.position - 1) - if(this.errorOnUnknown) - this.input.raise("Unknown token character " + c) - else - return new Token(TokenType.UNKNOWN, this.input.next(), this.input.position - 1) - } - - peek() { - if(this.currentToken == null) this.currentToken = this.readNextToken() - return this.currentToken - } - - next() { - let tmp - if(this.currentToken == null) - tmp = this.readNextToken() - else - tmp = this.currentToken - this.currentToken = null - return tmp - } - - atEnd() { - return this.peek() == null - } - - skip(type) { - let next = this.next() - if(next.type !== type) - this.input.raise("Unexpected token " + next.type.toLowerCase() + " \"" + next.value + "\". Expected " + type.toLowerCase()) - } -} diff --git a/common/src/preferences/common.mjs b/common/src/preferences/common.mjs deleted file mode 100644 index 4e3ecc8..0000000 --- a/common/src/preferences/common.mjs +++ /dev/null @@ -1,136 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { Expression } from "../math/index.mjs" - -class Setting { - constructor(type, name, nameInConfig, icon) { - this.type = type - this.name = name - this.nameInConfig = nameInConfig - this.icon = icon - } - - /** - * Returns the value of the setting. - * @returns {string|boolean|number} - */ - value() { - throw new TypeError(`value of ${this.constructor} not implemented.`) - } - - /** - * Sets the value of the setting - * @param {string|boolean|number|Expression} value - */ - set(value) { - throw new TypeError(`value of ${this.constructor} not implemented.`) - } - - toString() { - return `Setting<${this.type} ${this.name}>` - } -} - -export class BoolSetting extends Setting { - constructor(name, nameInConfig, icon) { - super("bool", name, nameInConfig, icon) - } - - value() { - return Helper.getSetting(this.nameInConfig) - } - - set(value) { - Helper.setSetting(this.nameInConfig, value === true) - } -} - -export class NumberSetting extends Setting { - constructor(name, nameInConfig, icon, min = -Infinity, max = +Infinity) { - super("number", name, nameInConfig, icon) - this.min = typeof min == "number" ? () => min : min - this.max = typeof max == "number" ? () => max : max - } - - value() { - return Helper.getSetting(this.nameInConfig) - } - - set(value) { - Helper.setSetting(this.nameInConfig, +value) - } -} - -export class EnumIntSetting extends Setting { - constructor(name, nameInConfig, icon, values = []) { - super("enum", name, nameInConfig, icon) - this.values = values - } - - value() { - return Helper.getSetting(this.nameInConfig) - } - - set(value) { - Helper.setSetting(this.nameInConfig, +value) - } -} - -export class ExpressionSetting extends Setting { - constructor(name, nameInConfig, icon, variables = []) { - super("expression", name, nameInConfig, icon) - this.variables = variables - } - - value() { - return Helper.getSetting(this.nameInConfig) - } - - /** - * - * @param {Expression} value - */ - set(value) { - let vars = value.variables() - if(vars.length === this.variables.length && vars.every(x => this.variables.includes(x))) - Helper.setSetting(this.nameInConfig, value) - else { - let undefinedVars = vars.filter(x => !this.variables.includes(x)) - let allowed = "" - if(this.variables.length > 0) - allowed = `Allowed variables: ${this.variables.join(", ")}.` - throw new TypeError(`Cannot use variable(s) ${undefinedVars.join(", or ")} to define ${this.displayName}. ${allowed}`) - } - } -} - -export class StringSetting extends Setting { - constructor(name, nameInConfig, icon, defaultValues = []) { - super("string", name, nameInConfig, icon) - this.defaultValues = defaultValues - } - - value() { - return Helper.getSetting(this.nameInConfig) - } - - set(value) { - Helper.setSetting(this.nameInConfig, ""+value) - } -} \ No newline at end of file diff --git a/common/src/preferences/default.mjs b/common/src/preferences/default.mjs deleted file mode 100644 index c6864d1..0000000 --- a/common/src/preferences/default.mjs +++ /dev/null @@ -1,120 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { BoolSetting, ExpressionSetting, NumberSetting, StringSetting } from "./common.mjs" - - -const XZOOM = new NumberSetting( - qsTranslate("Settings", "X Zoom"), - "default_graph.xzoom", - "xzoom", - 0.1 -) - -const YZOOM = new NumberSetting( - qsTranslate("Settings", "Y Zoom"), - "default_graph.yzoom", - "yzoom", - 0.1 -) - -const XMIN = new NumberSetting( - qsTranslate("Settings", "Min X"), - "default_graph.xmin", - "xmin", - () => Helper.getSetting("default_graph.logscalex") ? 1e-100 : -Infinity -) - -const YMAX = new NumberSetting( - qsTranslate("Settings", "Max Y"), - "default_graph.ymax", - "ymax" -) - -const XAXISSTEP = new ExpressionSetting( - qsTranslate("Settings", "X Axis Step"), - "default_graph.xaxisstep", - "xaxisstep" -) - -const YAXISSTEP = new ExpressionSetting( - qsTranslate("Settings", "Y Axis Step"), - "default_graph.yaxisstep", - "yaxisstep" -) - -const LINE_WIDTH = new NumberSetting( - qsTranslate("Settings", "Line width"), - "default_graph.linewidth", - "linewidth", - 1 -) - -const TEXT_SIZE = new NumberSetting( - qsTranslate("Settings", "Text size (px)"), - "default_graph.textsize", - "textsize" -) - -const X_LABEL = new StringSetting( - qsTranslate("Settings", "X Label"), - "default_graph.xlabel", - "xlabel", - ["", "x", "ω (rad/s)"] -) - -const Y_LABEL = new StringSetting( - qsTranslate("Settings", "Y Label"), - "default_graph.ylabel", - "xlabel", - ["", "y", "G (dB)", "φ (°)", "φ (deg)", "φ (rad)"] -) - -const LOG_SCALE_X = new BoolSetting( - qsTranslate("Settings", "X Log scale"), - "default_graph.logscalex", - "logscalex" -) - -const SHOW_X_GRAD = new BoolSetting( - qsTranslate("Settings", "Show X graduation"), - "default_graph.showxgrad", - "showxgrad" -) - -const SHOW_Y_GRAD = new BoolSetting( - qsTranslate("Settings", "Show Y graduation"), - "default_graph.showygrad", - "showygrad" -) - -export default [ - XZOOM, - YZOOM, - XMIN, - YMAX, - XAXISSTEP, - YAXISSTEP, - LINE_WIDTH, - TEXT_SIZE, - X_LABEL, - Y_LABEL, - LOG_SCALE_X, - SHOW_X_GRAD, - SHOW_Y_GRAD -] diff --git a/common/src/preferences/expression.mjs b/common/src/preferences/expression.mjs deleted file mode 100644 index bda6899..0000000 --- a/common/src/preferences/expression.mjs +++ /dev/null @@ -1,51 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { BoolSetting, EnumIntSetting } from "./common.mjs" - -const AUTOCLOSE_FORMULA = new BoolSetting( - qsTranslate("expression", "Automatically close parenthesises and brackets"), - "expression_editor.autoclose", - "text" -) - -const ENABLE_SYNTAX_HIGHLIGHTING = new BoolSetting( - qsTranslate("expression", "Enable syntax highlighting"), - "expression_editor.colorize", - "appearance" -) - -const ENABLE_AUTOCOMPLETE = new BoolSetting( - qsTranslate("expression", "Enable autocompletion"), - "autocompletion.enabled", - "label" -) - -const PICK_COLOR_SCHEME = new EnumIntSetting( - qsTranslate("expression", "Color Scheme"), - "expression_editor.color_scheme", - "color", - ["Breeze Light", "Breeze Dark", "Solarized", "Github Light", "Github Dark", "Nord", "Monokai"] -) - -export default [ - AUTOCLOSE_FORMULA, - ENABLE_AUTOCOMPLETE, - ENABLE_SYNTAX_HIGHLIGHTING, - PICK_COLOR_SCHEME -] diff --git a/common/src/preferences/general.mjs b/common/src/preferences/general.mjs deleted file mode 100644 index 093afaf..0000000 --- a/common/src/preferences/general.mjs +++ /dev/null @@ -1,60 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { BoolSetting } from "./common.mjs" -import Canvas from "../module/canvas.mjs" -import LatexAPI from "../module/latex.mjs" - -const CHECK_FOR_UPDATES = new BoolSetting( - qsTranslate("general", "Check for updates on startup"), - "check_for_updates", - "update" -) - -const RESET_REDO_STACK = new BoolSetting( - qsTranslate("general", "Reset redo stack automaticly"), - "reset_redo_stack", - "timeline" -) - -class EnableLatex extends BoolSetting { - constructor() { - super(qsTranslate("general", "Enable LaTeX rendering"), "enable_latex", "Expression") - } - - set(value) { - if(!value || Latex.checkLatexInstallation()) { - super.set(value) - LatexAPI.enabled = value - Canvas.requestPaint() - } - } -} - -const ENABLE_LATEX_ASYNC = new BoolSetting( - qsTranslate("general", "Enable threaded LaTeX renderer (experimental)"), - "enable_latex_threaded", - "new" -) - -export default [ - CHECK_FOR_UPDATES, - RESET_REDO_STACK, - new EnableLatex(), - ENABLE_LATEX_ASYNC -] diff --git a/common/src/utils/expression.mjs b/common/src/utils/expression.mjs deleted file mode 100644 index 2caf5ea..0000000 --- a/common/src/utils/expression.mjs +++ /dev/null @@ -1,258 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { textsub, textsup } from "./subsup.mjs" - -/** - * Simplifies (mathematically) a mathematical expression. - * @deprecated - * @param {string} str - Expression to parse - * @returns {string} - */ -export function simplifyExpression(str) { - let replacements = [ - // Operations not done by parser. - // [// Decomposition way 2 - // /(^|[+-] |\()([-.\d\w]+) ([*/]) \((([-.\d\w] [*/] )?[-\d\w.]+) ([+\-]) (([-.\d\w] [*/] )?[\d\w.+]+)\)($| [+-]|\))/g, - // "$1$2 $3 $4 $6 $2 $3 $7$9" - // ], - // [ // Decomposition way 2 - // /(^|[+-] |\()\((([-.\d\w] [*/] )?[-\d\w.]+) ([+\-]) (([-.\d\w] [*/] )?[\d\w.+]+)\) ([*/]) ([-.\d\w]+)($| [+-]|\))/g, - // "$1$2 $7 $8 $4 $5 $7 $8$9" - // ], - [ // Factorisation of π elements. - /(([-\d\w.]+ [*/] )*)(pi|π)(( [/*] [-\d\w.]+)*) ([+-]) (([-\d\w.]+ [*/] )*)(pi|π)(( [/*] [-\d\w.]+)*)?/g, - function(match, m1, n1, pi1, m2, ope2, n2, opeM, m3, n3, pi2, m4, ope4, n4) { - // g1, g2, g3 , g4, g5, g6, g7, g8, g9, g10, g11,g12 , g13 - // We don't care about mx & pix, ope2 & ope4 are either / or * for n2 & n4. - // n1 & n3 are multiplied, opeM is the main operation (- or +). - // Putting all n in form of number - //n2 = n2 == undefined ? 1 : parseFloat(n) - n1 = m1 === undefined ? 1 : eval(m1 + "1") - n2 = m2 === undefined ? 1 : eval("1" + m2) - n3 = m3 === undefined ? 1 : eval(m3 + "1") - n4 = m4 === undefined ? 1 : eval("1" + m4) - //let [n1, n2, n3, n4] = [n1, n2, n3, n4].map(n => n == undefined ? 1 : parseFloat(n)) - // Falling back to * in case it does not exist (the corresponding n would be 1) - [ope2, ope4] = [ope2, ope4].map(ope => ope === "/" ? "/" : "*") - let coeff1 = n1 * n2 - let coeff2 = n3 * n4 - let coefficient = coeff1 + coeff2 - (opeM === "-" ? 2 * coeff2 : 0) - - return `${coefficient} * π` - } - ], - [ // Removing parenthesis when content is only added from both sides. - /(^|[+-] |\()\(([^)(]+)\)($| [+-]|\))/g, - function(match, b4, middle, after) { - return `${b4}${middle}${after}` - } - ], - [ // Removing parenthesis when content is only multiplied. - /(^|[*\/] |\()\(([^)(+-]+)\)($| [*\/+-]|\))/g, - function(match, b4, middle, after) { - return `${b4}${middle}${after}` - } - ], - [ // Removing parenthesis when content is only multiplied. - /(^|[*\/+-] |\()\(([^)(+-]+)\)($| [*\/]|\))/g, - function(match, b4, middle, after) { - return `${b4}${middle}${after}` - } - ], - [// Simplification additions/subtractions. - /(^|[^*\/] |\()([-.\d]+) [+-] (\([^)(]+\)|[^)(]+) [+-] ([-.\d]+)($| [^*\/]|\))/g, - function(match, b4, n1, op1, middle, op2, n2, after) { - let total - if(op2 === "+") { - total = parseFloat(n1) + parseFloat(n2) - } else { - total = parseFloat(n1) - parseFloat(n2) - } - return `${b4}${total} ${op1} ${middle}${after}` - } - ], - [// Simplification multiplications/divisions. - /([-.\d]+) [*\/] (\([^)(]+\)|[^)(+-]+) [*\/] ([-.\d]+)/g, - function(match, n1, op1, middle, op2, n2) { - if(parseInt(n1) === n1 && parseInt(n2) === n2 && op2 === "/" && - (parseInt(n1) / parseInt(n2)) % 1 !== 0) { - // Non int result for int division. - return `(${n1} / ${n2}) ${op1} ${middle}` - } else { - if(op2 === "*") { - return `${parseFloat(n1) * parseFloat(n2)} ${op1} ${middle}` - } else { - return `${parseFloat(n1) / parseFloat(n2)} ${op1} ${middle}` - } - } - } - ], - [// Starting & ending parenthesis if not needed. - /^\s*\((.*)\)\s*$/g, - function(match, middle) { - let str = middle - // Replace all groups - while(/\([^)(]+\)/g.test(str)) - str = str.replace(/\([^)(]+\)/g, "") - // There shouldn't be any more parenthesis - // If there is, that means the 2 parenthesis are needed. - if(!str.includes(")") && !str.includes("(")) { - return middle - } else { - return `(${middle})` - } - - } - ] - // Simple simplifications - // [/(\s|^|\()0(\.0+)? \* (\([^)(]+\))/g, '$10'], - // [/(\s|^|\()0(\.0+)? \* ([^)(+-]+)/g, '$10'], - // [/(\([^)(]\)) \* 0(\.0+)?(\s|$|\))/g, '0$3'], - // [/([^)(+-]) \* 0(\.0+)?(\s|$|\))/g, '0$3'], - // [/(\s|^|\()1(\.0+)? [\*\/] /g, '$1'], - // [/(\s|^|\()0(\.0+)? (\+|\-) /g, '$1'], - // [/ [\*\/] 1(\.0+)?(\s|$|\))/g, '$3'], - // [/ (\+|\-) 0(\.0+)?(\s|$|\))/g, '$3'], - // [/(^| |\() /g, '$1'], - // [/ ($|\))/g, '$1'], - ] - - // Replacements - let found - do { - found = false - for(let replacement of replacements) - while(replacement[0].test(str)) { - found = true - str = str.replace(replacement[0], replacement[1]) - } - } while(found) - return str -} - - -/** - * Transforms a mathematical expression to make it readable by humans. - * NOTE: Will break parsing of expression. - * @deprecated - * @param {string} str - Expression to parse. - * @returns {string} - */ -export function makeExpressionReadable(str) { - let replacements = [ - // letiables - [/pi/g, "π"], - [/Infinity/g, "∞"], - [/inf/g, "∞"], - // Other - [/ \* /g, "×"], - [/ \^ /g, "^"], - [/\^\(([\d\w+-]+)\)/g, function(match, p1) { - return textsup(p1) - }], - [/\^([\d\w+-]+)/g, function(match, p1) { - return textsup(p1) - }], - [/_\(([\d\w+-]+)\)/g, function(match, p1) { - return textsub(p1) - }], - [/_([\d\w+-]+)/g, function(match, p1) { - return textsub(p1) - }], - [/\[([^\[\]]+)\]/g, function(match, p1) { - return textsub(p1) - }], - [/(\d|\))×/g, "$1"], - [/integral\((.+),\s?(.+),\s?["'](.+)["'],\s?["'](.+)["']\)/g, function(match, a, b, p1, body, p2, p3, by, p4) { - if(a.length < b.length) { - return `∫${textsub(a)}${textsup(b)} ${body} d${by}` - } else { - return `∫${textsup(b)}${textsub(a)} ${body} d${by}` - } - }], - [/derivative\(?["'](.+)["'], ?["'](.+)["'], ?(.+)\)?/g, function(match, p1, body, p2, p3, by, p4, x) { - return `d(${body.replace(new RegExp(by, "g"), "x")})/dx` - }] - ] - - // str = simplifyExpression(str) - // Replacements - for(let replacement of replacements) - while(replacement[0].test(str)) - str = str.replace(replacement[0], replacement[1]) - return str -} - -/** @type {[RegExp, string][]} */ -const replacements = [ - // Greek letters - [/(\W|^)al(pha)?(\W|$)/g, "$1α$3"], - [/(\W|^)be(ta)?(\W|$)/g, "$1β$3"], - [/(\W|^)ga(mma)?(\W|$)/g, "$1γ$3"], - [/(\W|^)de(lta)?(\W|$)/g, "$1δ$3"], - [/(\W|^)ep(silon)?(\W|$)/g, "$1ε$3"], - [/(\W|^)ze(ta)?(\W|$)/g, "$1ζ$3"], - [/(\W|^)et(a)?(\W|$)/g, "$1η$3"], - [/(\W|^)th(eta)?(\W|$)/g, "$1θ$3"], - [/(\W|^)io(ta)?(\W|$)/g, "$1ι$3"], - [/(\W|^)ka(ppa)?(\W|$)/g, "$1κ$3"], - [/(\W|^)la(mbda)?(\W|$)/g, "$1λ$3"], - [/(\W|^)mu(\W|$)/g, "$1μ$2"], - [/(\W|^)nu(\W|$)/g, "$1ν$2"], - [/(\W|^)xi(\W|$)/g, "$1ξ$2"], - [/(\W|^)rh(o)?(\W|$)/g, "$1ρ$3"], - [/(\W|^)si(gma)?(\W|$)/g, "$1σ$3"], - [/(\W|^)ta(u)?(\W|$)/g, "$1τ$3"], - [/(\W|^)up(silon)?(\W|$)/g, "$1υ$3"], - [/(\W|^)ph(i)?(\W|$)/g, "$1φ$3"], - [/(\W|^)ch(i)?(\W|$)/g, "$1χ$3"], - [/(\W|^)ps(i)?(\W|$)/g, "$1ψ$3"], - [/(\W|^)om(ega)?(\W|$)/g, "$1ω$3"], - // Capital greek letters - [/(\W|^)gga(mma)?(\W|$)/g, "$1Γ$3"], - [/(\W|^)gde(lta)?(\W|$)/g, "$1Δ$3"], - [/(\W|^)gth(eta)?(\W|$)/g, "$1Θ$3"], - [/(\W|^)gla(mbda)?(\W|$)/g, "$1Λ$3"], - [/(\W|^)gxi(\W|$)/g, "$1Ξ$2"], - [/(\W|^)gpi(\W|$)/g, "$1Π$2"], - [/(\W|^)gsi(gma)?(\W|$)/g, "$1Σ$3"], - [/(\W|^)gph(i)?(\W|$)/g, "$1Φ$3"], - [/(\W|^)gps(i)?(\W|$)/g, "$1Ψ$3"], - [/(\W|^)gom(ega)?(\W|$)/g, "$1Ω$3"], - // Array elements - [/\[([^\]\[]+)\]/g, function(match, p1) { - return textsub(p1) - }] -] - -/** - * Parses a variable name to make it readable by humans. - * - * @param {string} str - Variable name to parse - * @param {boolean} removeUnallowed - Remove domain symbols disallowed in name. - * @returns {string} - The parsed name - */ -export function parseName(str, removeUnallowed = true) { - for(const replacement of replacements) - str = str.replace(replacement[0], replacement[1]) - if(removeUnallowed) - str = str.replace(/[xnπℝℕ\\∪∩\]\[ ()^/÷*×+=\d¹²³⁴⁵⁶⁷⁸⁹⁰-]/g, "") - - return str -} diff --git a/common/src/utils/index.mjs b/common/src/utils/index.mjs deleted file mode 100644 index 2c11c1b..0000000 --- a/common/src/utils/index.mjs +++ /dev/null @@ -1,22 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -export * from "./prototype.mjs" -export * from "./subsup.mjs" -export * from "./expression.mjs" -export * from "./other.mjs" diff --git a/common/src/utils/other.mjs b/common/src/utils/other.mjs deleted file mode 100644 index 50f84f4..0000000 --- a/common/src/utils/other.mjs +++ /dev/null @@ -1,41 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - - - -/** - * Creates a randomized color string. - * @returns {string} - */ -export function getRandomColor() { - let clrs = "0123456789ABCDEF" - let color = "#" - for(let i = 0; i < 6; i++) { - color += clrs[Math.floor(Math.random() * (16 - 5 * (i % 2 === 0)))] - } - return color -} - -/** - * Escapes text to html entities. - * @param {string} str - * @returns {string} - */ -export function escapeHTML(str) { - return str.replace(/&/g, "&").replace(//g, ">") -} diff --git a/common/src/utils/prototype.mjs b/common/src/utils/prototype.mjs deleted file mode 100644 index b0fd27f..0000000 --- a/common/src/utils/prototype.mjs +++ /dev/null @@ -1,51 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -// Add string methods -/** - * Replaces latin characters with their uppercase versions. - * @return {string} - */ -String.prototype.toLatinUppercase = function() { - return this.replace(/[a-z]/g, function(match) { - return match.toUpperCase() - }) -} - -/** - * Removes the first and last character of a string - * Used to remove enclosing characters like quotes, parentheses, brackets... - * @note Does NOT check for their existence ahead of time. - * @return {string} - */ -String.prototype.removeEnclosure = function() { - return this.substring(1, this.length - 1) -} - -/** - * Rounds to a certain number of decimal places. - * From https://stackoverflow.com/a/48764436 - * - * @param {number} decimalPlaces - * @return {number} - */ -Number.prototype.toDecimalPrecision = function(decimalPlaces = 0) { - const p = Math.pow(10, decimalPlaces) - const n = (this * p) * (1 + Number.EPSILON) - return Math.round(n) / p -} diff --git a/common/src/utils/subsup.mjs b/common/src/utils/subsup.mjs deleted file mode 100644 index 6eb4280..0000000 --- a/common/src/utils/subsup.mjs +++ /dev/null @@ -1,140 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -const CHARACTER_TO_POWER = new Map([ - ["-", "⁻"], - ["+", "⁺"], - ["=", "⁼"], - [" ", " "], - ["(", "⁽"], - [")", "⁾"], - ["0", "⁰"], - ["1", "¹"], - ["2", "²"], - ["3", "³"], - ["4", "⁴"], - ["5", "⁵"], - ["6", "⁶"], - ["7", "⁷"], - ["8", "⁸"], - ["9", "⁹"], - ["a", "ᵃ"], - ["b", "ᵇ"], - ["c", "ᶜ"], - ["d", "ᵈ"], - ["e", "ᵉ"], - ["f", "ᶠ"], - ["g", "ᵍ"], - ["h", "ʰ"], - ["i", "ⁱ"], - ["j", "ʲ"], - ["k", "ᵏ"], - ["l", "ˡ"], - ["m", "ᵐ"], - ["n", "ⁿ"], - ["o", "ᵒ"], - ["p", "ᵖ"], - ["r", "ʳ"], - ["s", "ˢ"], - ["t", "ᵗ"], - ["u", "ᵘ"], - ["v", "ᵛ"], - ["w", "ʷ"], - ["x", "ˣ"], - ["y", "ʸ"], - ["z", "ᶻ"] -]) - -const CHARACTER_TO_INDICE = new Map([ - ["-", "₋"], - ["+", "₊"], - ["=", "₌"], - ["(", "₍"], - [")", "₎"], - [" ", " "], - ["0", "₀"], - ["1", "₁"], - ["2", "₂"], - ["3", "₃"], - ["4", "₄"], - ["5", "₅"], - ["6", "₆"], - ["7", "₇"], - ["8", "₈"], - ["9", "₉"], - ["a", "ₐ"], - ["e", "ₑ"], - ["h", "ₕ"], - ["i", "ᵢ"], - ["j", "ⱼ"], - ["k", "ₖ"], - ["l", "ₗ"], - ["m", "ₘ"], - ["n", "ₙ"], - ["o", "ₒ"], - ["p", "ₚ"], - ["r", "ᵣ"], - ["s", "ₛ"], - ["t", "ₜ"], - ["u", "ᵤ"], - ["v", "ᵥ"], - ["x", "ₓ"] -]) - -const EXPONENTS = [ - "⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹" -] - -const EXPONENTS_REG = new RegExp("([" + EXPONENTS.join("") + "]+)", "g") - -/** - * Put a text in sup position - * @param {string} text - * @return {string} - */ -export function textsup(text) { - let ret = "" - text = text.toString() - for(let letter of text) - ret += CHARACTER_TO_POWER.has(letter) ? CHARACTER_TO_POWER.get(letter) : letter - return ret -} - -/** - * Put a text in sub position - * @param {string} text - * @return {string} - */ -export function textsub(text) { - let ret = "" - text = text.toString() - for(let letter of text) - ret += CHARACTER_TO_INDICE.has(letter) ? CHARACTER_TO_INDICE.get(letter) : letter - return ret -} - - -/** - * Parses exponents and replaces them with expression values - * @param {string} expression - The expression to replace in. - * @return {string} The parsed expression - */ -export function exponentsToExpression(expression) { - return expression.replace(EXPONENTS_REG, (m, exp) => "^" + exp.split("").map((x) => EXPONENTS.indexOf(x)).join("")) -} - diff --git a/common/test/basics/events.mjs b/common/test/basics/events.mjs deleted file mode 100644 index 7dac6e2..0000000 --- a/common/test/basics/events.mjs +++ /dev/null @@ -1,118 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { describe, it } from "mocha" -import { expect } from "chai" - -const { spy } = chaiPlugins - -import { BaseEventEmitter, BaseEvent } from "../../src/events.mjs" - -class MockEmitter extends BaseEventEmitter { - static emits = ["example1", "example2"] -} - -class MockEvent1 extends BaseEvent { - constructor() { - super("example1") - } -} - -class MockEvent2 extends BaseEvent { - constructor(parameter) { - super("example2") - this.parameter = parameter - } -} - -describe("Lib/EventsEmitters", function() { - it("sends events with unique and readonly names", function() { - const event = new MockEvent1() - expect(event.name).to.equal("example1") - expect(() => event.name = "not").to.throw() - }) - - it("forwards events to all of its listeners", function() { - const emitter = new MockEmitter() - const listener1 = spy() - const listener2 = spy() - emitter.on("example1", listener1) - emitter.on("example1", listener2) - emitter.emit(new MockEvent1()) - expect(listener1).to.have.been.called.once - expect(listener2).to.have.been.called.once - }) - - it("forwards multiple events to a singular listener", function() { - const emitter = new MockEmitter() - const listener = spy() - const mockEvent1 = new MockEvent1() - const mockEvent2 = new MockEvent2(3) - emitter.on("example1 example2", listener) - emitter.emit(mockEvent1) - emitter.emit(mockEvent2) - expect(listener).to.have.been.called.twice - expect(listener).to.have.been.first.called.with.exactly(mockEvent1) - expect(listener).to.have.been.second.called.with.exactly(mockEvent2) - }) - - it("is able to have listeners removed", function() { - const emitter = new MockEmitter() - const listener = spy() - emitter.on("example1", listener) - const removedFromEventItDoesntListenTo = emitter.off("example2", listener) - const removedFromEventItListensTo = emitter.off("example1", listener) - const removedFromEventASecondTime = emitter.off("example1", listener) - expect(removedFromEventItDoesntListenTo).to.be.false - expect(removedFromEventItListensTo).to.be.true - expect(removedFromEventASecondTime).to.be.false - emitter.on("example1 example2", listener) - const removedFromBothEvents = emitter.off("example1 example2", listener) - expect(removedFromBothEvents).to.be.true - emitter.on("example1", listener) - const removedFromOneOfTheEvents = emitter.off("example1 example2", listener) - expect(removedFromOneOfTheEvents).to.be.true - }) - - it("is able to have one listener's listening to a single event removed when said listener listens to multiple", function() { - const emitter = new MockEmitter() - const listener = spy() - const mockEvent1 = new MockEvent1() - const mockEvent2 = new MockEvent2(3) - emitter.on("example1 example2", listener) - // Disable listener for example1 - emitter.off("example1", listener) - emitter.emit(mockEvent1) - emitter.emit(mockEvent2) - expect(listener).to.have.been.called.once - expect(listener).to.also.have.been.called.with.exactly(mockEvent2) - }) - - it("is not able to emit or add/remove listeners for inexistant events", function() { - const emitter = new MockEmitter() - const listener = spy() - expect(() => emitter.on("inexistant", listener)).to.throw(Error) - expect(() => emitter.off("inexistant", listener)).to.throw(Error) - expect(() => emitter.emit(new BaseEvent("inexistant"))).to.throw(Error) - }) - - it("isn't able to emit non-events", function() { - const emitter = new MockEmitter() - expect(() => emitter.emit("not-an-event")).to.throw(Error) - }) -}) \ No newline at end of file diff --git a/common/test/basics/interface.mjs b/common/test/basics/interface.mjs deleted file mode 100644 index 48af62e..0000000 --- a/common/test/basics/interface.mjs +++ /dev/null @@ -1,58 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { describe, it } from "mocha" -import { expect } from "chai" - -import { MockLatex } from "../mock/latex.mjs" -import { MockHelper } from "../mock/helper.mjs" -import { - CanvasInterface, - DialogInterface, - HelperInterface, - Interface, - LatexInterface, - RootInterface -} from "../../src/module/interface.mjs" -import { MockDialog } from "../mock/dialog.mjs" -import { MockRootElement } from "../mock/root.mjs" -import { MockCanvas } from "../mock/canvas.mjs" - -describe("Interfaces", function() { - describe("#interface methods", function() { - it("throws an error when called directly", function() { - const obj = new CanvasInterface() - expect(() => obj.markDirty()).to.throw(Error) - }) - }) - - describe("#checkImplementation", function() { - it("validates the implementation of mocks", function() { - const checkMockLatex = () => Interface.checkImplementation(LatexInterface, new MockLatex()) - const checkMockHelper = () => Interface.checkImplementation(HelperInterface, new MockHelper()) - const checkMockDialog = () => Interface.checkImplementation(DialogInterface, new MockDialog()) - const checkMockRoot = () => Interface.checkImplementation(RootInterface, new MockRootElement()) - const checkMockCanvas = () => Interface.checkImplementation(CanvasInterface, new MockCanvas()) - expect(checkMockLatex).to.not.throw() - expect(checkMockHelper).to.not.throw() - expect(checkMockDialog).to.not.throw() - expect(checkMockRoot).to.not.throw() - expect(checkMockCanvas).to.not.throw() - }) - }) -}) \ No newline at end of file diff --git a/common/test/basics/polyfill.mjs b/common/test/basics/polyfill.mjs deleted file mode 100644 index 1fc9599..0000000 --- a/common/test/basics/polyfill.mjs +++ /dev/null @@ -1,232 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -// Load prior tests - -import { describe, it } from "mocha" -import { expect } from "chai" - -import * as Polyfill from "../../src/lib/expr-eval/polyfill.mjs" -import { - andOperator, - cbrt, - equal, - expm1, - hypot, - lessThan, - log1p, - log2, - notEqual -} from "../../src/lib/expr-eval/polyfill.mjs" - -describe("Math/Polyfill", () => { - describe("#add", function() { - it("adds two numbers", function() { - expect(Polyfill.add(2, 3)).to.equal(5) - expect(Polyfill.add("2", "3")).to.equal(5) - }) - }) - - describe("#sub", function() { - it("subtracts two numbers", function() { - expect(Polyfill.sub(2, 1)).to.equal(1) - expect(Polyfill.sub("2", "1")).to.equal(1) - }) - }) - - describe("#mul", function() { - it("multiplies two numbers", function() { - expect(Polyfill.mul(2, 3)).to.equal(6) - expect(Polyfill.mul("2", "3")).to.equal(6) - }) - }) - - describe("#div", function() { - it("divides two numbers", function() { - expect(Polyfill.div(10, 2)).to.equal(5) - expect(Polyfill.div("10", "2")).to.equal(5) - }) - }) - - describe("#mod", function() { - it("returns the modulo of two numbers", function() { - expect(Polyfill.mod(10, 3)).to.equal(1) - expect(Polyfill.mod("10", "3")).to.equal(1) - }) - }) - - describe("#concat", function() { - it("returns the concatenation of two strings", function() { - expect(Polyfill.concat(10, 3)).to.equal("103") - expect(Polyfill.concat("abc", "def")).to.equal("abcdef") - }) - }) - - describe("#equal", function() { - it("returns whether its two arguments are equal", function() { - expect(Polyfill.equal(10, 3)).to.be.false - expect(Polyfill.equal(10, 10)).to.be.true - expect(Polyfill.equal("abc", "def")).to.be.false - expect(Polyfill.equal("abc", "abc")).to.be.true - }) - }) - - describe("#notEqual", function() { - it("returns whether its two arguments are not equal", function() { - expect(Polyfill.notEqual(10, 3)).to.be.true - expect(Polyfill.notEqual(10, 10)).to.be.false - expect(Polyfill.notEqual("abc", "def")).to.be.true - expect(Polyfill.notEqual("abc", "abc")).to.be.false - }) - }) - - describe("#greaterThan", function() { - it("returns whether its first argument is strictly greater than its second", function() { - expect(Polyfill.greaterThan(10, 3)).to.be.true - expect(Polyfill.greaterThan(10, 10)).to.be.false - expect(Polyfill.greaterThan(10, 30)).to.be.false - }) - }) - - describe("#lessThan", function() { - it("returns whether its first argument is strictly less than its second", function() { - expect(Polyfill.lessThan(10, 3)).to.be.false - expect(Polyfill.lessThan(10, 10)).to.be.false - expect(Polyfill.lessThan(10, 30)).to.be.true - }) - }) - - describe("#greaterThanEqual", function() { - it("returns whether its first argument is greater or equal to its second", function() { - expect(Polyfill.greaterThanEqual(10, 3)).to.be.true - expect(Polyfill.greaterThanEqual(10, 10)).to.be.true - expect(Polyfill.greaterThanEqual(10, 30)).to.be.false - }) - }) - - describe("#lessThanEqual", function() { - it("returns whether its first argument is strictly less than its second", function() { - expect(Polyfill.lessThanEqual(10, 3)).to.be.false - expect(Polyfill.lessThanEqual(10, 10)).to.be.true - expect(Polyfill.lessThanEqual(10, 30)).to.be.true - }) - }) - - describe("#andOperator", function() { - it("returns whether its arguments are both true", function() { - expect(Polyfill.andOperator(true, true)).to.be.true - expect(Polyfill.andOperator(true, false)).to.be.false - expect(Polyfill.andOperator(false, true)).to.be.false - expect(Polyfill.andOperator(false, false)).to.be.false - expect(Polyfill.andOperator(10, 3)).to.be.true - expect(Polyfill.andOperator(10, 0)).to.be.false - expect(Polyfill.andOperator(0, 0)).to.be.false - }) - }) - - describe("#orOperator", function() { - it("returns whether one of its arguments is true", function() { - expect(Polyfill.orOperator(true, true)).to.be.true - expect(Polyfill.orOperator(true, false)).to.be.true - expect(Polyfill.orOperator(false, true)).to.be.true - expect(Polyfill.orOperator(false, false)).to.be.false - expect(Polyfill.orOperator(10, 3)).to.be.true - expect(Polyfill.orOperator(10, 0)).to.be.true - expect(Polyfill.orOperator(0, 0)).to.be.false - }) - }) - - describe("#inOperator", function() { - it("checks if second argument contains first", function() { - expect(Polyfill.inOperator("a", ["a", "b", "c"])).to.be.true - expect(Polyfill.inOperator(3, [0, 1, 2])).to.be.false - expect(Polyfill.inOperator(3, [0, 1, 3, 2])).to.be.true - expect(Polyfill.inOperator("a", "abcdef")).to.be.true - expect(Polyfill.inOperator("a", "bcdefg")).to.be.false - }) - }) - - describe("#sinh, #cosh, #tanh, #asinh, #acosh, #atanh", function() { - const EPSILON = 1e-12 - for(let x = -.9; x < 1; x += 0.1) { - expect(Polyfill.sinh(x)).to.be.approximately(Math.sinh(x), EPSILON) - expect(Polyfill.cosh(x)).to.be.approximately(Math.cosh(x), EPSILON) - expect(Polyfill.tanh(x)).to.be.approximately(Math.tanh(x), EPSILON) - expect(Polyfill.asinh(x)).to.be.approximately(Math.asinh(x), EPSILON) - expect(Polyfill.atanh(x)).to.be.approximately(Math.atanh(x), EPSILON) - } - for(let x = 1.1; x < 10; x += 0.1) { - expect(Polyfill.sinh(x)).to.be.approximately(Math.sinh(x), EPSILON) - expect(Polyfill.cosh(x)).to.be.approximately(Math.cosh(x), EPSILON) - expect(Polyfill.tanh(x)).to.be.approximately(Math.tanh(x), EPSILON) - expect(Polyfill.asinh(x)).to.be.approximately(Math.asinh(x), EPSILON) - expect(Polyfill.acosh(x)).to.be.approximately(Math.acosh(x), EPSILON) - expect(Polyfill.log10(x)).to.be.approximately(Math.log10(x), EPSILON) - } - }) - - describe("#trunc", function() { - it("returns the decimal part of floats", function() { - for(let x = -10; x < 10; x += 0.1) - expect(Polyfill.trunc(x)).to.equal(Math.trunc(x)) - }) - }) - - describe("#gamma", function() { - it("returns the product of factorial(x - 1)", function() { - expect(Polyfill.gamma(0)).to.equal(Infinity) - expect(Polyfill.gamma(1)).to.equal(1) - expect(Polyfill.gamma(2)).to.equal(1) - expect(Polyfill.gamma(3)).to.equal(2) - expect(Polyfill.gamma(4)).to.equal(6) - expect(Polyfill.gamma(5)).to.equal(24) - expect(Polyfill.gamma(172)).to.equal(Infinity) - expect(Polyfill.gamma(172.3)).to.equal(Infinity) - expect(Polyfill.gamma(.2)).to.approximately(4.590_843_712, 1e-8) - expect(Polyfill.gamma(5.5)).to.be.approximately(52.34277778, 1e-8) - expect(Polyfill.gamma(90.2)).to.equal(4.0565358202825355e+136) - }) - }) - - describe("#hypot", function() { - it("returns the hypothenus length of a triangle whose length are provided in arguments", function() { - for(let x = 0; x < 10; x += 0.3) { - expect(Polyfill.hypot(x)).to.be.approximately(Math.hypot(x), Number.EPSILON) - for(let y = 0; y < 10; y += 0.3) { - expect(Polyfill.hypot(x, y)).to.be.approximately(Math.hypot(x, y), Number.EPSILON) - } - } - }) - }) - - describe("#sign, #cbrt, #exmp1", function() { - for(let x = -10; x < 10; x += 0.3) { - expect(Polyfill.sign(x)).to.approximately(Math.sign(x), 1e-12) - expect(Polyfill.cbrt(x)).to.approximately(Math.cbrt(x), 1e-12) - expect(Polyfill.expm1(x)).to.approximately(Math.expm1(x), 1e-12) - } - }) - - describe("#log1p, #log2", function() { - for(let x = 1; x < 10; x += 0.3) { - expect(Polyfill.log1p(x)).to - .be.approximately(Math.log1p(x), 1e-12) - expect(Polyfill.log2(x)).to.be.approximately(Math.log2(x), 1e-12) - } - }) -}) \ No newline at end of file diff --git a/common/test/basics/utils.mjs b/common/test/basics/utils.mjs deleted file mode 100644 index 66ecd92..0000000 --- a/common/test/basics/utils.mjs +++ /dev/null @@ -1,183 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { textsup, textsub, parseName, getRandomColor, escapeHTML, exponentsToExpression } from "../../src/utils/index.mjs" - - -import { describe, it } from "mocha" -import { expect } from "chai" - -describe("Lib/PrototypeExtensions", function() { - describe("#String.toLatinUppercase", function() { - it("transforms latin characters from strings to their uppercase version", function() { - expect("abc".toLatinUppercase()).to.equal("ABC") - expect("abCd".toLatinUppercase()).to.equal("ABCD") - expect("ab123cd456".toLatinUppercase()).to.equal("AB123CD456") - expect("ABC".toLatinUppercase()).to.equal("ABC") - }) - - it("does not transform non latin characters to their uppercase version", function() { - expect("abαπ".toLatinUppercase()).to.equal("ABαπ") - expect("abαπ".toLatinUppercase()).to.not.equal("abαπ".toUpperCase()) - }) - }) - - describe("#String.removeEnclosure", function() { - it("is able to remove the first and last characters", function() { - expect("[1+t]".removeEnclosure()).to.equal("1+t") - expect('"a+b+c*d"'.removeEnclosure()).to.equal("a+b+c*d") - expect("(pi/2)".removeEnclosure()).to.equal("pi/2") - }) - }) - - describe("#Number.toDecimalPrecision", function() { - it("rounds a number to a fixed decimal precision", function() { - expect(123.456789.toDecimalPrecision()).to.equal(123) - expect(123.456789.toDecimalPrecision(1)).to.equal(123.5) - expect(123.456789.toDecimalPrecision(2)).to.equal(123.46) - expect(123.456789.toDecimalPrecision(3)).to.equal(123.457) - expect(123.456789.toDecimalPrecision(4)).to.equal(123.4568) - expect(123.456789.toDecimalPrecision(5)).to.equal(123.45679) - expect(123.456789.toDecimalPrecision(6)).to.equal(123.456789) - expect(123.111111.toDecimalPrecision(5)).to.equal(123.11111) - }) - }) -}) - -describe("Lib/Utils", function() { - describe("#textsup", function() { - it("transforms characters which have a sup unicode equivalent", function() { - expect(textsup("-+=()")).to.equal("⁻⁺⁼⁽⁾") - expect(textsup("0123456789")).to.equal("⁰¹²³⁴⁵⁶⁷⁸⁹") - expect(textsup("abcdefghijklmnoprstuvwxyz")).to.equal("ᵃᵇᶜᵈᵉᶠᵍʰⁱʲᵏˡᵐⁿᵒᵖʳˢᵗᵘᵛʷˣʸᶻ") - }) - - it("does not transform characters without a sup equivalent", function() { - expect(textsup("ABCDEFGHIJKLMNOPQRSTUVWXYZq")).to.equal("ABCDEFGHIJKLMNOPQRSTUVWXYZq") - }) - - it("partially transforms strings which only have a few characters with a sup equivalent", function() { - expect(textsup("ABCabcABC")).to.equal("ABCᵃᵇᶜABC") - }) - }) - - describe("#textsub", function() { - it("transforms characters which have a sub unicode equivalent", function() { - expect(textsub("-+=()")).to.equal("₋₊₌₍₎") - expect(textsub("0123456789")).to.equal("₀₁₂₃₄₅₆₇₈₉") - expect(textsub("aehijklmnoprstuvx")).to.equal("ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓ") - }) - - it("does not transform characters without a sub equivalent", function() { - expect(textsub("ABCDEFGHIJKLMNOPQRSTUVWXYZ")).to.equal("ABCDEFGHIJKLMNOPQRSTUVWXYZ") - expect(textsub("bcdfgqyz")).to.equal("bcdfgqyz") - }) - - it("partially transforms strings which only have a few characters with a sub equivalent", function() { - expect(textsub("ABC123ABC")).to.equal("ABC₁₂₃ABC") - }) - }) - - describe("#parseName", function() { - it("parses greek letter names", function() { - const shorthands = { - "α": ["al", "alpha"], - "β": ["be", "beta"], - "γ": ["ga", "gamma"], - "δ": ["de", "delta"], - "ε": ["ep", "epsilon"], - "ζ": ["ze", "zeta"], - "η": ["et", "eta"], - "θ": ["th", "theta"], - "κ": ["ka", "kappa"], - "λ": ["la", "lambda"], - "μ": ["mu"], - "ν": ["nu"], - "ξ": ["xi"], - "ρ": ["rh", "rho"], - "σ": ["si", "sigma"], - "τ": ["ta", "tau"], - "υ": ["up", "upsilon"], - "φ": ["ph", "phi"], - "χ": ["ch", "chi"], - "ψ": ["ps", "psi"], - "ω": ["om", "omega"], - "Γ": ["gga", "ggamma"], - "Δ": ["gde", "gdelta"], - "Θ": ["gth", "gtheta"], - "Λ": ["gla", "glambda"], - "Ξ": ["gxi"], - "Π": ["gpi"], - "Σ": ["gsi", "gsigma"], - "Φ": ["gph", "gphi"], - "Ψ": ["gps", "gpsi"], - "Ω": ["gom", "gomega"], - } - for(const [char, shorts] of Object.entries(shorthands)) { - expect(parseName(char)).to.equal(char) - for(const short of shorts) - expect(parseName(short)).to.equal(char) - } - }) - - it("parses array elements into sub", function() { - expect(parseName("u[n+1]")).to.equal("uₙ₊₁") - expect(parseName("u[(n+x)]")).to.equal("u₍ₙ₊ₓ₎") - expect(parseName("u[A]")).to.equal("uA") - }) - - it("removes disallowed characters when indicated", function() { - const disallowed = "xnπℝℕ\\∪∩[] ()^/^/÷*×+=1234567890¹²³⁴⁵⁶⁷⁸⁹⁰-" - expect(parseName(disallowed)).to.equal("") - expect(parseName("AA" + disallowed)).to.equal("AA") - expect(parseName(disallowed, false)).to.equal(disallowed) - }) - - it("is able to do all three at once", function() { - expect(parseName("al[n+1]+n")).to.equal("αₙ₊₁") - expect(parseName("al[n+1]+n", false)).to.equal("αₙ₊₁+n") - }) - }) - - describe("#getRandomColor", function() { - it("provides a valid color", function() { - const colorReg = /^#[A-F\d]{6}$/ - for(let i = 0; i < 50; i++) - expect(getRandomColor()).to.match(colorReg) - }) - }) - - describe("#escapeHTML", function() { - it("escapes ampersands", function() { - expect(escapeHTML("&")).to.equal("&") - expect(escapeHTML("High & Mighty")).to.equal("High & Mighty") - }) - - it("escapes injected HTML tags", function() { - expect(escapeHTML("")).to.equal("<script>alert('Injected!')</script>") - expect(escapeHTML('Link')).to.equal('<a href="javascript:alert()">Link</a>') - }) - }) - - describe("#exponentsToExpression", function() { - it("transforms exponents to power expression", function() { - expect(exponentsToExpression("x¹²³⁴⁵⁶⁷⁸⁹⁰")).to.equal("x^1234567890") - expect(exponentsToExpression("x¹²+y³⁴")).to.equal("x^12+y^34") - }) - }) -}) diff --git a/common/test/hooks.mjs b/common/test/hooks.mjs deleted file mode 100644 index 39a25d7..0000000 --- a/common/test/hooks.mjs +++ /dev/null @@ -1,36 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ -import * as fs from "./mock/fs.mjs" -import Qt from "./mock/qt.mjs" -import { MockHelper } from "./mock/helper.mjs" -import { MockLatex } from "./mock/latex.mjs" - -import { use } from "chai" -import spies from "chai-spies" -import promised from "chai-as-promised" - -function setup() { - use(promised) - const { spy } = use(spies) - - globalThis.Helper = new MockHelper() - globalThis.Latex = new MockLatex() - globalThis.chaiPlugins = { spy } -} - -setup() diff --git a/common/test/math/domain.mjs b/common/test/math/domain.mjs deleted file mode 100644 index b9161c8..0000000 --- a/common/test/math/domain.mjs +++ /dev/null @@ -1,108 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { describe, it } from "mocha" -import { expect } from "chai" - -import { Domain, EmptySet, parseDomainSimple } from "../../src/math/domain.mjs" - -describe("math.domain", function() { - describe("#parseDomainSimple", function() { - it("returns empty sets when a domain cannot be parsed", function() { - expect(parseDomainSimple("∅")).to.be.an.instanceof(EmptySet) - expect(parseDomainSimple("O")).to.be.an.instanceof(EmptySet) - expect(parseDomainSimple("AAAAAAAAA")).to.be.an.instanceof(EmptySet) - expect(parseDomainSimple("???")).to.be.an.instanceof(EmptySet) - expect(parseDomainSimple("∅").latexMarkup).to.equal("\\emptyset") - expect(parseDomainSimple("???").toString()).to.equal("∅") - expect(parseDomainSimple("∅").includes(0)).to.be.false - expect(parseDomainSimple("∅").includes(Infinity)).to.be.false - expect(parseDomainSimple("∅").includes(-3)).to.be.false - - }) - - it("returns predefined domains", function() { - const predefinedToCheck = [ - // Real domains - { domain: Domain.R, shortcuts: ["R", "ℝ"] }, - // Zero exclusive real domains - { domain: Domain.RE, shortcuts: ["RE", "R*", "ℝ*"] }, - // Real positive domains - { domain: Domain.RP, shortcuts: ["RP", "R+", "ℝ⁺", "ℝ+"] }, - // Zero-exclusive real positive domains - { domain: Domain.RPE, shortcuts: ["RPE", "REP", "R+*", "R*+", "ℝ*⁺", "ℝ⁺*", "ℝ*+", "ℝ+*"] }, - // Real negative domain - { domain: Domain.RM, shortcuts: ["RM", "R-", "ℝ⁻", "ℝ-"] }, - // Zero-exclusive real negative domains - { domain: Domain.RME, shortcuts: ["RME", "REM", "R-*", "R*-", "ℝ⁻*", "ℝ*⁻", "ℝ-*", "ℝ*-"] }, - // Natural integers domain - { domain: Domain.N, shortcuts: ["ℕ", "N", "ZP", "Z+", "ℤ⁺", "ℤ+"] }, - // Zero-exclusive natural integers domain - { domain: Domain.NE, shortcuts: ["NE", "NP", "N*", "N+", "ℕ*", "ℕ⁺", "ℕ+", "ZPE", "ZEP", "Z+*", "Z*+", "ℤ⁺*", "ℤ*⁺", "ℤ+*", "ℤ*+"] }, - // Logarithmic natural domains - { domain: Domain.NLog, shortcuts: ["NLOG", "ℕˡᵒᵍ", "ℕLOG"] }, - // All integers domains - { domain: Domain.Z, shortcuts: ["Z", "ℤ"] }, - // Zero-exclusive all integers domain - { domain: Domain.ZE, shortcuts: ["ZE", "Z*", "ℤ*"] }, - // Negative integers domain - { domain: Domain.ZM, shortcuts: ["ZM", "Z-", "ℤ⁻", "ℤ-"] }, - // Zero-exclusive negative integers domain - { domain: Domain.ZME, shortcuts: ["ZME", "ZEM", "Z-*", "Z*-", "ℤ⁻*", "ℤ*⁻", "ℤ-*", "ℤ*-"] }, - ] - - // Real domains - for(const { domain, shortcuts } of predefinedToCheck) - for(const shortcut of shortcuts) - expect(parseDomainSimple(shortcut)).to.be.equal(domain) - }) - - it("returns parsed ranges", function() { - const parsedClosed = parseDomainSimple("[1;3]") - expect(parsedClosed.includes(1)).to.be.true - expect(parsedClosed.includes(2.4)).to.be.true - expect(parsedClosed.includes(3)).to.be.true - expect(parsedClosed.includes(3.01)).to.be.false - expect(parsedClosed.includes(0.99)).to.be.false - const parsedOpen = parseDomainSimple("]1;3[") - expect(parsedOpen.includes(1)).to.be.false - expect(parsedOpen.includes(3)).to.be.false - expect(parsedOpen.includes(2.4)).to.be.true - expect(parsedOpen.includes(1.01)).to.be.true - expect(parsedOpen.includes(2.99)).to.be.true - const parsedOpenBefore = parseDomainSimple("]1;3]") - expect(parsedOpenBefore.includes(1)).to.be.false - expect(parsedOpenBefore.includes(3)).to.be.true - expect(parsedOpenBefore.includes(2.4)).to.be.true - expect(parsedOpenBefore.includes(1.01)).to.be.true - expect(parsedOpenBefore.includes(3.01)).to.be.false - const parsedOpenAfter = parseDomainSimple("[1;3[") - expect(parsedOpenAfter.includes(1)).to.be.true - expect(parsedOpenAfter.includes(3)).to.be.false - expect(parsedOpenAfter.includes(2.4)).to.be.true - expect(parsedOpenAfter.includes(0.99)).to.be.false - expect(parsedOpenAfter.includes(2.99)).to.be.true - }) - - it("does not parse invalid ranges", function() { - expect(() => parseDomainSimple("]1;2;3[")).to.throw - expect(() => parseDomainSimple("]1,2;3[")).to.throw - expect(() => parseDomainSimple("](12);3[")).to.throw - }) - }) -}) diff --git a/common/test/math/expression.mjs b/common/test/math/expression.mjs deleted file mode 100644 index 588480c..0000000 --- a/common/test/math/expression.mjs +++ /dev/null @@ -1,181 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -// Load prior tests -import "../basics/utils.mjs" -import "../module/latex.mjs" -import "../module/expreval.mjs" -import "../module/objects.mjs" - -import { describe, it } from "mocha" -import { expect } from "chai" - -import { executeExpression, Expression } from "../../src/math/expression.mjs" -import ExprEval from "../../src/module/expreval.mjs" - - -describe("Math/Expression", function() { - describe("#constructor", function() { - it("accepts strings", function() { - expect(() => new Expression("2+3")).to.not.throw - expect(() => new Expression("x+2")).to.not.throw - }) - - it("accepts already parsed expressions", function() { - expect(() => new Expression(ExprEval.parse("2+3"))).to.not.throw - expect(() => new Expression(ExprEval.parse("x+2"))).to.not.throw - }) - - it("doesn't accept anything else", function() { - expect(() => new Expression()).to.throw("Cannot create an expression with undefined.") - expect(() => new Expression(12)).to.throw("Cannot create an expression with a Number.") - expect(() => new Expression({})).to.throw("Cannot create an expression with a Object.") - expect(() => new Expression(true)).to.throw("Cannot create an expression with a Boolean.") - }) - }) - - describe("#variables", function() { - it("returns a list of variables for non-constant expressions", function() { - expect(new Expression("x+1").variables()).to.deep.equal(["x"]) - expect(new Expression("x+n").variables()).to.deep.equal(["x", "n"]) - expect(new Expression("u[n] + A.x").variables()).to.deep.equal(["u", "n", "A"]) - }) - - it("returns an empty array if the expression is constant", function() { - expect(new Expression("2+1").variables()).to.deep.equal([]) - expect(new Expression("sin π").variables()).to.deep.equal([]) - expect(new Expression("e^3").variables()).to.deep.equal([]) - }) - }) - - describe("#isConstant", function() { - it("returns true if neither x nor n are included into the expression", function() { - expect(new Expression("2+1").isConstant()).to.be.true - expect(new Expression("e^3").isConstant()).to.be.true - expect(new Expression("2+f(3)").isConstant()).to.be.true - expect(new Expression("sin A.x").isConstant()).to.be.true - }) - - it("returns false if either x or n are included into the expression", function() { - expect(new Expression("2+x").isConstant()).to.be.false - expect(new Expression("e^n").isConstant()).to.be.false - expect(new Expression("2+f(x)").isConstant()).to.be.false - expect(new Expression("n + sin x").isConstant()).to.be.false - }) - }) - - describe("#requiredObjects", function() { - it("returns the list of objects that need to be registered for this expression", function() { - expect(new Expression("x^n").requiredObjects()).to.deep.equal([]) - expect(new Expression("2+f(3)").requiredObjects()).to.deep.equal(["f"]) - expect(new Expression("A.x+x").requiredObjects()).to.deep.equal(["A"]) - expect(new Expression("2+f(sin A.x)+n").requiredObjects()).to.deep.equal(["f", "A"]) - }) - }) - - describe.skip("#allRequirementsFulfilled", function() { - // TODO: Make tests for objects - }) - - describe.skip("#undefinedVariables", function() { - // TODO: Make tests for objects - }) - - describe("#toEditableString", function() { - it("should return a readable expression", function() { - expect(new Expression("2+1").toEditableString()).to.equal("3") - expect(new Expression("2+x").toEditableString()).to.equal("(2 + x)") - expect(new Expression("x*2+x/3").toEditableString()).to.equal("((x * 2) + (x / 3))") - }) - - it("should be able to be reparsed and equal the same expression", function() { - const exprs = ["5", "x/2", "4/2", "sin x"] - for(const expr of exprs) { - const exprObj = new Expression(expr) - expect(new Expression(exprObj.toEditableString()).calc).to.deep.equal(exprObj.calc) - } - }) - }) - - describe("#execute", function() { - it("returns the result of the computation of the expression", function() { - expect(new Expression("2+3").execute()).to.equal(5) - expect(new Expression("2+3").execute(10)).to.equal(5) - expect(new Expression("2+x").execute(10)).to.equal(12) - expect(new Expression("sin x").execute(Math.PI)).to.be.approximately(0, Number.EPSILON) - }) - - it("returns the cached value if the expression can be cached", function() { - const exprs = ["2+3", "x/2", "4/2", "sin x"] - for(const expr of exprs) { - const exprObj = new Expression(expr) - if(exprObj.canBeCached) - expect(exprObj.execute()).to.equal(exprObj.cachedValue) - else - expect(exprObj.execute()).to.not.equal(exprObj.cachedValue) - } - }) - - it("throws an error if some variables are undefined.", function() { - expect(() => new Expression("x+n").execute()).to.throw("Undefined variable n.") - expect(() => new Expression("sin A.t").execute()).to.throw("Undefined variable A.") - expect(() => new Expression("f(3)").execute()).to.throw("Undefined variable f.") - }) - }) - - describe("#simplify", function() { - it("returns an expression with just the result when no constant or object are used", function() { - expect(new Expression("2+2").simplify(Math.PI/2)).to.deep.equal(new Expression("4")) - expect(new Expression("x+3").simplify(5)).to.deep.equal(new Expression("8")) - expect(new Expression("sin x").simplify(Math.PI/2)).to.deep.equal(new Expression("1")) - expect(new Expression("0*e^x").simplify(Math.PI/2)).to.deep.equal(new Expression("0")) - }) - - it("returns a simplified version of the expression if constants are used", function() { - const original = new Expression("e^x").simplify(2) - const to = new Expression("e^2") - expect(original.toEditableString()).to.deep.equal(to.toEditableString()) - }) - }) - - describe("#toString", function() { - it("returns a human readable string of the expression", function() { - expect(new Expression("-2-3").toString()).to.equal("-5") - expect(new Expression("0.2+0.1").toString()).to.equal("0.3") - expect(new Expression("sin x").toString()).to.equal("sin x") - expect(new Expression("sin π").toString()).to.equal("sin π") - }) - - it("should add a sign if the option is passed", function() { - expect(new Expression("-2-3").toString(true)).to.equal("-5") - expect(new Expression("2+3").toString(true)).to.equal("+5") - }) - }) - - describe("#executeExpression", function() { - it("directly computes the result of the expression with no variable", function() { - expect(executeExpression("2+3")).to.equal(5) - expect(executeExpression("sin (π/2)")).to.equal(1) - expect(executeExpression("e^3")).to.be.approximately(Math.pow(Math.E, 3), Number.EPSILON) - }) - - it("throws an error if variables are employed", function() { - expect(() => executeExpression("x+n")).to.throw("Undefined variable n.") - }) - }) -}) diff --git a/common/test/mock/canvas.mjs b/common/test/mock/canvas.mjs deleted file mode 100644 index 82bcbff..0000000 --- a/common/test/mock/canvas.mjs +++ /dev/null @@ -1,59 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - - -export class MockCanvas { - constructor(mockLoading = false) { - this.mockLoading = mockLoading - } - - getContext(context) { - throw new Error("MockCanvas.getContext not implemented") - } - - markDirty(rect) { - this.requestPaint() - } - - loadImageAsync(image) { - return new Promise((resolve, reject) => { - resolve() - }) - } - - /** - * Image loading is instantaneous. - * @param {string} image - * @return {boolean} - */ - isImageLoading(image) { - return this.mockLoading - } - - /** - * Image loading is instantaneous. - * @param {string} image - * @return {boolean} - */ - isImageLoaded(image) { - return !this.mockLoading - } - - requestPaint() { - } -} \ No newline at end of file diff --git a/common/test/mock/dialog.mjs b/common/test/mock/dialog.mjs deleted file mode 100644 index ec8628d..0000000 --- a/common/test/mock/dialog.mjs +++ /dev/null @@ -1,23 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -export class MockDialog { - constructor() {} - - show() {} -} \ No newline at end of file diff --git a/common/test/mock/fs.mjs b/common/test/mock/fs.mjs deleted file mode 100644 index 7cb00dd..0000000 --- a/common/test/mock/fs.mjs +++ /dev/null @@ -1,44 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { readFileSync as readNode } from "node:fs" -import { dirname } from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const __dirname = dirname(fileURLToPath(import.meta.url)) - -export const HOME = "/home/user" -export const TMP = "/tmp" - - -const filesystem = { - [`${HOME}/test1.lpf`]: readNode(__dirname + "/../../../ci/test1.lpf") -} - - -export function existsSync(file) { - return filesystem[file] !== undefined -} - -export function writeFileSync(file, data, encoding) { - filesystem[file] = Buffer.from(data, encoding) -} - -export function readFileSync(file, encoding) { - return filesystem[file].toString(encoding) -} \ No newline at end of file diff --git a/common/test/mock/helper.mjs b/common/test/mock/helper.mjs deleted file mode 100644 index 9186947..0000000 --- a/common/test/mock/helper.mjs +++ /dev/null @@ -1,116 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { readFileSync, writeFileSync, existsSync } from "./fs.mjs" - -const DEFAULT_SETTINGS = { - "check_for_updates": true, - "reset_redo_stack": true, - "last_install_greet": "0", - "enable_latex": true, - "enable_latex_threaded": true, - "expression_editor": { - "autoclose": true, - "colorize": true, - "color_scheme": 0 - }, - "autocompletion": { - "enabled": true - }, - "default_graph": { - "xzoom": 100, - "yzoom": 10, - "xmin": 5 / 10, - "ymax": 25, - "xaxisstep": "4", - "yaxisstep": "4", - "xlabel": "", - "ylabel": "", - "linewidth": 1, - "textsize": 18, - "logscalex": true, - "showxgrad": true, - "showygrad": true - } -} - -export class MockHelper { - constructor() { - this.__settings = { ...DEFAULT_SETTINGS } - } - - - /** - * Gets a setting from the config - * @param {string} settingName - Setting (and its dot-separated namespace) to get (e.g. "default_graph.xmin") - * @returns {string|number|boolean} Value of the setting - */ - getSetting(settingName) { - const namespace = settingName.split(".") - let data = this.__settings - for(const name of namespace) - if(data.hasOwnProperty(name)) - data = data[name] - else - throw new Error(`Setting ${namespace} does not exist.`) - return data - } - - /** - * Sets a setting in the config - * @param {string} settingName - Setting (and its dot-separated namespace) to set (e.g. "default_graph.xmin") - * @param {string|number|boolean} value - */ - setSetting(settingName, value) { - const namespace = settingName.split(".") - const finalName = namespace.pop() - let data = this.__settings - for(const name of namespace) - if(data.hasOwnProperty(name)) - data = data[name] - else - throw new Error(`Setting ${namespace} does not exist.`) - data[finalName] = value - } - - /** - * Sends data to be written - * @param {string} file - * @param {string} dataToWrite - just JSON encoded, requires the "LPFv1" mime to be added before writing - */ - write(file, dataToWrite) { - writeFileSync(file, "LPFv1" + dataToWrite) - } - - /** - * Requests data to be read from a file - * @param {string} file - * @returns {string} the loaded data - just JSON encoded, requires the "LPFv1" mime to be stripped - */ - load(file) { - if(existsSync(file)) { - const data = readFileSync(file, "utf8") - if(data.startsWith("LPFv1")) - return data.substring(5) - else - throw new Error(`Invalid LogarithmPlotter file.`) - } else - throw new Error(`File not found.`) - } - -} diff --git a/common/test/mock/latex.mjs b/common/test/mock/latex.mjs deleted file mode 100644 index 54d427d..0000000 --- a/common/test/mock/latex.mjs +++ /dev/null @@ -1,101 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import { TMP, existsSync, writeFileSync } from "./fs.mjs" - -const PIXEL = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVR4AWNgAAAAAgABc3UBGAAAAABJRU5ErkJggg==" - -export class MockLatex { - constructor() { - this.supportsAsyncRender = true - } - - /** - * Creates a simple string hash. - * @param {string} string - * @return {number} - * @private - */ - __hash(string) { - let hash = 0 - let i, chr - if(string.length === 0) return hash - for(i = 0; i < string.length; i++) { - chr = string.charCodeAt(i) - hash = ((hash << 5) - hash) + chr - hash |= 0 // Convert to 32bit integer - } - return hash - } - - /** - * - * @param {string} markup - * @param {number} fontSize - * @param {string} color - * @return {string} - * @private - */ - __getFileName(markup, fontSize, color) { - const name = this.__hash(`${markup}_${fontSize}_${color}`) - return `${TMP}/${name}.png` - } - - /** - * @param {string} markup - LaTeX markup to render - * @param {number} fontSize - Font size (in pt) to render - * @param {string} color - Color of the text to render - * @returns {Promise} - Comma separated data of the image (source, width, height) - */ - async renderAsync(markup, fontSize, color) { - return this.renderSync(markup, fontSize, color) - } - - /** - * @param {string} markup - LaTeX markup to render - * @param {number} fontSize - Font size (in pt) to render - * @param {string} color - Color of the text to render - * @returns {string} - Comma separated data of the image (source, width, height) - */ - renderSync(markup, fontSize, color) { - const file = this.__getFileName(markup, fontSize, color) - writeFileSync(file, PIXEL, "base64") - return `${file},1,1` - } - - /** - * @param {string} markup - LaTeX markup to render - * @param {number} fontSize - Font size (in pt) to render - * @param {string} color - Color of the text to render - * @returns {string} - Comma separated data of the image (source, width, height) - */ - findPrerendered(markup, fontSize, color) { - const file = this.__getFileName(markup, fontSize, color) - if(existsSync(file)) - return `${file},1,1` - return "" - } - - /** - * Checks if the Latex installation is valid - * @returns {boolean} - */ - checkLatexInstallation() { - return true // We're not *actually* doing any latex. - } -} diff --git a/common/test/mock/qt.mjs b/common/test/mock/qt.mjs deleted file mode 100644 index 46f5b3d..0000000 --- a/common/test/mock/qt.mjs +++ /dev/null @@ -1,69 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -// Mock qt methods. - -/** - * Polyfill for Qt.rect. - * @param {number} x - * @param {number} y - * @param {number} width - * @param {number} height - * @returns {{x, width, y, height}} - */ -function rect(x, y, width, height) { - return { x, y, width, height } -} - -/** - * Mock for QT_TRANSLATE_NOOP and qsTranslate - * @param {string} category - * @param {string} string - * @return {string} - */ -function QT_TRANSLATE_NOOP(category, string) { - return string -} - -/** - * Polyfilling Qt arg function. - * @param {string} argument - */ -String.prototype.arg = function(argument) { - for(let i = 0; i < 10; i++) - if(this.includes("%"+i)) - return this.replaceAll("%" + i, argument) - throw new Error("Too many arguments used.") -} - -function setup() { - globalThis.Qt = { - rect - } - - globalThis.QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP - globalThis.qsTranslate = QT_TRANSLATE_NOOP -} - -setup() - -export default { - rect, - QT_TRANSLATE_NOOP, - qtTranslate: QT_TRANSLATE_NOOP, -} \ No newline at end of file diff --git a/common/test/mock/root.mjs b/common/test/mock/root.mjs deleted file mode 100644 index 0539342..0000000 --- a/common/test/mock/root.mjs +++ /dev/null @@ -1,61 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -/** - * Mock for root element with width and height property. - * setWidth, setHeight, getWidth, and getHeight methods can be spied on to check - * when the accessor is called. - */ -export class MockRootElement { - #width = 0 - #height = 0 - - constructor() {} - - setWidth(width) { - this.#width = width; - } - - getWidth() { - return this.#width - } - - setHeight(height) { - this.#height = height; - } - - getHeight() { - return this.#height - } - - get width() { - return this.getWidth() - } - - set width(value) { - this.setWidth(value) - } - - get height() { - return this.getHeight() - } - - set height(value) { - this.setHeight(value) - } -} \ No newline at end of file diff --git a/common/test/module/base.mjs b/common/test/module/base.mjs deleted file mode 100644 index 0cc73ea..0000000 --- a/common/test/module/base.mjs +++ /dev/null @@ -1,89 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -// Load prior tests -import "../basics/events.mjs" -import "../basics/interface.mjs" - -import { describe, it } from "mocha" -import { expect } from "chai" -import { MockDialog } from "../mock/dialog.mjs" -import { BOOLEAN, DialogInterface, FUNCTION, NUMBER, STRING } from "../../src/module/interface.mjs" -import { Module } from "../../src/module/common.mjs" - -class MockModule extends Module { - constructor() { - super("mock", { - number: NUMBER, - bool: BOOLEAN, - str: STRING, - func: FUNCTION, - dialog: DialogInterface - }) - } -} - -describe("Module/Base", function() { - it("defined a Modules global", function() { - expect(globalThis.Modules).to.not.be.undefined - }) - - it("is not be initialized upon construction", function() { - const module = new MockModule() - expect(module.name).to.equal("mock") - expect(module.initialized).to.be.false - }) - - it("is only be initialized with the right arguments", function() { - const module = new MockModule() - const initializeWithNoArg = () => module.initialize({}) - const initializeWithSomeArg = () => module.initialize({ number: 0, str: "" }) - const initializeWithWrongType = () => module.initialize({ - number: () => {}, - str: 0, - bool: "", - func: false, - dialog: null - }) - const initializeProperly = () => module.initialize({ - number: 0, - str: "", - bool: true, - func: FUNCTION, - dialog: new MockDialog() - }) - expect(initializeWithNoArg).to.throw(Error) - expect(initializeWithSomeArg).to.throw(Error) - expect(initializeWithWrongType).to.throw(Error) - expect(initializeProperly).to.not.throw(Error) - expect(module.initialized).to.be.true - }) - - it("cannot be initialized twice", function() { - const module = new MockModule() - const initialize = () => module.initialize({ - number: 0, - str: "", - bool: true, - func: FUNCTION, - dialog: new MockDialog() - }) - expect(initialize).to.not.throw(Error) - expect(initialize).to.throw(Error) - }) -}) \ No newline at end of file diff --git a/common/test/module/expreval.mjs b/common/test/module/expreval.mjs deleted file mode 100644 index c04290e..0000000 --- a/common/test/module/expreval.mjs +++ /dev/null @@ -1,389 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -// Load prior tests -import "./base.mjs" - -import { describe, it } from "mocha" -import { expect } from "chai" - -import ExprEval from "../../src/module/expreval.mjs" - -describe("Module/ExprEval", function() { - describe("#parse.evaluate", function() { - const evaluate = (expr, vals = {}) => ExprEval.parse(expr).evaluate(vals) - it("parses simple mathematical expressions", function() { - expect(evaluate(`"\\'\\"\\\\\\/\\b\\f\\n\\r\\t\\u3509"`)).to.equal(`'"\\/\b\f\n\r\t\u3509`) - expect(evaluate("1")).to.equal(1) - expect(evaluate(" 1 ")).to.equal(1) - expect(evaluate("0xFF")).to.equal(255) - expect(evaluate("0b11")).to.equal(3) - expect(evaluate("-1")).to.equal(-1) - expect(evaluate("-(-1)")).to.equal(1) - expect(evaluate("+(-1)")).to.equal(-1) - expect(evaluate("3!")).to.equal(6) - expect(evaluate("1+1")).to.equal(2) - expect(evaluate("4*3")).to.equal(12) - expect(evaluate("4•3")).to.equal(12) - expect(evaluate("64/4")).to.equal(16) - expect(evaluate("2^10")).to.equal(1024) - expect(evaluate("10%3")).to.equal(1) - expect(evaluate("10%3")).to.equal(1) - // Test priorities - expect(evaluate("10*10+10*10")).to.equal(200) - expect(evaluate("10/10+10/10")).to.equal(2) - expect(evaluate("10/10+10/10")).to.equal(2) - expect(evaluate("2^2-2^2")).to.equal(0) - expect(evaluate("(2^2-2)^2")).to.equal(4) - }) - - it("parses equality and test statements", function() { - expect(evaluate("10%3 == 1 ? 2 : 1")).to.equal(2) - expect(evaluate("not(10%3 == 1) ? 2 : 1")).to.equal(1) - expect(evaluate("10%3 != 1 ? 2 : 1")).to.equal(1) - expect(evaluate("10 < 3 ? 2 : 1")).to.equal(1) - expect(evaluate("10 > 3 ? (2+1) : 1")).to.equal(3) - expect(evaluate("10 <= 3 ? 4 : 1")).to.equal(1) - expect(evaluate("10 >= 3 ? 4 : 1")).to.equal(4) - // Check equality - expect(evaluate("10 < 10 ? 2 : 1")).to.equal(1) - expect(evaluate("10 > 10 ? 2 : 1")).to.equal(1) - expect(evaluate("10 <= 10 ? 4 : 1")).to.equal(4) - expect(evaluate("10 >= 10 ? 4 : 1")).to.equal(4) - // Check 'and' and 'or' - expect(evaluate("10 <= 3 and 10 < 10 ? 4 : 1")).to.equal(1) - expect(evaluate("10 <= 10 and 10 < 10 ? 4 : 1")).to.equal(1) - expect(evaluate("10 <= 10 and 10 < 20 ? 4 : 1")).to.equal(4) - expect(evaluate("10 <= 3 or 10 < 10 ? 4 : 1")).to.equal(1) - expect(evaluate("10 <= 10 or 10 < 10 ? 4 : 1")).to.equal(4) - expect(evaluate("10 <= 10 or 10 < 20 ? 4 : 1")).to.equal(4) - }) - - it("parses singular function operators (functions with one arguments and no parenthesis)", function() { - // Trigonometric functions - expect(evaluate("sin 0")).to.be.approximately(0, Number.EPSILON) - expect(evaluate("cos 0")).to.be.approximately(1, Number.EPSILON) - expect(evaluate("tan 0")).to.be.approximately(0, Number.EPSILON) - expect(evaluate("asin 1")).to.be.approximately(Math.PI / 2, Number.EPSILON) - expect(evaluate("acos 1")).to.be.approximately(0, Number.EPSILON) - expect(evaluate("atan 1")).to.be.approximately(Math.PI / 4, Number.EPSILON) - expect(evaluate("sinh 1")).to.be.approximately(Math.sinh(1), Number.EPSILON) - expect(evaluate("cosh 1")).to.be.approximately(Math.cosh(1), Number.EPSILON) - expect(evaluate("tanh 1")).to.be.approximately(Math.tanh(1), Number.EPSILON) - expect(evaluate("asinh 1")).to.be.approximately(Math.asinh(1), Number.EPSILON) - expect(evaluate("acosh 1")).to.be.approximately(Math.acosh(1), Number.EPSILON) - expect(evaluate("atanh 0.5")).to.be.approximately(Math.atanh(0.5), Number.EPSILON) - // Reverse trigonometric - expect(evaluate("asin sin 1")).to.be.approximately(1, Number.EPSILON) - expect(evaluate("acos cos 1")).to.be.approximately(1, Number.EPSILON) - expect(evaluate("atan tan 1")).to.be.approximately(1, Number.EPSILON) - expect(evaluate("asinh sinh 1")).to.be.approximately(1, Number.EPSILON) - expect(evaluate("acosh cosh 1")).to.be.approximately(1, Number.EPSILON) - expect(evaluate("atanh tanh 1")).to.be.approximately(1, Number.EPSILON) - // Other functions - expect(evaluate("sqrt 4")).to.be.approximately(2, Number.EPSILON) - expect(evaluate("sqrt 2")).to.be.approximately(Math.sqrt(2), Number.EPSILON) - expect(evaluate("cbrt 27")).to.be.approximately(3, Number.EPSILON) - expect(evaluate("cbrt 14")).to.be.approximately(Math.cbrt(14), Number.EPSILON) - expect(evaluate("log 1")).to.be.approximately(Math.log(1), Number.EPSILON) - expect(evaluate("ln 1")).to.be.approximately(Math.log(1), Number.EPSILON) - expect(evaluate("log2 8")).to.be.approximately(3, Number.EPSILON) - expect(evaluate("log10 100")).to.be.approximately(2, Number.EPSILON) - expect(evaluate("lg 100")).to.be.approximately(2, Number.EPSILON) - expect(evaluate("expm1 0")).to.be.approximately(0, Number.EPSILON) - expect(evaluate("expm1 10")).to.be.approximately(Math.expm1(10), Number.EPSILON) - expect(evaluate("log1p 0")).to.be.approximately(0, Number.EPSILON) - expect(evaluate("log1p 10")).to.be.approximately(Math.log1p(10), Number.EPSILON) - // Roundings/Sign transformations - expect(evaluate("abs -12.34")).to.equal(12.34) - expect(evaluate("abs 12.45")).to.equal(12.45) - expect(evaluate("ceil 12.45")).to.equal(13) - expect(evaluate("ceil 12.75")).to.equal(13) - expect(evaluate("ceil 12.0")).to.equal(12) - expect(evaluate("ceil -12.6")).to.equal(-12) - expect(evaluate("floor 12.45")).to.equal(12) - expect(evaluate("floor 12.75")).to.equal(12) - expect(evaluate("floor 12.0")).to.equal(12) - expect(evaluate("floor -12.2")).to.equal(-13) - expect(evaluate("round 12.45")).to.equal(12) - expect(evaluate("round 12.75")).to.equal(13) - expect(evaluate("round 12.0")).to.equal(12) - expect(evaluate("round -12.2")).to.equal(-12) - expect(evaluate("round -12.6")).to.equal(-13) - expect(evaluate("trunc 12.45")).to.equal(12) - expect(evaluate("trunc 12.75")).to.equal(12) - expect(evaluate("trunc 12.0")).to.equal(12) - expect(evaluate("trunc -12.2")).to.equal(-12) - expect(evaluate("exp 1")).to.be.approximately(Math.E, Number.EPSILON) - expect(evaluate("exp 10")).to.be.approximately(Math.pow(Math.E, 10), 1e-8) - expect(evaluate("length \"string\"")).to.equal(6) - expect(evaluate("sign 0")).to.equal(0) - expect(evaluate("sign -0")).to.equal(0) - expect(evaluate("sign -10")).to.equal(-1) - expect(evaluate("sign 80")).to.equal(1) - }) - - it("parses regular functions", function() { - for(let i = 0; i < 1000; i++) { - expect(evaluate("random()")).to.be.within(0, 1) - expect(evaluate("random(100)")).to.be.within(0, 100) - } - expect(evaluate("fac(3)")).to.equal(6) - expect(evaluate("fac(10)")).to.equal(3628800) - expect(evaluate("min(10, 20)")).to.equal(10) - expect(evaluate("min(-10, -20)")).to.equal(-20) - expect(evaluate("max(10, 20)")).to.equal(20) - expect(evaluate("max(-10, -20)")).to.equal(-10) - expect(evaluate("hypot(3, 4)")).to.equal(5) - expect(evaluate("pyt(30, 40)")).to.equal(50) - expect(evaluate("atan2(1, 1)")).to.be.approximately(Math.PI / 4, Number.EPSILON) - expect(evaluate("atan2(1, 0)")).to.be.approximately(Math.PI / 2, Number.EPSILON) - expect(evaluate("atan2(0, 1)")).to.be.approximately(0, Number.EPSILON) - expect(evaluate("if(10 == 10, 1, 0)")).to.be.approximately(1, Number.EPSILON) - expect(evaluate("if(10 != 10, 1, 0)")).to.be.approximately(0, Number.EPSILON) - expect(evaluate("gamma(10) == 9!")).to.be.true - expect(evaluate("Γ(30) == 29!")).to.be.true - expect(evaluate("Γ(25) == 23!")).to.be.false - expect(evaluate("roundTo(26.04)")).to.equal(26) - expect(evaluate("roundTo(26.04, 2)")).to.equal(26.04) - expect(evaluate("roundTo(26.04836432123, 5)")).to.equal(26.04836) - expect(evaluate("roundTo(26.04836432123, 5)")).to.equal(26.04836) - }) - - it("parses arrays and access their members", function() { - expect(evaluate("[6, 7, 9]")).to.have.lengthOf(3) - expect(evaluate("[6, 7, 9]")).to.deep.equal([6, 7, 9]) - expect(evaluate("[6, \"8\", 9]")).to.have.lengthOf(3) - expect(evaluate("[6, 7%2]")).to.deep.equal([6, 1]) - // Access array indices - expect(evaluate("[6, 7][1]")).to.equal(7) - expect(evaluate("[6, 7, 8, 9, 10][2*2-1]")).to.equal(9) - }) - - it("can apply functions to arrays", function() { - expect(evaluate("length [6, 7, 9]")).to.equal(3) - expect(evaluate("length [6, 7, 8, 9]")).to.equal(4) - expect(evaluate("[6, 7, 9]||[10,11,12]")).to.deep.equal([6, 7, 9, 10, 11, 12]) - expect(evaluate("6 in [6, 7, 9]")).to.be.true - expect(evaluate("2 in [6, 7, 9]")).to.be.false - expect(evaluate("min([10, 6, 7, 8, 9])")).to.equal(6) - expect(evaluate("max([6, 7, 8, 9, 2])")).to.equal(9) - }) - - it("throws errors when invalid function parameters are provided", function() { - expect(() => evaluate("max()")).to.throw() - expect(() => evaluate("min()")).to.throw() - }) - - it("parses constants", function() { - expect(evaluate("pi")).to.equal(Math.PI) - expect(evaluate("PI")).to.equal(Math.PI) - expect(evaluate("π")).to.equal(Math.PI) - expect(evaluate("e")).to.equal(Math.E) - expect(evaluate("E")).to.equal(Math.E) - expect(evaluate("true")).to.be.true - expect(evaluate("false")).to.be.false - // expect(evaluate("∞")).to.equal(Math.Infinity) - // expect(evaluate("infinity")).to.equal(Math.Infinity) - // expect(evaluate("Infinity")).to.equal(Math.Infinity) - }) - - it("can be provided variables", function() { - const u = [1, 2, 3, 4] - const x = 10 - const s_ = "string" - const f = (x) => x * 2 - expect(evaluate("u", { u })).to.deep.equal([...u]) - expect(evaluate("x", { x })).to.equal(x) - expect(evaluate("s_", { s_ })).to.equal(s_) - expect(evaluate("f", { f })).to.equal(f) - expect(evaluate("b", { b: true })).to.equal(true) - expect(evaluate("u[1]", { u })).to.equal(u[1]) - expect(evaluate("x/2", { x })).to.equal(x / 2) - expect(evaluate("f(2)", { f })).to.equal(f(2)) - expect(evaluate("if(x == f(2), u[0], s_)", { x, u, s_, f })).to.equal(s_) - }) - - it("can be provided objects", function() { - const obj = { execute: (x) => x * 3, x: 10, y: { cached: true, execute: () => 20 } } - expect(evaluate("O(3)+O(2)", { O: obj })).to.equal(9 + 6) - expect(evaluate("O.x+O.y", { O: obj })).to.equal(30) - }) - - it("throws errors when trying to use variables wrongly", function() { - const obj = { execute: (x) => x * 3 } - expect(() => evaluate("O()", { O: obj })).to.throw() - expect(() => evaluate("O.x", { O: obj })).to.throw() - expect(() => evaluate("x()", { x: 10 })).to.throw() - expect(() => evaluate("x")).to.throw() - expect(() => evaluate("n")).to.throw() - }) - - it("can do it all at once", function() { - const obj = { execute: (x) => x * 3, x: 20 } - const u = [1, 2, 3, 4] - const x = 10 - const s = "string" - const expr = "random(e) <= e ? fac(x)+u[2]+O(pi) : O.x+length s" - expect(evaluate(expr, { x, u, s, O: obj })).to.equal(3628803 + obj.execute(Math.PI)) - }) - - it("cannot parse invalid expressions", function() { - expect(() => evaluate("1+")).to.throw() - expect(() => evaluate("@")).to.throw() - expect(() => evaluate("]")).to.throw() - expect(() => evaluate("")).to.throw() - expect(() => evaluate(`"\\u35P2"`)).to.throw() - expect(() => evaluate(`"\\x"`)).to.throw() - }) - }) - - describe("#parse.toString", function() { - it("can be converted back into a string without changes", function() { - const expressions = ["pi+2*(e+2)^4", "sin(1+2!+pi+cos -3)^2", "[2,3,4][(2-1)*2]", "true ? false : true"] - for(const ogString of expressions) { - const expr = ExprEval.parse(ogString) - const convertedString = expr.toString() - expect(ExprEval.parse(convertedString)).to.deep.equal(expr) // Can be reparsed just the same - } - }) - }) - - describe("#parse.substitute", function() { - const parsed = ExprEval.parse("if(x == 0, 1, 2+x)") - it("can substitute a variable for a number", function() { - expect(parsed.substitute("x", 10).evaluate({})).to.equal(12) - expect(parsed.substitute("x", 0).evaluate({})).to.equal(1) - }) - - it("can substitute a variable for another", function() { - expect(parsed.substitute("x", "b").evaluate({ b: 10 })).to.equal(12) - expect(parsed.substitute("x", "b").evaluate({ b: 0 })).to.equal(1) - }) - - it("can substitute a variable for an expression", function() { - expect(parsed.substitute("x", "sin α").evaluate({ "α": Math.PI / 2 })).to.be.approximately(3, Number.EPSILON) - expect(parsed.substitute("x", "sin α").evaluate({ "α": 0 })).to.equal(1) - expect(parsed.substitute("x", "α == 1 ? 0 : 1").evaluate({ "α": 1 })).to.equal(1) - }) - }) - - describe("#parse.variables", function() { - it("can list all parsed undefined variables", function() { - expect(ExprEval.parse("a+b+x+pi+sin(b)").variables()).to.deep.equal(["a", "b", "x"]) - }) - }) - - describe("#parse.toJSFunction", function() { - const func = ExprEval.parse("not(false) ? a+b+x+1/x : x!+random()+A.x+[][0]").toJSFunction("x", { a: "10", b: "0" }) - expect(func(10)).to.equal(20.1) - expect(func(20)).to.equal(30.05) - }) - - - describe("#integral", function() { - it("returns the integral value between two integers", function() { - expect(ExprEval.integral(0, 1, "1", "t")).to.be.approximately(1, Number.EPSILON) - expect(ExprEval.integral(0, 1, "t", "t")).to.be.approximately(1 / 2, Number.EPSILON) - expect(ExprEval.integral(0, 1, "t^2", "t")).to.be.approximately(1 / 3, Number.EPSILON) - expect(ExprEval.integral(0, 1, "t^3", "t")).to.be.approximately(1 / 4, 0.01) - expect(ExprEval.integral(0, 1, "t^4", "t")).to.be.approximately(1 / 5, 0.01) - - expect(ExprEval.integral(10, 40, "1", "t")).to.equal(30) - expect(ExprEval.integral(20, 40, "1", "t")).to.equal(20) - - expect(ExprEval.integral(0, 10, { execute: (x) => 1 })).to.equal(10) - expect(ExprEval.integral(0, 10, { execute: (x) => x })).to.equal(50) - expect(ExprEval.integral(0, 1, { execute: (x) => Math.pow(x, 2) })).to.equal(1 / 3) - }) - - - it("throws error when provided with invalid arguments", function() { - const noArg1 = () => ExprEval.integral() - const noArg2 = () => ExprEval.integral(0) - const noFunction = () => ExprEval.integral(0, 1) - const invalidObjectProvided = () => ExprEval.integral(0, 1, { a: 2 }) - const notAnObjectProvided = () => ExprEval.integral(0, 1, "string") - const invalidFromProvided = () => ExprEval.integral("ze", 1, "t^2", "t") - const invalidToProvided = () => ExprEval.integral(0, "ze", "t^2", "t") - const notStringProvided1 = () => ExprEval.integral(0, 1, { a: 2 }, { b: 1 }) - const notStringProvided2 = () => ExprEval.integral(0, 1, { a: 2 }, "t") - const notStringProvided3 = () => ExprEval.integral(0, 1, "t^2", { b: 1 }) - const invalidVariableProvided = () => ExprEval.integral(0, 1, "t^2", "93IO74") - const invalidExpressionProvided = () => ExprEval.integral(0, 1, "t^2t", "t") - const invalidVariableInExpression = () => ExprEval.integral(0, 1, "t^2+x", "t") - expect(noArg1).to.throw() - expect(noArg2).to.throw() - expect(noFunction).to.throw() - expect(invalidObjectProvided).to.throw() - expect(invalidFromProvided).to.throw() - expect(invalidToProvided).to.throw() - expect(notAnObjectProvided).to.throw() - expect(notStringProvided1).to.throw() - expect(notStringProvided2).to.throw() - expect(notStringProvided3).to.throw() - expect(invalidVariableProvided).to.throw() - expect(invalidExpressionProvided).to.throw() - expect(invalidVariableInExpression).to.throw() - }) - }) - - describe("#derivative", function() { - const DELTA = 1e-5 - it("returns the derivative value of a function at a given number", function() { - expect(ExprEval.derivative("1", "t", 2)).to.be.approximately(0, DELTA) - expect(ExprEval.derivative("t", "t", 2)).to.be.approximately(1, DELTA) - expect(ExprEval.derivative("t^2", "t", 2)).to.be.approximately(4, DELTA) - expect(ExprEval.derivative("t^3", "t", 2)).to.be.approximately(12, DELTA) - expect(ExprEval.derivative("t^4", "t", 2)).to.be.approximately(32, DELTA) - - expect(ExprEval.derivative({ execute: (x) => 1 }, 10)).to.equal(0) - expect(ExprEval.derivative({ execute: (x) => x }, 10)).to.be.approximately(1, DELTA) - expect(ExprEval.derivative({ execute: (x) => Math.pow(x, 2) }, 10)).to.be.approximately(20, DELTA) - }) - - it("throws error when provided with invalid arguments", function() { - const noArg1 = () => ExprEval.derivative() - const noArg2 = () => ExprEval.derivative("1") - const noValue1 = () => ExprEval.derivative("0", "1") - const noValue2 = () => ExprEval.derivative({ execute: (x) => 1 }) - const invalidObjectProvided = () => ExprEval.derivative({ a: 2 }, 1) - const notAnObjectProvided = () => ExprEval.derivative("string", 1) - const invalidXProvided = () => ExprEval.derivative("t^2+x", "t", "ze") - const notStringProvided1 = () => ExprEval.derivative({ a: 2 }, { b: 1 }, 1) - const notStringProvided2 = () => ExprEval.derivative({ a: 2 }, "t", 1) - const notStringProvided3 = () => ExprEval.derivative("t^2", { b: 1 }, 1) - const invalidVariableProvided = () => ExprEval.derivative("t^2", "93IO74", 1) - const invalidExpressionProvided = () => ExprEval.derivative("t^2t", "t", 1) - const invalidVariableInExpression = () => ExprEval.derivative("t^2+x", "t", 1) - expect(noArg1).to.throw() - expect(noArg2).to.throw() - expect(noValue1).to.throw() - expect(noValue2).to.throw() - expect(invalidObjectProvided).to.throw() - expect(invalidXProvided).to.throw() - expect(notAnObjectProvided).to.throw() - expect(notStringProvided1).to.throw() - expect(notStringProvided2).to.throw() - expect(notStringProvided3).to.throw() - expect(invalidVariableProvided).to.throw() - expect(invalidExpressionProvided).to.throw() - expect(invalidVariableInExpression).to.throw() - }) - }) -}) \ No newline at end of file diff --git a/common/test/module/latex.mjs b/common/test/module/latex.mjs deleted file mode 100644 index 6581850..0000000 --- a/common/test/module/latex.mjs +++ /dev/null @@ -1,231 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -// Load prior tests -import "../basics/utils.mjs" -import "./base.mjs" -import "./expreval.mjs" - -import { describe, it } from "mocha" -import { expect } from "chai" -import { existsSync } from "../mock/fs.mjs" - -const { spy } = chaiPlugins - -import ExprEval from "../../src/module/expreval.mjs" -import LatexAPI from "../../src/module/latex.mjs" - -describe("Module/Latex", function() { - it("is defined as a global", function() { - expect(globalThis.Modules.Latex).to.equal(LatexAPI) - }) - - describe("#initialize", function() { - it("isn't enabled before initialization", function() { - expect(LatexAPI.enabled).to.be.false - }) - - it("is enabled after initialization", function() { - LatexAPI.initialize({ latex: Latex, helper: Helper }) - expect(LatexAPI.enabled).to.equal(Helper.getSetting("enable_latex")) - expect(LatexAPI.initialized).to.be.true - }) - }) - - describe("#requestAsyncRender", function() { - it("returns a render result with a valid source, a width, and a height", async function() { - const data = await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#AA0033") - expect(data).to.be.an("object") - expect(data.source).to.be.a("string") - expect(data.source).to.satisfy(existsSync) - expect(data.height).to.be.a("number") - expect(data.width).to.be.a("number") - }) - - it("calls functions from the LaTeX module", async function() { - const renderSyncSpy = spy.on(Latex, "renderSync") - const renderAsyncSpy = spy.on(Latex, "renderAsync") - Latex.supportsAsyncRender = true - await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#AA0033") - expect(renderAsyncSpy).to.have.been.called.once - expect(renderSyncSpy).to.have.been.called.once // Called async - Latex.supportsAsyncRender = false - await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#AA0033") - expect(renderAsyncSpy).to.have.been.called.once // From the time before - expect(renderSyncSpy).to.have.been.called.twice - Latex.supportsAsyncRender = true - }) - - it("should not reply with the same source for different markup, font size, or color.", async function() { - const datas = [ - await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#AA0033"), - await LatexAPI.requestAsyncRender("\\frac{x}{4}", 13, "#AA0033"), - await LatexAPI.requestAsyncRender("\\frac{x}{3}", 14, "#AA0033"), - await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#0033AA") - ] - const sources = datas.map(x => x.source) - expect(new Set(sources)).to.have.a.lengthOf(4) - }) - }) - - describe("#findPrerendered", function() { - it("returns the same data as async render for the same markup, font size, and color", async function() { - const data = await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#AA0033") - const found = LatexAPI.findPrerendered("\\frac{x}{3}", 13, "#AA0033") - expect(found).to.not.be.null - expect(found.source).to.equal(data.source) - expect(found.width).to.equal(data.width) - }) - - it("returns null if the markup hasn't been prerendered with the same markup, font size, and color", async function() { - await LatexAPI.requestAsyncRender("\\frac{x}{3}", 13, "#AA0033") - expect(LatexAPI.findPrerendered("\\frac{y}{3}", 13, "#AA0033")).to.be.null - expect(LatexAPI.findPrerendered("\\frac{x}{3}", 12, "#AA0033")).to.be.null - expect(LatexAPI.findPrerendered("\\frac{x}{3}", 13, "#3300AA")).to.be.null - }) - }) - - describe("#par", function() { - it("adds parentheses to strings", function() { - expect(LatexAPI.par("string")).to.equal("(string)") - expect(LatexAPI.par("aaaa")).to.equal("(aaaa)") - expect(LatexAPI.par("")).to.equal("()") - expect(LatexAPI.par("(example)")).to.equal("((example))") - }) - }) - - describe("#parif", function() { - it("adds parentheses to strings that contain one of the ones in the list", function() { - expect(LatexAPI.parif("string", ["+"])).to.equal("string") - expect(LatexAPI.parif("string+assert", ["+"])).to.equal("(string+assert)") - expect(LatexAPI.parif("string+assert", ["+", "-"])).to.equal("(string+assert)") - expect(LatexAPI.parif("string-assert", ["+", "-"])).to.equal("(string-assert)") - }) - - it("doesn't add new parentheses to strings that contains one of the ones in the list if they already have one", function() { - expect(LatexAPI.parif("(string+assert", ["+"])).to.equal("((string+assert)") - expect(LatexAPI.parif("string+assert)", ["+"])).to.equal("(string+assert))") - expect(LatexAPI.parif("(string+assert)", ["+"])).to.equal("(string+assert)") - expect(LatexAPI.parif("(string+assert)", ["+", "-"])).to.equal("(string+assert)") - expect(LatexAPI.parif("(string-assert)", ["+", "-"])).to.equal("(string-assert)") - }) - - it("doesn't add parentheses to strings that does not contains one of the ones in the list", function() { - expect(LatexAPI.parif("string", ["+"])).to.equal("string") - expect(LatexAPI.parif("string+assert", ["-"])).to.equal("string+assert") - expect(LatexAPI.parif("(string*assert", ["+", "-"])).to.equal("(string*assert") - expect(LatexAPI.parif("string/assert)", ["+", "-"])).to.equal("string/assert)") - }) - - it("removes parentheses from strings that does not contains one of the ones in the list", function() { - expect(LatexAPI.parif("(string)", ["+"])).to.equal("string") - expect(LatexAPI.parif("(string+assert)", ["-"])).to.equal("string+assert") - expect(LatexAPI.parif("((string*assert)", ["+", "-"])).to.equal("(string*assert") - expect(LatexAPI.parif("(string/assert))", ["+", "-"])).to.equal("string/assert)") - }) - }) - - describe("#variable", function() { - const from = [ - "α", "β", "γ", "δ", "ε", "ζ", "η", - "π", "θ", "κ", "λ", "μ", "ξ", "ρ", - "ς", "σ", "τ", "φ", "χ", "ψ", "ω", - "Γ", "Δ", "Θ", "Λ", "Ξ", "Π", "Σ", - "Φ", "Ψ", "Ω", "ₐ", "ₑ", "ₒ", "ₓ", - "ₕ", "ₖ", "ₗ", "ₘ", "ₙ", "ₚ", "ₛ", - "ₜ", "¹", "²", "³", "⁴", "⁵", "⁶", - "⁷", "⁸", "⁹", "⁰", "₁", "₂", "₃", - "₄", "₅", "₆", "₇", "₈", "₉", "₀", - "pi", "∞"] - const to = [ - "\\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"] - - it("converts unicode characters to their latex equivalent", function() { - for(let i = 0; i < from.length; i++) - expect(LatexAPI.variable(from[i])).to.include(to[i]) - }) - - it("wraps within dollar signs when the option is included", function() { - for(let i = 0; i < from.length; i++) { - expect(LatexAPI.variable(from[i], false)).to.equal(to[i]) - expect(LatexAPI.variable(from[i], true)).to.equal(`$${to[i]}$`) - } - }) - - it("can convert multiple of them", function() { - expect(LatexAPI.variable("α₂", false)).to.equal("\\alpha{}_{2}") - expect(LatexAPI.variable("∞piΠ", false)).to.equal("\\infty\\pi\\Pi") - }) - }) - - describe("#functionToLatex", function() { - it("transforms derivatives into latex fractions", function() { - const d1 = LatexAPI.functionToLatex("derivative", ["'3t'", "'t'", "x+2"]) - const d2 = LatexAPI.functionToLatex("derivative", ["f", "x+2"]) - expect(d1).to.equal("\\frac{d3x}{dx}") - expect(d2).to.equal("\\frac{df}{dx}(x+2)") - }) - - it("transforms integrals into latex limits", function() { - const i1 = LatexAPI.functionToLatex("integral", ["0", "x", "'3y'", "'y'"]) - const i2 = LatexAPI.functionToLatex("integral", ["1", "2", "f"]) - expect(i1).to.equal("\\int\\limits_{0}^{x}3y dy") - expect(i2).to.equal("\\int\\limits_{1}^{2}f(t) dt") - }) - - it("transforms sqrt functions to sqrt latex", function() { - const sqrt1 = LatexAPI.functionToLatex("sqrt", ["(x+2)"]) - const sqrt2 = LatexAPI.functionToLatex("sqrt", ["\\frac{x}{2}"]) - expect(sqrt1).to.equal("\\sqrt{x+2}") - expect(sqrt2).to.equal("\\sqrt{\\frac{x}{2}}") - }) - - it("transforms abs, floor and ceil", function() { - const abs = LatexAPI.functionToLatex("abs", ["x+3"]) - const floor = LatexAPI.functionToLatex("floor", ["x+3"]) - const ceil = LatexAPI.functionToLatex("ceil", ["x+3"]) - expect(abs).to.equal("\\left|x+3\\right|") - expect(floor).to.equal("\\left\\lfloor{x+3}\\right\\rfloor") - expect(ceil).to.equal("\\left\\lceil{x+3}\\right\\rceil") - }) - - it("transforms regular functions into latex", function() { - const f1 = LatexAPI.functionToLatex("f", ["x+3", true]) - const f2 = LatexAPI.functionToLatex("h_1", ["10"]) - expect(f1).to.equal("\\mathrm{f}\\left(x+3, true\\right)") - expect(f2).to.equal("\\mathrm{h_1}\\left(10\\right)") - }) - }) - - describe("#expression", function() { - it("transforms parsed expressions", function() { - const expr = ExprEval.parse("(+1! == 2/2 ? sin [-2.2][0] : f(t)^(1+1-1) + sqrt(A.t)) * 3 % 1") - const expected = "((((+1!))==(\\frac{2}{2}) ? (\\mathrm{sin}\\left(([(-2.2)][0])\\right)) : (\\mathrm{f}\\left(t\\right)^{1+1-1}+\\sqrt{A.t})) \\times 3) \\mathrm{mod} 1" - expect(LatexAPI.expression(expr.tokens)).to.equal(expected) - }) - }) -}) \ No newline at end of file diff --git a/common/test/module/objects.mjs b/common/test/module/objects.mjs deleted file mode 100644 index 73b9e25..0000000 --- a/common/test/module/objects.mjs +++ /dev/null @@ -1,31 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -// Load prior tests -import "./base.mjs" -import "../basics/utils.mjs" - -import { describe, it } from "mocha" -import { expect } from "chai" - -// import Objects from "../../src/module/objects.mjs" -// -// describe("Module/Objects", function() { -// describe("#getNewName", function() { -// }) -// }) \ No newline at end of file diff --git a/common/test/module/settings.mjs b/common/test/module/settings.mjs deleted file mode 100644 index b266bb2..0000000 --- a/common/test/module/settings.mjs +++ /dev/null @@ -1,101 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -// Load prior tests -import "./base.mjs" -import "../basics/utils.mjs" - -import { describe, it } from "mocha" -import { expect } from "chai" - -const { spy } = chaiPlugins - -import Settings from "../../src/module/settings.mjs" -import { BaseEvent } from "../../src/events.mjs" - -describe("Module/Settings", function() { - it("is defined as a global", function() { - expect(globalThis.Modules.Settings).to.equal(Settings) - }) - - it("has base defined properties even before initialization", function() { - expect(Settings.saveFilename).to.be.a("string") - expect(Settings.xzoom).to.be.a("number") - expect(Settings.yzoom).to.be.a("number") - expect(Settings.xmin).to.be.a("number") - expect(Settings.ymax).to.be.a("number") - expect(Settings.xaxisstep).to.be.a("string") - expect(Settings.yaxisstep).to.be.a("string") - expect(Settings.xlabel).to.be.a("string") - expect(Settings.ylabel).to.be.a("string") - expect(Settings.linewidth).to.be.a("number") - expect(Settings.textsize).to.be.a("number") - expect(Settings.logscalex).to.be.a("boolean") - expect(Settings.showxgrad).to.be.a("boolean") - expect(Settings.showygrad).to.be.a("boolean") - }) - - it("can be set values, but only of the right type", function() { - expect(() => Settings.set("xzoom", "", false)).to.throw() - expect(() => Settings.set("xlabel", true, false)).to.throw() - expect(() => Settings.set("showxgrad", 2, false)).to.throw() - - expect(() => Settings.set("xzoom", 200, false)).to.not.throw() - expect(() => Settings.set("xlabel", "x", false)).to.not.throw() - expect(() => Settings.set("showxgrad", false, false)).to.not.throw() - }) - - it("cannot be set unknown settings", function() { - expect(() => Settings.set("unknown", "", false)).to.throw() - }) - - it("sends an event when a value is set", function() { - const listener = spy((e) => { - expect(e).to.be.an.instanceof(BaseEvent) - expect(e.name).to.equal("changed") - expect(e.property).to.equal("xzoom") - expect(e.newValue).to.equal(300) - expect(e.byUser).to.be.true - }) - Settings.on("changed", listener) - Settings.set("xzoom", 300, true) - expect(listener).to.have.been.called.once - Settings.off("changed", listener) - }) - - it("requires a helper to set default values", function() { - spy.on(Settings, "set") - expect(() => Settings.initialize({})).to.throw() - expect(() => Settings.initialize({ helper: globalThis.Helper })).to.not.throw() - expect(Settings.set).to.have.been.called.exactly(13) - expect(Settings.set).to.not.have.been.called.with("saveFilename") - expect(Settings.set).to.have.been.called.with("xzoom") - expect(Settings.set).to.have.been.called.with("yzoom") - expect(Settings.set).to.have.been.called.with("xmin") - expect(Settings.set).to.have.been.called.with("ymax") - expect(Settings.set).to.have.been.called.with("xaxisstep") - expect(Settings.set).to.have.been.called.with("yaxisstep") - expect(Settings.set).to.have.been.called.with("xlabel") - expect(Settings.set).to.have.been.called.with("ylabel") - expect(Settings.set).to.have.been.called.with("linewidth") - expect(Settings.set).to.have.been.called.with("textsize") - expect(Settings.set).to.have.been.called.with("logscalex") - expect(Settings.set).to.have.been.called.with("showxgrad") - expect(Settings.set).to.have.been.called.with("showygrad") - }) -}) \ No newline at end of file diff --git a/assets/logplotterfile.svg b/linux/application-x-logarithm-plot.svg similarity index 100% rename from assets/logplotterfile.svg rename to linux/application-x-logarithm-plot.svg diff --git a/linux/debian/changelog b/linux/debian/changelog new file mode 100644 index 0000000..165844f --- /dev/null +++ b/linux/debian/changelog @@ -0,0 +1,113 @@ +logarithmplotter (0.1.8) stable; urgency=medium + + * New: There is now a user manual for LogarithmPlotter! Contributions apprecriated. + * Changed: A link to LogarithmPlotter's official website (https://apps.ad5001.eu/logarithmplotter/) has been added in the about dialog. + * Changed: A link to the user manual has been added both on the greeting screen and the `Help` menu. + * Added translation: User manual. + * Added translation: Official website. + * Added translation: Filtering for history browser. + * Fixed bug: The label position of X Cursors now display the label even when unexpected values are entered. + * Fixed bug: X Cursors target object in history are now properly rendered when no object were selected + * Fixed bug: Fixed slight clipping at the bottom of the border. + * Fixed bug: TextInput no longer allow to input forbidden characters for numbers. + * Fixed bug: ALT+ shortcuts on the menu bar now work. NOTE: May break some mobile configuration. Qt bug report: https://bugreports.qt.io/browse/QTBUG-100539 + * Fixed bug (flatpak): Buttons on side menu to create object now have proper width on startup. + * Internal changes: There is now a script to generate offline versions of the manual based on their online version. + * Internal changes: Sidebar button width is now fixed. + * Internal changes: Artifacts have been added to appstream metadata. + + -- Ad5001 Sat, 19 Jan 2022 20:00:00 +0100 + +logarithmplotter (0.1.7) stable; urgency=medium + + * New: The history browser has been completly redesigned, improving UX. + * New: The history browser now features a filter bar. + * New: All side panel tabs now have a visually identifiable scrollbar. + * Changed: Shorter rich text representations of history entries to improve readability. + * Changed: Usage of gradiants and icons to better identify history entries at a glance. + * Changed: History entries are now showing the whole label on several lines, instead of cutting it at the end. + * Changed: New history action for renaming. + * Changed: New history action for coloring. Note: color changing history items created in previous versions of LogarithmPlotter will not be updated to the new action. + * Changed: Tooltips for object creation buttons have been added. + * Changed: Tooltips have been set to have a delay of 200ms to match most software's handling of them. + * Changed: Object creation buttons now have a unified style accross all platforms. + * Added translation: History action of renaming objects. + * Added translation: History action of changing the color of objects. + * Added translation: Filtering for history browser. + * Fixed bug: Visibility history actions (shown and hidden) are now properly saved, solving the issue that when loading file with one, it's not automaticly changed to "Show". + * Fixed bug: Name changes history actions are now properly saved. + * Fixed bug: Non translated object type on the "+ Create new object" item selection combobox for Bode Magnitude and Phase. + * Fixed bug: Proper handling for future LogarithmPlotter files. + * Fixed bug: Shortcuts not being displayed in the menu bar are now properly shown. + * Fixed bug (flatpak): Black versions of the icons when using a black theme with the KDE SDK. + * Fixed bug (debian): Fixed launchpad building properly. + * Internal changes: Better organisation on icons. + * Internal changes: Historylib has been separated in several files. + * Internal changes: Trying to switch metainfo once more to try and fix another bug. + * Internal changes: Keywords added to metainfo. + + -- Ad5001 Thu, 03 Jan 2022 00:00:00 +0100 + +logarithmplotter (0.1.6) stable; urgency=medium + + * New: A new changelog popup is available at startup and in the help menu. + * Added translation: Object properties names. + * Added translation: Object properties enum values. + * Added translation: Object comments. + * Added translation: Most elements using a ":". + * Fixed bug: X Cursor's targets can now be set to null. + * Fixed bug: History now imports domains and objects properly. + * Fixed bug: Proper handling for future LogarithmPlotter files. + * Fixed bug (debian): Fixing bug that created a /build directory and didn't put the icons in the right directories. + * Other: Refractoring done on helper. + * Other: All QML elements are now properly commented. + * Other: Scripts have been moved to it's own directory. + + -- Ad5001 Sat, 29 Jan 2022 20:00:00 +0100 + +logarithmplotter (0.1.5) stable; urgency=medium + + * New: LogarithmPlotter has now better handling of very high values in logarithmic scale. + * Added translation for flatpak metadata, including translated image. + * [URGENT PATCH] Fixed bug: File saving dialog was not working. + * [URGENT PATCH] Fixed bug: Debian packages does include any language file. + * Fixed bug: X Cursor pointing does not detect any object. + + -- Ad5001 Wed, 26 Jan 2022 10:00:00 +0100 + +logarithmplotter (0.1.4) stable; urgency=medium + + * New feature: LogarithmPlotter detects unsaved changes. + * New feature: LogarithmPlotter is now translated! See https://hosted.weblate.org/engage/logarithmplotter/ to help. + * New translation: English by Ad5001: 100% + * New translation: French by Ad5001: 100% + * New translation: German by Ad5001: 100% + * New translation: Hungarian by Óvári (@ovari on github): 100% + * New translation: Norvegian by Allan Nordhøy (@comradekingu on github): 80% + * Fixed bug: No notification when closing LogarithmPlotter with unsaved changes. + * Fixed bug: π unavailable in symbols. + + -- Ad5001 Wed, 24 Jan 2022 20:00:00 +0100 + +logarithmplotter (0.1.3) stable; urgency=medium + + * Fixed bug: Confined packages (snapcraft & flatpak) won't show error messages related to update checks. + * FIxed bug: Equations of the form (x + y) / z were not being simplified properly. + + -- Ad5001 Wed, 19 Jan 2022 20:00:00 +0100 + +logarithmplotter (0.1.2) unstable; urgency=medium + + * Fixed bug: Unable to move Bode diagrams elements when having deleted the sum element. + * Fixed bug: Names were not not being changed from previous object when editing a new one. + * Fixed bug: Bode Magnitude was not drawn far enough + * Fixed bug: Bode Magnitude had undefined ending. + * Fixed other bugs from v0.1.1. + + -- Ad5001 Mon, 30 Sep 2021 20:00:00 +0100 + +logarithmplotter (0.1) UNRELEASED; urgency=medium + + * Initial release. + + -- Ad5001 Thu, 26 Aug 2021 08:48:28 +0100 diff --git a/linux/debian/control b/linux/debian/control new file mode 100644 index 0000000..1f05813 --- /dev/null +++ b/linux/debian/control @@ -0,0 +1,13 @@ +Package: logarithmplotter +Source: logarithmplotter +Version: 0.2.0 +Architecture: all +Maintainer: Ad5001 +Depends: python3, python3-pip, qml-module-qtquick-controls2 (>= 5.12.0), qml-module-qtmultimedia (>= 5.12.0), qml-module-qtgraphicaleffects (>= 5.12.0), qml-module-qtquick2 (>= 5.12.0), qml-module-qtqml-models2 (>= 5.12.0), qml-module-qtquick-controls (>= 5.12.0), python3-pyside2.qtcore (>= 5.12.0), python3-pyside2.qtqml (>= 5.12.0), python3-pyside2.qtgui (>= 5.12.0), python3-pyside2.qtquick (>= 5.12.0), python3-pyside2.qtwidgets (>= 5.12.0), python3-pyside2.qtmultimedia (>= 5.12.0), python3-pyside2.qtnetwork (>= 5.12.0), texlive-latex-base, dvipng + +Build-Depends: debhelper (>=11~), dh-python, dpkg-dev (>= 1.16.1~), python-setuptools, python3-all-dev (>=3.6) +Section: science +Priority: optional +Homepage: https://apps.ad5001.eu/logarithmplotter/ +Installed-Size: 174 +Description: Create graphs with logarithm scales. diff --git a/assets/native/linux/debian/copyright b/linux/debian/copyright similarity index 81% rename from assets/native/linux/debian/copyright rename to linux/debian/copyright index 138dae9..2d22f6e 100644 --- a/assets/native/linux/debian/copyright +++ b/linux/debian/copyright @@ -3,6 +3,6 @@ Upstream-Name: logarithmplotter Upstream-Contact: Ad5001 Files: * -Copyright: 2024, Ad5001 +Copyright: 2022, Ad5001 License: GPL-3.0+ diff --git a/linux/debian/depends b/linux/debian/depends new file mode 100644 index 0000000..53dcda3 --- /dev/null +++ b/linux/debian/depends @@ -0,0 +1 @@ +python3-pip, qml-module-qtquick-controls2 (>= 5.12.0), qml-module-qtmultimedia (>= 5.12.0), qml-module-qtgraphicaleffects (>= 5.12.0), qml-module-qtquick2 (>= 5.12.0), qml-module-qtqml-models2 (>= 5.12.0), qml-module-qtquick-controls (>= 5.12.0), python3-pyside2.qtcore (>= 5.12.0), python3-pyside2.qtqml (>= 5.12.0), python3-pyside2.qtgui (>= 5.12.0), python3-pyside2.qtquick (>= 5.12.0), python3-pyside2.qtwidgets (>= 5.12.0), python3-pyside2.qtmultimedia (>= 5.12.0), python3-pyside2.qtnetwork (>= 5.12.0), texlive-latex-base, dvipng diff --git a/assets/native/linux/debian/recommends b/linux/debian/recommends similarity index 100% rename from assets/native/linux/debian/recommends rename to linux/debian/recommends diff --git a/assets/native/linux/debian/rules b/linux/debian/rules similarity index 100% rename from assets/native/linux/debian/rules rename to linux/debian/rules diff --git a/linux/eu.ad5001.LogarithmPlotter.metainfo.xml b/linux/eu.ad5001.LogarithmPlotter.metainfo.xml new file mode 100644 index 0000000..ece209f --- /dev/null +++ b/linux/eu.ad5001.LogarithmPlotter.metainfo.xml @@ -0,0 +1,394 @@ + + + + eu.ad5001.LogarithmPlotter + eu.ad5001.LogarithmPlotter.desktop + logarithmplotter.desktop + CC0-1.0 + GPL-3.0+ + + LogarithmPlotter + http://apps.ad5001.eu/icons/apps/svg/logarithmplotter.svg + 2D logarithmic-scaled plotter software to make Bode plots, sequences and distribution functions + 2D-Grafiksoftware mit logarithmischer Skalierung zur Erstellung von Bode-Diagramms, Folgen und Verteilungsfunktionen + Logiciel de traçage 2D pour les diagrammes de Bode, les suites et les fonctions de répartition + Síkbeli ábrázolásszoftver Bode-ábrák, sorozatok és eloszlási funkciók készítéséhez + 2D-plotterprogramvare laget for opprettelse av Bode-diagram, sekvenser, og distribusjonsfunksjoner + + + +

+ LogarithmPlotter is, as it's name suggests, a plotter made with logarithm scales in mind. With an object system similar to Geogebra's, it allows dynamic creation of both logarithmic-scaled and non logarithmic-scaled plots with very few limitations. +

+

+ It's primary use is to quickly create asymptotic Bode plots, but it's extensible nature and ability to switch to non-logarithmic scales allow it to create other things with it, like sequences or statistical repartition functions. +

+

Features:

+
    +
  • Graphical objects (points, fonctions, Bode magnitudes...) management system
  • +
  • Complete object edition
  • +
  • Advanced history system
  • +
  • Diagram looks customisation
  • +
+

LogarithmPlotter is available in:

+
    +
  • 🇬🇧 English
  • +
  • 🇫🇷 French
  • +
  • 🇩🇪 German
  • +
  • 🇭🇺 Hungarian
  • +
  • 🇳🇴 Norwergian
  • +
+

Learn more: https://apps.ad5001.eu/logarithmplotter/

+

+ LogarithmPlotter est, comme son nom l'indique, un créateur de graphes et diagrammes 2D réalisé avec l'échelle logarithmique en tête. Avec un système d'objets similaire à Geogebra, ce qui lui permet de créer des graphes à échelle logarithmique et non logarithmique avec peu de limitations. +

+

+ Son intérêt principal est de permettre de créer des diagrammes asymptotiques de Bode, mais sa nature extensible et sa capacité à passer à une échelle non-logarithmique lui permet de créer d'autres choses. +

+

Fonctionnalités:

+
    +
  • Système de gestion des objets graphiques (points, fonctions, gains de Bode...)
  • +
  • Modification complète des objets
  • +
  • Système d'historique avancé
  • +
  • Personnalisation de l'apparence des diagrammes
  • +
+

LogarithmPlotter est disponible en:

+
    +
  • 🇬🇧 Anglais
  • +
  • 🇫🇷 Français
  • +
  • 🇩🇪 Allemand
  • +
  • 🇭🇺 Hongrois
  • +
  • 🇳🇴 Norvégien
  • +
+

En savoir plus: https://apps.ad5001.eu/fr/logarithmplotter/

+

+ A LogarithmPlotter egy logaritmus-ábrázoló, amely logaritmikus léptékek figyelembevételével készült. A Geogebrához hasonló objektumrendszerrel dinamikus parcellák létrehozását teszi lehetővé, nagyon kevés korlátozással. +

+

+ Elsődleges felhasználása az aszimptotikus Bode-ábrák gyors létrehozása, de bővíthető jellege és a nem logaritmikus skálákra váltás lehetősége lehetővé teszi, hogy más dolgokat is létrehozzon vele, például sorozatokat vagy statisztikai újraosztási függvényeket. +

+
+ + + Science + Education + Qt + + + https://apps.ad5001.eu/logarithmplotter/ + https://git.ad5001.eu/Ad5001/LogarithmPlotter/issues/ + https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/ + https://hosted.weblate.org/engage/logarithmplotter/ + + https://apps.ad5001.eu/img/full/logarithmplotter.png + https://apps.ad5001.eu/img/en/logarithmplotter/phase.png + https://apps.ad5001.eu/img/en/logarithmplotter/welcome.png + https://apps.ad5001.eu/img/de/gain.png + https://apps.ad5001.eu/img/de/logarithmplotter/phase.png + https://apps.ad5001.eu/img/de/logarithmplotter/welcome.png + https://apps.ad5001.eu/img/fr/gain.png + https://apps.ad5001.eu/img/fr/logarithmplotter/phase.png + https://apps.ad5001.eu/img/fr/logarithmplotter/welcome.png + https://apps.ad5001.eu/img/hu/gain.png + https://apps.ad5001.eu/img/hu/logarithmplotter/phase.png + https://apps.ad5001.eu/img/hu/logarithmplotter/welcome.png + https://apps.ad5001.eu/img/no/gain.png + https://apps.ad5001.eu/img/no/logarithmplotter/phase.png + https://apps.ad5001.eu/img/no/logarithmplotter/welcome.png + + + + + medium + + + + + xlarge + xsmall + + + + + +

Changes for v0.1.8:

+

New

+
    +
  • There is now a user manual for LogarithmPlotter! Contributions apprecriated.
  • +
+

Changes

+
    +
  • A link to LogarithmPlotter's official website has been added in the about dialog.
  • +
  • A link to the user manual has been added both on the greeting screen and the `Help` menu.
  • +
+

Added translations

+
    +
  • User manual.
  • +
  • Official website.
  • +
+

Fixed bugs

+
    +
  • The label position of X Cursors now display the label even when unexpected values are entered.
  • +
  • X Cursors target object in history are now properly rendered when no object were selected
  • +
  • Fixed slight clipping at the bottom of the border.
  • +
  • TextInput no longer allow to input forbidden characters for numbers.
  • +
  • ALT+ shortcuts on the menu bar now work. NOTE: May break some mobile configuration. Qt bug report
  • +
  • (flatpak) Buttons on side menu to create object now have proper width on startup.
  • +
+

Internal changes

+
    +
  • There is now a script to generate offline versions of the manual based on their online version.
  • +
  • Sidebar button width is now fixed.
  • +
  • Artifacts have been added to appstream metadata.
  • +
+
+ + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.8/logarithmplotter-v0.1.8-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.8/logarithmplotter-v0.1.8-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.8/logarithmplotter-0.1.8.tar.gz + + +
+ + +

Changes for v0.1.7:

+

New

+
    +
  • The history browser has been completly redesigned, improving UX.
  • +
  • The history browser now features a filter bar.
  • +
  • All side panel tabs now have a visually identifiable scrollbar.
  • +
+

Changes

+
    +
  • Shorter rich text representations of history entries to improve readability.
  • +
  • Usage of gradiants and icons to better identify history entries at a glance.
  • +
  • History entries are now showing the whole label on several lines, instead of cutting it at the end.
  • +
  • New history action for renaming.
  • +
  • New history action for coloring. Note: color changing history entries created in previous versions of LogarithmPlotter will not be updated.
  • +
  • Tooltips for object creation buttons have been added.
  • +
  • Tooltips have been set to have a delay of 200ms to match most software's handling of them.
  • +
  • Object creation buttons now have a unified style accross all platforms.
  • +
+

Added translations

+
    +
  • History action of renaming objects.
  • +
  • History action of changing the color of objects.
  • +
  • Filtering for history browser.
  • +
+

Fixed bugs

+
    +
  • Visibility history actions (shown and hidden) are now properly savedmaking loading them not automaticly changed to "Show".
  • +
  • Name changes history actions are now properly saved.
  • +
  • Non translated object type on the "+ Create new object" item selection combobox for Bode Magnitude and Phase.
  • +
  • Proper handling for future LogarithmPlotter files.
  • +
  • Shortcuts not being displayed in the menu bar are now properly shown.
  • +
  • (flatpak) Black versions of the icons when using a black theme with the KDE SDK.
  • +
  • (debian) Fixed launchpad building properly.
  • +
+

Internal changes

+
    +
  • Better organisation on icons.
  • +
  • Historylib has been separated in several files.
  • +
  • Trying to switch metainfo once more to try and fix another bug.
  • +
  • Keywords added to metainfo.
  • +
+
+ + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.7/logarithmplotter-v0.1.7-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.7/logarithmplotter-v0.1.7-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.7/logarithmplotter-0.1.7.tar.gz + + +
+ + +

Changes for v0.1.6:

+

New

+
    +
  • A new changelog popup is available at startup and in the help menu.
  • +
+

Added translations

+
    +
  • Object properties names.
  • +
  • Object properties enum values.
  • +
  • Object comments.
  • +
  • Most elements using a ":".
  • +
+

Fixed bugs

+
    +
  • X Cursor's targets can now be set to null.
  • +
  • History now imports domains and objects properly.
  • +
  • Proper handling for future LogarithmPlotter files.
  • +
  • (debian) Fixing bug that created a /build directory and didn't put the icons in the right directories.
  • +
+

Other

+
    +
  • Other: Refractoring done on helper.
  • +
  • Other: All QML elements are now properly commented.
  • +
  • Other: Scripts have been moved to it's own directory.
  • +
  • Other: Added changelog to metainfo for flathub.
  • +
+
+ + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.6/logarithmplotter-v0.1.6-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.6/logarithmplotter-v0.1.6-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.6/LogarithmPlotter-v0.1.6.tar.gz + + +
+ + +

Changes for v0.1.5:

+

New

+
    +
  • LogarithmPlotter has now better handling of very high values in logarithmic scale.
  • +
+

Added translations

+
    +
  • Flatpak metadata, including translated image.
  • +
+

Fixed bugs

+
    +
  • (!) File saving dialog was not working.
  • +
  • (!) Debian packages does include any language file.
  • +
  • X Cursor pointing does not detect any object.
  • +
+
+ + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.5/logarithmplotter-v0.1.5-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.5/logarithmplotter-v0.1.5-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.5/LogarithmPlotter-v0.1.5.tar.gz + + +
+ + +

Changes for v0.1.4:

+

New

+
    +
  • LogarithmPlotter detects unsaved changes.
  • +
  • LogarithmPlotter is now translated!
  • +
  • New translation: English by Ad5001: 100%
  • +
  • New translation: French by Ad5001: 100%
  • +
  • New translation: German by Ad5001: 100%
  • +
  • New translation: Hungarian by Óvári (@ovari on github): 100%
  • +
  • New translation: Norvegian by Allan Nordhøy (@comradekingu on github): 80%
  • +
+

Fixed bugs

+
    +
  • Fixed bug: No notification when closing LogarithmPlotter with unsaved changes.
  • +
  • Fixed bug: π unavailable in symbols.
  • +
+
+ + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.4/logarithmplotter-v0.1.4-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.4/logarithmplotter-v0.1.4-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.4/LogarithmPlotter-v0.1.4.tar.gz + + +
+ + +

Changes for v0.1.3:

+

Fixed bugs

+
    +
  • Sandboxed packages (snapcraft and flatpak) won't show error messages related to update checks.
  • +
  • Equations of the form (x + y) / z were not being simplified properly.
  • +
+
+ + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.3/logarithmplotter-v0.1.3-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.3/logarithmplotter-v0.1.3-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.3/LogarithmPlotter-v0.1.3.tar.gz + + +
+ + +

Changes for v0.1.2:

+

Fixed bugs

+
    +
  • Unable to move Bode diagrams elements when having deleted the sum element.
  • +
  • Names were not not being changed from previous object when editing a new one.
  • +
  • Bode Magnitude was not drawn far enough.
  • +
  • Bode Magnitude had undefined ending.
  • +
  • Other bugs patched in v0.1.1.
  • +
+
+ + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.2/logarithmplotter-v0.1.2-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.2/logarithmplotter-v0.1.2-setup.exe + + + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.2/LogarithmPlotter-v0.1.2.tar.gz + + +
+ +

Changes for v0.1:

+
    +
  • Initial release.
  • +
+
+
+ + + Ad5001 + mail@ad5001.eu + + + Plot + Plotter + Log + Logarithm + Logarithmic + Bode + Magnitude + Diagram + Graph + Phase + Sequence + Distribution + +
+ + diff --git a/linux/flatpak b/linux/flatpak new file mode 160000 index 0000000..a23d256 --- /dev/null +++ b/linux/flatpak @@ -0,0 +1 @@ +Subproject commit a23d256b5838751f6aadaa9281aceee9ba058047 diff --git a/scripts/generate-appstream-changelog.sh b/linux/generate-appstream-changelog.sh similarity index 68% rename from scripts/generate-appstream-changelog.sh rename to linux/generate-appstream-changelog.sh index e37c9fa..9728103 100644 --- a/scripts/generate-appstream-changelog.sh +++ b/linux/generate-appstream-changelog.sh @@ -11,13 +11,13 @@ BEGIN { print " " print " " print " " - print " https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-v"version"-setup.exe" + print " https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-v"version"-setup.exe" print " " print " " - print " https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/LogarithmPlotter-v"version"-setup.dmg" + print " https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-v"version"-setup.exe" print " " print " " - print " https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-"version".tar.gz" + print " https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-"version".tar.gz" print " " print " " print " " @@ -28,7 +28,7 @@ BEGIN { version = substr($2,2,5) print " " print " " - print "

Changes for "$2":

" + print "

Changes for "$2":

" } /^\s*\*\*/ { if(listBegan) { @@ -48,8 +48,6 @@ BEGIN { text = substr(s,2) # Removing links text = gensub(/\[([^\]]+)\]\(([^\)]+)\)/, "\\1", "g", text); - # Fixing & in text. - text = gensub(/&/, "&", "g", text) print "
  • "text"
  • " } /^\s*--/ { @@ -58,13 +56,13 @@ BEGIN { print "
    " print " " print " " - print " https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-v"version"-setup.exe" + print " https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-v"version"-setup.exe" print " " print " " - print " https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/LogarithmPlotter-v"version"-setup.dmg" + print " https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-v"version"-setup.exe" print " " print " " - print " https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-"version".tar.gz" + print " https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-"version".tar.gz" print " " print " " print "
    " @@ -74,13 +72,13 @@ END { print " " print " " print " " - print " https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-v"version"-setup.exe" + print " https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-v"version"-setup.exe" print " " print " " - print " https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/LogarithmPlotter-v"version"-setup.dmg" + print " https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-v"version"-setup.exe" print " " print " " - print " https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-"version".tar.gz" + print " https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v"version"/logarithmplotter-"version".tar.gz" print " " print " " print " " diff --git a/linux/install_local.sh b/linux/install_local.sh new file mode 100644 index 0000000..714cc0b --- /dev/null +++ b/linux/install_local.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# +# AccountFree - Browse and use online services, free of account. +# Copyright (C) 2022 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 . +# +# This script installs the desktop file & mime type for development environment, linking it directly to run.py. + +APPROOT="$(cd -P "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +echo "Adding desktop file..." +sed "s+ROOTFOLDER+$APPROOT/+g" "$APPROOT/linux/logplotter.desktop" > "$APPROOT/linux/logarithmplotter-local.desktop" +xdg-desktop-menu install "$APPROOT/linux/logarithmplotter-local.desktop" +echo "Installing mime-type..." +xdg-mime install "$APPROOT/linux/x-logarithm-plot.xml" +echo "Installing icons..." +mkdir -p ~/.local/share/icons/hicolor/scalable/mimetypes +cp "$APPROOT/linux/application-x-logarithm-plot.svg" ~/.local/share/icons/hicolor/scalable/mimetypes/application-x-logarithm-plot.svg +mkdir -p ~/.local/share/icons/hicolor/scalable/apps +cp "$APPROOT/logplotter.svg" ~/.local/share/icons/hicolor/scalable/apps/logarithmplotter.svg +# xdg-icon-resource does not work with SVG yet. See https://bugs.launchpad.net/ubuntu/+source/xdg-utils/+bug/790449. +#xdg-icon-resource install --context mimetypes --novendor "$APPROOT/linux/application-x-logarithm-plot.svg" "application-x-logarithm-plot" +#xdg-icon-resource install --context apps --novendor "$APPROOT/logplotter.svg" "logarithmplotter" +update-mime-database ~/.local/share/mime/ +update-icon-caches ~/.local/share/icons/hicolor diff --git a/assets/native/linux/logarithmplotter.desktop b/linux/logarithmplotter.desktop similarity index 81% rename from assets/native/linux/logarithmplotter.desktop rename to linux/logarithmplotter.desktop index 70637c5..56a0b14 100644 --- a/assets/native/linux/logarithmplotter.desktop +++ b/linux/logarithmplotter.desktop @@ -7,14 +7,14 @@ GenericName[de]=2D-Grafiksoftware mit logarithmischer Skalierung GenericName[fr]=Logiciel de traçage à l'échelle logarithmique GenericName[hu]=Síkbeli ábrázolásszoftver GenericName[no]=2D-plotterprogramvare -Comment=Create Bode diagrams, sequences and distribution functions +Comment=Create BODE diagrams, sequences and distribution functions Comment[de]=Erstellung von Bode-Diagramms, Folgen und Verteilungsfunktionen -Comment[fr]=Créer des diagrammes de Bode, des suites et des fonctions de répartition +Comment[fr]=Créer des diagrammes de BODE, des suites et des fonctions de répartition Comment[hu]=Bode-ábrák, sorozatok és újraosztási függvények létrehozása TryExec=logarithmplotter Exec=logarithmplotter %f -Icon=logarithmplotter +Icon=logplotter MimeType=application/x-logarithm-plot; Terminal=false StartupNotify=false diff --git a/linux/logplotter.desktop b/linux/logplotter.desktop new file mode 100644 index 0000000..1c46f8f --- /dev/null +++ b/linux/logplotter.desktop @@ -0,0 +1,20 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=LogarithmPlotter +GenericName=2D plotter software +GenericName[fr]=Logiciel de traçage 2D +GenericName[de]=2D-Grafiksoftware +GenericName[no]=2D-plotterprogramvare +GenericName[hu]=Síkbeli ábrázolásszoftver +Comment=Create BODE diagrams, sequences and distribution functions +Comment[fr]=Créer des diagrammes de BODE, des suites et des fonctions de répartition +Comment[de]=Erstellung von Bode-Diagramms, Folgen und Verteilungsfunktionen +Comment[hu]=Bode-ábrák, sorozatok és újraosztási függvények létrehozása + +Exec=/usr/bin/python3 ROOTFOLDER/run.py %f +Icon=logarithmplotter +MimeType=application/x-logarithm-plot; +Terminal=false +StartupNotify=false +Categories=Science diff --git a/assets/native/linux/snapcraft/launcher/launch-logarithmplotter b/linux/snapcraft/launcher/launch-logarithmplotter similarity index 100% rename from assets/native/linux/snapcraft/launcher/launch-logarithmplotter rename to linux/snapcraft/launcher/launch-logarithmplotter diff --git a/assets/native/linux/x-logarithm-plot.xml b/linux/x-logarithm-plot.xml similarity index 90% rename from assets/native/linux/x-logarithm-plot.xml rename to linux/x-logarithm-plot.xml index 888641e..31b5c9d 100644 --- a/assets/native/linux/x-logarithm-plot.xml +++ b/linux/x-logarithm-plot.xml @@ -1,7 +1,7 @@ - Logarithmic Plot File + Logarithm Plot File Fichier Graphe Logarithmique diff --git a/assets/icons/objects/Text.svg b/logplotter.svg similarity index 57% rename from assets/icons/objects/Text.svg rename to logplotter.svg index 0fd08e2..89b2b1b 100644 --- a/assets/icons/objects/Text.svg +++ b/logplotter.svg @@ -1,21 +1,23 @@ + sodipodi:docname="logplotter.svg" + inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)"> + LogarithmPlotter Icon v1.0 + id="defs833" /> + inkscape:window-maximized="1"> - + id="grid1403" /> + id="metadata836"> image/svg+xml - 2021-2023 + LogarithmPlotter Icon v1.0 + + 2021 Ad5001 @@ -73,11 +60,9 @@ - (C) Ad5001 2021-2023 - Licensed under CC4.0-BY-NC-SA + (c) Ad5001 2021 - All rights reserved - @@ -98,30 +83,49 @@ + + + - + + - t + style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.95063;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path1529" + sodipodi:type="arc" + sodipodi:cx="1" + sodipodi:cy="1" + sodipodi:rx="20.024685" + sodipodi:ry="18.024683" + sodipodi:start="0" + sodipodi:end="1.5707963" + sodipodi:arc-type="arc" + d="M 21.024685,1 A 20.024685,18.024683 0 0 1 1,19.024683" + sodipodi:open="true" /> diff --git a/logplotterfile.svg b/logplotterfile.svg new file mode 100644 index 0000000..580277f --- /dev/null +++ b/logplotterfile.svg @@ -0,0 +1,177 @@ + + + LogarithmPlotter Icon + + + + + + + + + + + + + + + + + + + image/svg+xml + + LogarithmPlotter File Icon + 2021 + + + Ad5001 + + + + + (c) Copyright Ad5001 2021 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/native/mac/Info.plist b/mac/Info.plist similarity index 98% rename from assets/native/mac/Info.plist rename to mac/Info.plist index 1ba304b..ff60696 100644 --- a/assets/native/mac/Info.plist +++ b/mac/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.6.0 + 0.1 NSHighResolutionCapable UTExportedTypeDeclarations diff --git a/assets/native/mac/install-bg.png b/mac/install-bg.png similarity index 100% rename from assets/native/mac/install-bg.png rename to mac/install-bg.png diff --git a/assets/native/mac/install-bg.xcf b/mac/install-bg.xcf similarity index 100% rename from assets/native/mac/install-bg.xcf rename to mac/install-bg.xcf diff --git a/mac/logarithmplotter.icns b/mac/logarithmplotter.icns new file mode 100644 index 0000000..46c62fa Binary files /dev/null and b/mac/logarithmplotter.icns differ diff --git a/mac/logarithmplotter.iconset/icon_128x128.png b/mac/logarithmplotter.iconset/icon_128x128.png new file mode 100644 index 0000000..0f68d7b Binary files /dev/null and b/mac/logarithmplotter.iconset/icon_128x128.png differ diff --git a/mac/logarithmplotter.iconset/icon_16x16.png b/mac/logarithmplotter.iconset/icon_16x16.png new file mode 100644 index 0000000..2e3cbde Binary files /dev/null and b/mac/logarithmplotter.iconset/icon_16x16.png differ diff --git a/mac/logarithmplotter.iconset/icon_256x256.png b/mac/logarithmplotter.iconset/icon_256x256.png new file mode 100644 index 0000000..124cd88 Binary files /dev/null and b/mac/logarithmplotter.iconset/icon_256x256.png differ diff --git a/mac/logarithmplotter.iconset/icon_32x32.png b/mac/logarithmplotter.iconset/icon_32x32.png new file mode 100644 index 0000000..75daca5 Binary files /dev/null and b/mac/logarithmplotter.iconset/icon_32x32.png differ diff --git a/mac/logarithmplotter.iconset/icon_512x512.png b/mac/logarithmplotter.iconset/icon_512x512.png new file mode 100644 index 0000000..08c025f Binary files /dev/null and b/mac/logarithmplotter.iconset/icon_512x512.png differ diff --git a/assets/native/mac/logarithmplotterfile.icns b/mac/logarithmplotterfile.icns similarity index 100% rename from assets/native/mac/logarithmplotterfile.icns rename to mac/logarithmplotterfile.icns diff --git a/assets/native/mac/logarithmplotterfile.iconset/icon_128x128.png b/mac/logarithmplotterfile.iconset/icon_128x128.png similarity index 100% rename from assets/native/mac/logarithmplotterfile.iconset/icon_128x128.png rename to mac/logarithmplotterfile.iconset/icon_128x128.png diff --git a/assets/native/mac/logarithmplotterfile.iconset/icon_128x128@2x.png b/mac/logarithmplotterfile.iconset/icon_128x128@2x.png similarity index 100% rename from assets/native/mac/logarithmplotterfile.iconset/icon_128x128@2x.png rename to mac/logarithmplotterfile.iconset/icon_128x128@2x.png diff --git a/assets/native/mac/logarithmplotterfile.iconset/icon_16x16.png b/mac/logarithmplotterfile.iconset/icon_16x16.png similarity index 100% rename from assets/native/mac/logarithmplotterfile.iconset/icon_16x16.png rename to mac/logarithmplotterfile.iconset/icon_16x16.png diff --git a/assets/native/mac/logarithmplotterfile.iconset/icon_16x16@2x.png b/mac/logarithmplotterfile.iconset/icon_16x16@2x.png similarity index 100% rename from assets/native/mac/logarithmplotterfile.iconset/icon_16x16@2x.png rename to mac/logarithmplotterfile.iconset/icon_16x16@2x.png diff --git a/assets/native/mac/logarithmplotterfile.iconset/icon_256x256.png b/mac/logarithmplotterfile.iconset/icon_256x256.png similarity index 100% rename from assets/native/mac/logarithmplotterfile.iconset/icon_256x256.png rename to mac/logarithmplotterfile.iconset/icon_256x256.png diff --git a/assets/native/mac/logarithmplotterfile.iconset/icon_256x256@2x.png b/mac/logarithmplotterfile.iconset/icon_256x256@2x.png similarity index 100% rename from assets/native/mac/logarithmplotterfile.iconset/icon_256x256@2x.png rename to mac/logarithmplotterfile.iconset/icon_256x256@2x.png diff --git a/assets/native/mac/logarithmplotterfile.iconset/icon_32x32.png b/mac/logarithmplotterfile.iconset/icon_32x32.png similarity index 100% rename from assets/native/mac/logarithmplotterfile.iconset/icon_32x32.png rename to mac/logarithmplotterfile.iconset/icon_32x32.png diff --git a/assets/native/mac/logarithmplotterfile.iconset/icon_32x32@2x.png b/mac/logarithmplotterfile.iconset/icon_32x32@2x.png similarity index 100% rename from assets/native/mac/logarithmplotterfile.iconset/icon_32x32@2x.png rename to mac/logarithmplotterfile.iconset/icon_32x32@2x.png diff --git a/assets/native/mac/logarithmplotterfile.iconset/icon_512x512.png b/mac/logarithmplotterfile.iconset/icon_512x512.png similarity index 100% rename from assets/native/mac/logarithmplotterfile.iconset/icon_512x512.png rename to mac/logarithmplotterfile.iconset/icon_512x512.png diff --git a/assets/native/mac/logarithmplotterfile.iconset/icon_512x512@2x.png b/mac/logarithmplotterfile.iconset/icon_512x512@2x.png similarity index 100% rename from assets/native/mac/logarithmplotterfile.iconset/icon_512x512@2x.png rename to mac/logarithmplotterfile.iconset/icon_512x512@2x.png diff --git a/run.py b/run.py index 058ca94..faddd86 100644 --- a/run.py +++ b/run.py @@ -1,6 +1,6 @@ """ * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2022 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 @@ -15,24 +15,21 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . """ -from os import system, getcwd, path -from sys import path as sys_path, argv - -def build(): +def update_translations(): """ Updates all binary translations """ - system("./scripts/build.sh") + from os import system, getcwd, chdir, path + pwd = getcwd() + chdir(path.join("LogarithmPlotter", "i18n")) + system("./release.sh") + chdir(pwd) def run(): + update_translations() from LogarithmPlotter import logarithmplotter logarithmplotter.run() if __name__ == "__main__": - if '--test-build' not in argv: - build() - logplotter_path = path.realpath(path.join(getcwd(), "build", "runtime-pyside6")) - print(f"Appending {logplotter_path} to path...") - sys_path.append(logplotter_path) run() diff --git a/runtime-pyside6/LogarithmPlotter/logarithmplotter.py b/runtime-pyside6/LogarithmPlotter/logarithmplotter.py deleted file mode 100644 index 562231a..0000000 --- a/runtime-pyside6/LogarithmPlotter/logarithmplotter.py +++ /dev/null @@ -1,215 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -from os import getcwd, chdir, environ, path -from platform import system as os_name, release as OS_RELEASE -from sys import path as sys_path -from sys import argv, exit -from tempfile import TemporaryDirectory -from math import ceil -from time import time - -from PySide6.QtCore import QTranslator, QLocale, QThreadPool, QThread -from PySide6.QtGui import QIcon -from PySide6.QtQml import QQmlApplicationEngine -from PySide6.QtQuickControls2 import QQuickStyle -from PySide6.QtWidgets import QApplication - -start_time = time() - -# Create the temporary directory for saving copied screenshots and latex files -tempdir = TemporaryDirectory() -tmpfile = path.join(tempdir.name, 'graph.png') -pwd = getcwd() - -logarithmplotter_path = path.dirname(path.realpath(__file__)) -chdir(logarithmplotter_path) - -if path.realpath(path.join(getcwd(), "..")) not in sys_path: - sys_path.append(path.realpath(path.join(getcwd(), ".."))) - -from LogarithmPlotter import __VERSION__ -from LogarithmPlotter.util import config, native, debug -from LogarithmPlotter.util.update import check_for_updates -from LogarithmPlotter.util.helper import Helper -from LogarithmPlotter.util.latex import Latex -from LogarithmPlotter.util.js import PyJSValue - -OS_NAME = os_name() - - -CACHE_PATH = { - "Linux": path.join(environ["XDG_CONFIG_HOME"], "LogarithmPlotter") - if "XDG_CONFIG_HOME" in environ else - path.join(path.expanduser("~"), ".cache", "LogarithmPlotter"), - "Windows": path.join(path.expandvars('%APPDATA%'), "LogarithmPlotter", "cache"), - "Darwin": path.join(path.expanduser("~"), "Library", "Caches", "LogarithmPlotter"), -}[OS_NAME] - - -LINUX_THEMES = { # See https://specifications.freedesktop.org/menu-spec/latest/onlyshowin-registry.html - "COSMIC": "Basic", - "GNOME": "Basic", - "GNOME-Classic": "Basic", - "GNOME-Flashback": "Basic", - "KDE": "Fusion", - "LXDE": "Basic", - "LXQt": "Fusion", - "MATE": "Fusion", - "TDE": "Fusion", - "Unity": "Basic", - "XFCE": "Basic", - "Cinnamon": "Fusion", - "Pantheon": "Basic", - "DDE": "Basic", - "EDE": "Fusion", - "Endless": "Basic", - "Old": "Fusion", -} - - -def get_linux_theme() -> str: - if "XDG_SESSION_DESKTOP" in environ: - if environ["XDG_SESSION_DESKTOP"] in LINUX_THEMES: - return LINUX_THEMES[environ["XDG_SESSION_DESKTOP"]] - return "Fusion" - else: - # Android - return "Material" - - -def get_platform_qt_style(os) -> str: - return { - "Linux": get_linux_theme(), - "Windows": "Universal" if OS_RELEASE() in ["10", "11", "12", "13", "14"] else "Windows", - "Darwin": "macOS", - "Android": "Material" - }[os] - - -def register_icon_directories() -> None: - icon_fallbacks = QIcon.fallbackSearchPaths() - base_icon_path = path.join(logarithmplotter_path, "qml", "eu", "ad5001", "LogarithmPlotter", "icons") - paths = [["common"], ["objects"], ["history"], ["settings"], ["properties"]] - for p in paths: - icon_fallbacks.append(path.realpath(path.join(base_icon_path, *p))) - QIcon.setFallbackSearchPaths(icon_fallbacks) - - -def create_qapp() -> QApplication: - app = QApplication(argv) - app.setApplicationName("LogarithmPlotter") - app.setApplicationDisplayName("LogarithmPlotter") - app.setApplicationVersion(f"v{__VERSION__}") - app.setDesktopFileName("eu.ad5001.LogarithmPlotter") - app.setOrganizationName("Ad5001") - app.styleHints().setShowShortcutsInContextMenus(True) - app.setWindowIcon(QIcon(path.realpath(path.join(logarithmplotter_path, "logarithmplotter.svg")))) - return app - - -def install_translation(app: QApplication) -> QTranslator: - # Installing translators - translator = QTranslator() - # Check if lang is forced. - forcedlang = [p for p in argv if p[:7] == "--lang="] - i18n_path = path.realpath(path.join(logarithmplotter_path, "i18n")) - locale = QLocale(forcedlang[0][7:]) if len(forcedlang) > 0 else QLocale() - if not translator.load(locale, "lp", "_", i18n_path): - # Load default translation - print("Loading default language en...") - translator.load(QLocale("en"), "lp", "_", i18n_path) - app.installTranslator(translator) - return translator - - -def create_engine(helper: Helper, latex: Latex, dep_time: float) -> tuple[QQmlApplicationEngine, PyJSValue]: - global tmpfile - engine = QQmlApplicationEngine() - js_globals = PyJSValue(engine.globalObject()) - js_globals.globalThis = engine.globalObject() - js_globals.Helper = engine.newQObject(helper) - js_globals.Latex = engine.newQObject(latex) - engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv) - engine.rootContext().setContextProperty("StartTime", dep_time) - - qml_path = path.realpath(path.join(logarithmplotter_path, "qml")) - engine.addImportPath(qml_path) - engine.load(path.join(qml_path, "eu", "ad5001", "LogarithmPlotter", "LogarithmPlotter.qml")) - - return engine, js_globals - - -def run(): - config.init() - - if not 'QT_QUICK_CONTROLS_STYLE' in environ: - QQuickStyle.setStyle(get_platform_qt_style(OS_NAME)) - - dep_time = time() - print("Loaded dependencies in " + str((dep_time - start_time) * 1000) + "ms.") - - # Maxing thread count to half the computer's thread count to avoid maxing CPU - # with too many threads (and also leaving some for rendering). - QThreadPool.globalInstance().setMaxThreadCount(int(ceil(QThread.idealThreadCount() / 2))) - - register_icon_directories() - app = create_qapp() - translator = install_translation(app) - debug.setup() - - # Installing macOS file handler. - macos_file_open_handler = None - if OS_NAME == "Darwin": - macos_file_open_handler = native.MacOSFileOpenHandler() - app.installEventFilter(macos_file_open_handler) - - helper = Helper(pwd, tmpfile) - latex = Latex(CACHE_PATH) - engine, js_globals = create_engine(helper, latex, dep_time) - - if len(engine.rootObjects()) == 0: # No root objects loaded - print("No root object", path.realpath(path.join(getcwd(), "qml"))) - exit(-1) - - # Open the current diagram - chdir(pwd) - if len(argv) > 0 and path.exists(argv[-1]) and argv[-1].split('.')[-1] in ['lpf']: - js_globals.Modules.IO.loadDiagram(argv[-1]) - chdir(path.dirname(path.realpath(__file__))) - - if OS_NAME == "Darwin": - macos_file_open_handler.init_io(js_globals.Modules.IO) - - # Check for LaTeX installation if LaTeX support is enabled - if config.getSetting("enable_latex"): - latex.checkLatexInstallation() - - # Check for updates - if config.getSetting("check_for_updates"): - check_for_updates(__VERSION__, engine.rootObjects()[0]) - - exit_code = app.exec() - - tempdir.cleanup() - config.save() - exit(exit_code) - - -if __name__ == "__main__": - run() diff --git a/runtime-pyside6/LogarithmPlotter/logarithmplotter.svg b/runtime-pyside6/LogarithmPlotter/logarithmplotter.svg deleted file mode 100644 index 77a2817..0000000 --- a/runtime-pyside6/LogarithmPlotter/logarithmplotter.svg +++ /dev/null @@ -1,64 +0,0 @@ - -LogarithmPlotter Icon v1.0image/svg+xmlLogarithmPlotter Icon v1.02021Ad5001(c) Ad5001 2021 - All rights reserved diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Common/qmldir b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Common/qmldir deleted file mode 100644 index af9eb16..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Common/qmldir +++ /dev/null @@ -1,3 +0,0 @@ -module eu.ad5001.LogarithmPlotter.Common - -JS 1.0 index.mjs diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/qmldir b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/qmldir deleted file mode 100644 index 66c4408..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/qmldir +++ /dev/null @@ -1,4 +0,0 @@ -module eu.ad5001.LogarithmPlotter.History - -Browser 1.0 Browser.qml -SingleItem 1.0 SingleItem.qml diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml deleted file mode 100644 index 30bf6ca..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml +++ /dev/null @@ -1,86 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -import Qt.labs.platform as Native - -/*! - \qmltype LogGraphCanvas - \inqmlmodule eu.ad5001.LogarithmPlotter - \brief Canvas used to display the diagram. - - Provides a customized canvas with several helper methods to be used by objects. - - \sa LogarithmPlotter, PickLocationOverlay -*/ -Canvas { - id: canvas - anchors.top: parent.top - anchors.left: parent.left - height: parent.height - 90 - width: parent.width - /*! - \qmlproperty var LogGraphCanvas::imageLoaders - Dictionary of format {image: callback} containing data for deferred image loading. - */ - property var imageLoaders: {} - - Component.onCompleted: { - imageLoaders = {} - Modules.Canvas.initialize({ canvas, drawingErrorDialog }) - } - - Native.MessageDialog { - id: drawingErrorDialog - title: qsTranslate("expression", "LogarithmPlotter - Drawing error") - text: "" - function show(objType, objName, error) { - text = qsTranslate("error", "Error while attempting to draw %1 %2:\n%3\n\nUndoing last change.").arg(objType).arg(objName).arg(error) - open() - } - } - - onPaint: function(rect) { - //console.log('Redrawing') - if(rect.width == canvas.width) { // Redraw full canvas - Modules.Canvas.redraw() - } - } - - onImageLoaded: { - Object.keys(imageLoaders).forEach((key) => { - if(isImageLoaded(key)) { - // Calling callback - imageLoaders[key]() - delete imageLoaders[key] - } - }) - } - - /*! - \qmlmethod void LogGraphCanvas::loadImageAsync(string imageSource) - Loads an image data onto the canvas asynchronously. - Returns a Promise that is resolved when the image is loaded. - */ - function loadImageAsync(imageSource) { - return new Promise((resolve) => { - this.loadImage(imageSource) - this.imageLoaders[imageSource] = resolve - }) - } -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/CustomPropertyList.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/CustomPropertyList.qml deleted file mode 100644 index 113b8bd..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/CustomPropertyList.qml +++ /dev/null @@ -1,347 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -import QtQuick.Controls -import Qt.labs.platform as Native -import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting -import eu.ad5001.LogarithmPlotter.Common - -/*! - \qmltype CustomPropertyList - \inqmlmodule eu.ad5001.LogarithmPlotter.ObjectLists.Editor - \brief Lists all custom properties editors inside a repeater and allow for edition. - - This class repeats all of the custom properties and loads the appropriate editor for each kind of property. - - \sa Dialog -*/ -Repeater { - id: root - - signal changed() - - /*! - \qmlproperty var CustomPropertyList::obj - Object whose properties to list and edit. - */ - property var obj - - /*! - \qmlproperty var CustomPropertyList::objPropertyTypes - Record containing the properties types of the object mapped by their name. - */ - property var objPropertyTypes - /*! - \qmlproperty var CustomPropertyList::positionPicker - Reference to the global PositionPicker QML object. - */ - property var positionPicker - - readonly property var textTypes: ['Domain', 'string', 'number', 'int'] - readonly property var comboBoxTypes: ['ObjectType', 'Enum'] - readonly property var listTypes: ['List', 'Dict'] - - - // NOTE: All components have the declared properties 'propertyLabel', 'propertyIcon', propertyName' and 'propertyType' to access the object in question. - Component { - id: commentComponent - - // Item for comments. - // NOTE: propertyType here is the content of the comment (yes, it's a bit backwards, but it's more clear on the properties side). - Label { - // Translated text with object name. - property string trText: qsTranslate('comment', propertyType).toString() - text: (trText.includes("%1") ? trText.arg(obj.name) : trText).toString() - //color: sysPalette.windowText - wrapMode: Text.WordWrap - } - } - - Component { - id: expressionEditorComponent - - // Setting for expressions - Setting.ExpressionEditor { - height: 30 - label: propertyLabel - icon: `properties/${propertyIcon}.svg` - defValue: JS.Utils.simplifyExpression(obj[propertyName].toEditableString()) - self: obj.name - variables: propertyType.variables - onChanged: function(newExpr) { - if(obj[propertyName].toString() != newExpr.toString()) { - Modules.History.addToHistory(new JS.HistoryLib.EditedProperty( - obj.name, objType, propertyName, - obj[propertyName], newExpr - )) - obj[propertyName] = newExpr - root.changed() - } - } - } - } - - - Component { - id: textEditorComponent - - // Setting for text & number settings as well as domains - Setting.TextSetting { - height: 30 - label: propertyLabel - icon: `properties/${propertyIcon}.svg` - min: propertyType == "int" ? 0 : -Infinity - isInt: propertyType == "int" - isDouble: propertyType == "number" - defValue: obj[propertyName] == null ? '' : obj[propertyName].toString() - category: { - return { - "Domain": "domain", - "string": "all", - "number": "all", - "int": "all", - }[propertyType] - } - onChanged: function(newValue) { - try { - var newValueParsed = { - "Domain": () => JS.MathLib.parseDomain(newValue), - "string": () => newValue, - "number": () => newValue, - "int": () => newValue - }[propertyType]() - - // Ensuring old and new values are different to prevent useless adding to history. - if(obj[propertyName] != newValueParsed) { - Modules.History.addToHistory(new JS.HistoryLib.EditedProperty( - obj.name, objType, propertyName, - obj[propertyName], newValueParsed - )) - obj[propertyName] = newValueParsed - root.changed() - } - } catch(e) { - // Error in expression or domain - console.trace() - parsingErrorDialog.showDialog(propertyName, newValue, e.message) - } - } - - - Native.MessageDialog { - id: parsingErrorDialog - title: qsTranslate("expression", "LogarithmPlotter - Parsing error") - text: "" - function showDialog(propName, propValue, error) { - text = qsTranslate("error", "Error while parsing expression for property %1:\n%2\n\nEvaluated expression: %3") - .arg(qsTranslate('prop', propName)) - .arg(error).arg(propValue) - open() - } - } - } - } - - Component { - id: checkboxComponent - - // Setting for boolean - CheckBox { - height: 20 - text: propertyLabel - //icon: `properties/${propertyIcon}.svg` - - checked: { - //if(obj[propertyName] == null) { - // return false - //} - return obj[propertyName] - } - onClicked: { - Modules.History.addToHistory(new JS.HistoryLib.EditedProperty( - obj.name, objType, propertyName, - obj[propertyName], this.checked - )) - obj[propertyName] = this.checked - root.changed() - } - } - } - - Component { - id: comboBoxComponent - - // Setting when selecting data from an enum, or an object of a certain type. - Setting.ComboBoxSetting { - height: 30 - label: propertyLabel - icon: `properties/${propertyIcon}.svg` - // True to select an object of type, false for enums. - property bool selectObjMode: paramTypeIn(propertyType, ['ObjectType']) - property bool isRealObject: !selectObjMode || (propertyType.objType != "ExecutableObject" && propertyType.objType != "DrawableObject") - - // Base, untranslated version of the model. - property var baseModel: selectObjMode ? - Modules.Objects.getObjectsName(propertyType.objType).concat( - isRealObject ? [qsTr("+ Create new %1").arg(Modules.Objects.types[propertyType.objType].displayType())] : []) - : propertyType.values - // Translated version of the model. - model: selectObjMode ? baseModel : propertyType.translatedValues - currentIndex: baseModel.indexOf(selectObjMode ? obj[propertyName].name : obj[propertyName]) - - onActivated: function(newIndex) { - if(selectObjMode) { - // This is only done when what we're selecting are Objects. - // Setting object property. - var selectedObj = Modules.Objects.currentObjectsByName[baseModel[newIndex]] - if(newIndex != 0) { - // Make sure we don't set the object to null. - if(selectedObj == null) { - // Creating new object. - selectedObj = Modules.Objects.createNewRegisteredObject(propertyType.objType) - Modules.History.addToHistory( - new JS.HistoryLib.CreateNewObject(selectedObj.name, propertyType.objType, selectedObj.export()) - ) - baseModel = Modules.Objects.getObjectsName(propertyType.objType).concat( - isRealObject ? [qsTr("+ Create new %1").arg(Modules.Objects.types[propertyType.objType].displayType())] : - []) - currentIndex = baseModel.indexOf(selectedObj.name) - } - selectedObj.requiredBy.push(Modules.Objects.currentObjects[objType][objIndex]) - //Modules.Objects.currentObjects[objType][objIndex].requiredBy = obj[propertyName].filter((obj) => obj.name != obj.name) - } - obj.requiredBy = obj.requiredBy.filter((obj) => obj.name != obj.name) - Modules.History.addToHistory(new JS.HistoryLib.EditedProperty( - obj.name, objType, propertyName, - obj[propertyName], selectedObj - )) - obj[propertyName] = selectedObj - } else if(baseModel[newIndex] != obj[propertyName]) { - // Ensuring new property is different to not add useless history entries. - Modules.History.addToHistory(new JS.HistoryLib.EditedProperty( - obj.name, objType, propertyName, - obj[propertyName], baseModel[newIndex] - )) - obj[propertyName] = baseModel[newIndex] - } - // Refreshing - root.changed() - } - } - } - - Component { - // Setting to edit lists or dictionaries (e.g sequences & repartition function values) - id: listDictEditorComponent - - Setting.ListSetting { - label: propertyLabel - //icon: `properties/${propertyIcon}.svg` - dictionaryMode: paramTypeIn(propertyType, ['Dict']) - keyType: dictionaryMode ? propertyType.keyType : 'string' - valueType: propertyType.valueType - preKeyLabel: (dictionaryMode ? propertyType.preKeyLabel : propertyType.label).replace(/\{name\}/g, obj.name).replace(/\{name_\}/g, obj.name.substring(obj.name.indexOf("_")+1)) - postKeyLabel: (dictionaryMode ? propertyType.postKeyLabel : '').replace(/\{name\}/g, obj.name).replace(/\{name_\}/g, obj.name.substring(obj.name.indexOf("_")+1)) - keyRegexp: dictionaryMode ? propertyType.keyFormat : /^.+$/ - valueRegexp: propertyType.format - forbidAdding: propertyType.forbidAdding - - onChanged: { - var exported = exportModel() - Modules.History.addToHistory(new JS.HistoryLib.EditedProperty( - obj.name, objType, propertyName, - obj[propertyName], exported - )) - obj[propertyName] = exported - root.changed() - } - - Component.onCompleted: { - importModel(obj[propertyName]) - } - } - } - - delegate: Component { - Row { - width: dlgProperties.width - spacing: 5 - - Loader { - id: propertyEditor - width: dlgProperties.width - pointerButton.width - property string propertyName: modelData - property var propertyType: objPropertyTypes[modelData] - property string propertyLabel: qsTranslate('prop',propertyName) - property string propertyIcon: propertyName - - sourceComponent: { - if(propertyName.startsWith('comment')) - return commentComponent - else if(propertyType === 'boolean') - return checkboxComponent - else if(paramTypeIn(propertyType, ['Expression'])) - return expressionEditorComponent - else if(paramTypeIn(propertyType, textTypes)) - return textEditorComponent - else if(paramTypeIn(propertyType, comboBoxTypes)) - return comboBoxComponent - else if(paramTypeIn(propertyType, listTypes)) - return listDictEditorComponent - else - return {} - } - } - - Button { - id: pointerButton - height: parent.height - width: visible ? height : 0 - anchors.verticalCenter: parent.verticalCenter - - property bool isXProp: ['labelX', 'x'].includes(propertyEditor.propertyName) - property bool isYProp: ['y'].includes(propertyEditor.propertyName) - visible: isXProp || isYProp - ToolTip.visible: hovered - ToolTip.text: qsTr("Pick on graph") - - Setting.Icon { - id: icon - width: 18 - height: 18 - anchors.centerIn: parent - - color: sysPalette.windowText - source: '../icons/common/position.svg' - } - - onClicked: { - positionPicker.objType = objType - positionPicker.objName = obj.name - positionPicker.pickX = isXProp - positionPicker.pickY = isYProp - positionPicker.propertyX = propertyEditor.propertyName - positionPicker.propertyY = propertyEditor.propertyName - positionPicker.visible = true - objEditor.close() - } - } - } - } -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/Dialog.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/Dialog.qml deleted file mode 100644 index a5352d4..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/Dialog.qml +++ /dev/null @@ -1,172 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -import QtQuick.Controls -import Qt.labs.platform as Native -import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting -import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup -import eu.ad5001.LogarithmPlotter.Common - -/*! - \qmltype Dialog - \inqmlmodule eu.ad5001.LogarithmPlotter.ObjectLists.Editor - \brief Dialog used to edit properties of objects. - - This class contains the dialog that allows to edit all properties of objects. - \todo In the future, this class should be optimized so that each property doesn't instanciate one instance of each setting type. - - \sa Loader, ObjectLists -*/ -Popup.BaseDialog { - id: objEditor - /*! - \qmlproperty string EditorDialog::objType - Type of object being edited by the dialog. - */ - property string objType: 'Point' - /*! - \qmlproperty int EditorDialog::objIndex - Index of the objects amongst the ones of it's type. - */ - property int objIndex: 0 - /*! - \qmlproperty var EditorDialog::obj - Instance of the object being edited. - */ - property var obj: Modules.Objects.currentObjects[objType][objIndex] - /*! - \qmlproperty var EditorDialog::posPicker - Reference to the global PositionPicker QML object. - */ - property var posPicker - - title: "LogarithmPlotter" - width: 350 - minimumHeight: Math.max(450,dlgProperties.height + margin*4 + 30) - maximumHeight: minimumHeight - - Item { - anchors { - top: parent.top; - left: parent.left; - bottom: parent.bottom; - right: parent.right; - topMargin: margin; - leftMargin: margin; - bottomMargin: margin + 30; - rightMargin: margin; - } - - Column { - id: dlgProperties - anchors.top: parent.top - width: objEditor.width - 20 - spacing: 10 - - Label { - id: dlgTitle - verticalAlignment: TextInput.AlignVCenter - text: qsTr("Edit properties of %1 %2").arg(Modules.Objects.types[objEditor.objType].displayType()).arg(objEditor.obj.name) - font.pixelSize: 20 - color: sysPalette.windowText - } - - Native.MessageDialog { - id: invalidNameDialog - title: qsTr("LogarithmPlotter - Invalid object name") - text: "" - function showDialog(objectName) { - text = qsTr("An object with the name '%1' already exists.").arg(objectName) - open() - } - } - - Setting.TextSetting { - id: nameProperty - height: 30 - label: qsTr("Name") - icon: "common/label.svg" - category: "name" - width: dlgProperties.width - value: objEditor.obj.name - onChanged: function(newValue) { - let newName = JS.Utils.parseName(newValue) - if(newName != '' && objEditor.obj.name != newName) { - if(newName in Modules.Objects.currentObjectsByName) { - invalidNameDialog.showDialog(newName) - } else { - Modules.History.addToHistory(new JS.HistoryLib.NameChanged( - objEditor.obj.name, objEditor.objType, newName - )) - Modules.Objects.renameObject(obj.name, newName) - objEditor.obj = Modules.Objects.currentObjects[objEditor.objType][objEditor.objIndex] - objectListList.update() - } - } - } - } - - Setting.ComboBoxSetting { - id: labelContentProperty - height: 30 - width: dlgProperties.width - label: qsTranslate("prop", "labelContent") - model: [qsTr("null"), qsTr("name"), qsTr("name + value")] - property var idModel: ["null", "name", "name + value"] - icon: "common/label.svg" - currentIndex: idModel.indexOf(objEditor.obj.labelContent) - onActivated: function(newIndex) { - if(idModel[newIndex] != objEditor.obj.labelContent) { - Modules.History.addToHistory(new JS.HistoryLib.EditedProperty( - obj.name, objType, "labelContent", - objEditor.obj.labelContent, idModel[newIndex] - )) - objEditor.obj.labelContent = idModel[newIndex] - objEditor.obj.update() - objectListList.update() - } - } - } - - // Dynamic properties - CustomPropertyList { - id: dlgCustomProperties - obj: objEditor.obj - positionPicker: posPicker - - onChanged: { - obj.update() - objectListList.update() - } - } - } - } - - /*! - \qmlmethod void EditorDialog::open() - Shows the editor after the object to be edited is set. - */ - function open() { - dlgCustomProperties.model = [] // Reset - const objProps = Modules.Objects.types[objEditor.objType].properties() - dlgCustomProperties.objPropertyTypes = objProps - dlgCustomProperties.model = Object.keys(objProps) - objEditor.show() - } -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/qmldir b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/qmldir deleted file mode 100644 index feb9724..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/qmldir +++ /dev/null @@ -1,5 +0,0 @@ -module eu.ad5001.LogarithmPlotter.ObjectLists.Editor - -Dialog 1.0 Dialog.qml -CustomPropertyList 1.0 CustomPropertyList.qml - diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml deleted file mode 100644 index 8047573..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml +++ /dev/null @@ -1,147 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -// import QtQuick.Dialogs 1.3 as D -import QtQuick.Controls -import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting -import eu.ad5001.LogarithmPlotter.ObjectLists.Editor 1.0 as Editor - -/*! - \qmltype ObjectLists - \inqmlmodule eu.ad5001.LogarithmPlotter.ObjectLists - \brief Tab of the drawer that allows the user to manage the objects. - - This item allows the user to synthetically see all objects, while giving the user the ability - to show, hide, delete, change the location and color, as well as opening the editor dialog - for each object. - - \sa LogarithmPlotter, ObjectCreationGrid, ObjectLists -*/ -ScrollView { - id: objectListList - - signal changed() - - property var listViews: {'':''} // Needs to be initialized or will be undefined -_- - - - ScrollBar.horizontal.visible: false - ScrollBar.vertical.visible: true - - ListView { - id: objectsListView - model: Object.keys(Modules.Objects.types) - //width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0) - implicitHeight: contentItem.childrenRect.height + footerItem.height + 10 - - delegate: ListView { - id: objTypeList - property string objType: objectsListView.model[index] - property var editingRows: [] - model: Modules.Objects.currentObjects[objType] - width: objectsListView.width - height: contentItem.childrenRect.height + (visible ? 10 : 0) - visible: model != undefined && model.length > 0 - interactive: false - - Component.onCompleted: objectListList.listViews[objType] = objTypeList // Listing in order to be refreshed - - header: Row { - width: typeHeaderText.width + typeVisibilityCheckBox.visible - height: visible ? 20 : 0 - visible: objTypeList.visible - - CheckBox { - id: typeVisibilityCheckBox - checked: Modules.Objects.currentObjects[objType] != undefined ? Modules.Objects.currentObjects[objType].every(obj => obj.visible) : true - onClicked: { - for(const obj of Modules.Objects.currentObjects[objType]) obj.visible = this.checked - for(const obj of objTypeList.editingRows) obj.objVisible = this.checked - objectListList.changed() - } - - ToolTip.visible: hovered - ToolTip.text: checked ? - qsTr("Hide all %1").arg(Modules.Objects.types[objType].displayTypeMultiple()) : - qsTr("Show all %1").arg(Modules.Objects.types[objType].displayTypeMultiple()) - } - - Label { - id: typeHeaderText - verticalAlignment: TextInput.AlignVCenter - text: qsTranslate("control", "%1: ").arg(Modules.Objects.types[objType].displayTypeMultiple()) - font.pixelSize: 20 - } - } - - delegate: ObjectRow { - id: controlRow - width: objTypeList.width - obj: Modules.Objects.currentObjects[objType][index] - posPicker: positionPicker - - onChanged: { - obj = Modules.Objects.currentObjects[objType][index] - objectListList.update() - } - - Component.onCompleted: objTypeList.editingRows.push(controlRow) - } - } - - // Create items - footer: ObjectCreationGrid { - id: createRow - width: objectsListView.width - objectEditor: objEditor - objectLists: objectListList - posPicker: positionPicker - } - } - - // Object editor - Editor.Dialog { - id: objEditor - - posPicker: positionPicker - } - - /*! - \qmlmethod void ObjectLists::update() - Updates the view of the ObjectLists. - */ - function update() { - objectListList.changed() - for(var objType in objectListList.listViews) { - objectListList.listViews[objType].model = Modules.Objects.currentObjects[objType] - } - } - - /*! - \qmlmethod void ObjectLists::paramTypeIn(var parameter, var types) - Checks if the type of the provided \c parameter is in \c types. - \note The type can be normal string types ('boolean', 'string', 'number'...) or object types (Enum, Dictionay, Object types...). If the latter, only the type of object type should be provided in \c types. E.g: if you want to check if the parameter is an enum, add "Enum" to types. - */ - function paramTypeIn(parameter, types = []) { - if(types.includes(parameter.toString())) return true - if(typeof parameter === 'object' && 'type' in parameter) - return types.includes(parameter.type) - return false - } -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml deleted file mode 100644 index 0f7003f..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml +++ /dev/null @@ -1,240 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -import QtQuick.Dialogs -import QtQuick.Controls -import QtQuick.Window -import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting -import eu.ad5001.LogarithmPlotter.Common - - -/*! - \qmltype ObjectRow - \inqmlmodule eu.ad5001.LogarithmPlotter.ObjectLists - \brief Row describing an object. - - This item allows the user to see, control, and modify a graph object. - It includes the visibility checkbox, the description label (optionally latex if enabled), - the reposition and delete buttons, and the color picker. - - \sa LogarithmPlotter, ObjectCreationGrid, ObjectLists -*/ -Item { - id: objectRow - - signal changed() - - /*! - \qmlproperty var ObjectRow::obj - Object to show. - */ - property var obj - /*! - \qmlproperty var ObjectRow::posPicker - Reference to the global PositionPicker QML object. - */ - property var posPicker - - /*! - \qmlproperty bool ObjectRow::objVisible - True if the object should be visible, false otherwise. - */ - property alias objVisible: objVisibilityCheckBox.checked - /*! - \qmlproperty bool ObjectRow::minHeight - Minimum height of the row. - */ - readonly property int minHeight: 40 - - height: objDescription.height - width: obj.typeList.width - - CheckBox { - id: objVisibilityCheckBox - checked: obj.visible - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: 5 - onClicked: { - Modules.History.addToHistory(new JS.HistoryLib.EditedVisibility( - obj.name, obj.type, this.checked - )) - obj.visible = this.checked - changed() - } - - ToolTip.visible: hovered - ToolTip.text: checked ? - qsTr("Hide %1 %2").arg(obj.constructor.displayType()).arg(obj.name) : - qsTr("Show %1 %2").arg(obj.constructor.displayType()).arg(obj.name) - } - - Label { - id: objDescription - anchors.left: objVisibilityCheckBox.right - anchors.right: deleteButton.left - height: Modules.Latex.enabled ? Math.max(parent.minHeight, latexDescription.height+4) : parent.minHeight - verticalAlignment: TextInput.AlignVCenter - text: Modules.Latex.enabled ? "" : obj.getReadableString() - font.pixelSize: 14 - - Image { - id: latexDescription - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - visible: Modules.Latex.enabled - property double depth: Screen.devicePixelRatio - source: "" - width: 0/depth - height: 0/depth - - Component.onCompleted: function() { - if(Modules.Latex.enabled) { - const args = [obj.getLatexString(), depth*(parent.font.pixelSize+2), parent.color] - const prerendered = Modules.Latex.findPrerendered(...args) - if(prerendered !== null) { - source = prerendered.source - width = prerendered.width/depth - height = prerendered.height/depth - } else - Modules.Latex.requestAsyncRender(...args).then(info => { - source = info.source - width = info.width/depth - height = info.height/depth - }) - } - } - } - - MouseArea { - anchors.fill: parent - onClicked: { - objEditor.obj = Modules.Objects.currentObjects[obj.type][index] - objEditor.objType = obj.type - objEditor.objIndex = index - //objEditor.editingRow = objectRow - objEditor.open() - } - } - } - - Button { - id: pointerButton - width: parent.height - 10 - height: width - anchors.right: deleteButton.left - anchors.rightMargin: 5 - anchors.verticalCenter: parent.verticalCenter - - Setting.Icon { - id: icon - width: 18 - height: 18 - anchors.centerIn: parent - - color: sysPalette.windowText - source: '../icons/common/position.svg' - } - - property bool hasXProp: obj.constructor.properties().hasOwnProperty('x') - property bool hasYProp: obj.constructor.properties().hasOwnProperty('y') - visible: hasXProp || hasYProp - ToolTip.visible: hovered - ToolTip.text: qsTr("Set %1 %2 position").arg(obj.constructor.displayType()).arg(obj.name) - - onClicked: { - posPicker.objType = obj.type - posPicker.objName = obj.name - posPicker.pickX = hasXProp - posPicker.pickY = hasYProp - posPicker.propertyX = 'x' - posPicker.propertyY = 'y' - posPicker.visible = true - - } - } - - Button { - id: deleteButton - width: parent.minHeight - 10 - height: width - anchors.right: colorPickRect.left - anchors.rightMargin: 5 - anchors.verticalCenter: parent.verticalCenter - icon.name: 'delete' - icon.source: '../icons/common/delete.svg' - icon.color: sysPalette.buttonText - ToolTip.visible: hovered - ToolTip.text: qsTr("Delete %1 %2").arg(obj.constructor.displayType()).arg(obj.name) - - onClicked: { - deleteRecursively(obj) - changed() - } - } - - Rectangle { - id: colorPickRect - anchors.right: parent.right - anchors.rightMargin: 5 - anchors.verticalCenter: parent.verticalCenter - color: obj.color - width: parent.minHeight - 10 - height: width - radius: Math.min(width, height) - border.width: 2 - border.color: sysPalette.windowText - - MouseArea { - anchors.fill: parent - onClicked: pickColor.open() - } - } - - ColorDialog { - id: pickColor - selectedColor: obj.color - title: qsTr("Pick new color for %1 %2").arg(obj.constructor.displayType()).arg(obj.name) - onAccepted: { - Modules.History.addToHistory(new JS.HistoryLib.ColorChanged( - obj.name, obj.type, obj.color, selectedColor.toString() - )) - obj.color = selectedColor.toString() - changed() - } - } - - /*! - \qmlmethod void ObjectRow::deleteRecursively(var object) - Deletes an object and it's dependencies recursively. - */ - function deleteRecursively(object) { - for(let toRemove of object.requiredBy) - deleteRecursively(toRemove) - if(Modules.Objects.currentObjectsByName[object.name] !== undefined) { - // Object still exists - // Temporary fix for objects require not being propertly updated. - object.requiredBy = [] - Modules.History.addToHistory(new JS.HistoryLib.DeleteObject( - object.name, object.type, object.export() - )) - Modules.Objects.deleteObject(object.name) - } - } -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/Loading.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/Loading.qml deleted file mode 100644 index 10f3665..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/Loading.qml +++ /dev/null @@ -1,130 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -import QtQuick.Controls - - -/*! - \qmltype Loading - \inqmlmodule eu.ad5001.LogarithmPlotter.Overlay - \brief Overlay notifiying the user when a file is loading. - - Provides an overlay over the canvas that is shown when the user loads a new file, both to lock the ViewPositionChange - overlay and inform the user of what is loading and how much remains. - - \sa Common, ViewPositionChange -*/ -Item { - id: loadingRoot - opacity: 0 - visible: opacity !== 0 - clip: true - - property int currentlyLoading: 0 - property int maxCurrentLoadingSteps: 0 - - Behavior on opacity { PropertyAnimation {} } - - Rectangle { - anchors.fill: parent - color: sysPalette.window - opacity: 0.85 - } - - Column { - spacing: 5 - anchors { - verticalCenter: parent.verticalCenter - left: parent.left - right: parent.right - } - - Text { - id: loadingTitle - anchors.horizontalCenter: parent.horizontalCenter - font.pixelSize: 20 - color: sysPalette.windowText - } - - ProgressBar { - id: progress - anchors.horizontalCenter: parent.horizontalCenter - width: 300 - from: 0 - value: loadingRoot.maxCurrentLoadingSteps - loadingRoot.currentlyLoading - to: loadingRoot.maxCurrentLoadingSteps - } - - Text { - id: lastFinishedStep - anchors.horizontalCenter: parent.horizontalCenter - color: sysPalette.windowText - } - } - - MouseArea { - id: picker - anchors.fill: parent - hoverEnabled: parent.visible - cursorShape: Qt.ArrowCursor - acceptedButtons: Qt.LeftButton | Qt.RightButton - } - - - - /*! - \qmlmethod void Loading::addedLoadingStep() - Registers one new loading step that will eventually call \c finishedLoadingStep. - */ - function addedLoadingStep() { - if(loadingRoot.maxCurrentLoadingSteps === 1) { - // Only when several ones need to be loaded. - const fileName = Modules.Settings.saveFilename.split('/').pop().split('\\').pop() - loadingTitle.text = qsTr("Loading...") - loadingRoot.opacity = 1 - } - loadingRoot.currentlyLoading++ - loadingRoot.maxCurrentLoadingSteps++ - } - - /*! - \qmlmethod void Loading::finishedLoadingStep() - Marks a loading step as finished and displays the message to the user. - */ - function finishedLoadingStep(message) { - loadingRoot.currentlyLoading-- - const current = loadingRoot.maxCurrentLoadingSteps - loadingRoot.currentlyLoading - lastFinishedStep.text = `${message} (${current}/${loadingRoot.maxCurrentLoadingSteps})` - if(loadingRoot.currentlyLoading === 0) { - loadingRoot.maxCurrentLoadingSteps = 0 - loadingRoot.opacity = 0 - } - } - - - Component.onCompleted: function() { - Modules.Latex.on("async-render-started", (e) => { - addedLoadingStep() - }) - Modules.Latex.on("async-render-finished", (e) => { - const markup = e.markup.length > 20 ? e.markup.substring(0, 15)+"..." : e.markup - finishedLoadingStep(qsTr("Finished rendering of %1").arg(markup)) - }) - } -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/PickLocation.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/PickLocation.qml deleted file mode 100644 index 6ba8bb6..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/PickLocation.qml +++ /dev/null @@ -1,331 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -import QtQuick.Controls -import eu.ad5001.LogarithmPlotter.Setting as Setting -import eu.ad5001.LogarithmPlotter.Common - -/*! - \qmltype PickLocation - \inqmlmodule eu.ad5001.LogarithmPlotter.Overlay - \brief Overlay used to pick a new location for an object. - - Provides an overlay over the canvas that can be shown when the user clicks the "Set position" button - on a specific object. It allows the user to pick a new location on the canvas to place the object at. - This overlay allows to set the precision of the pick as well as whether the pick should be on the plot grid. - - \sa LogarithmPlotter, LogGraphCanvas -*/ -Item { - id: pickerRoot - visible: false - 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 - logGraphCanvas instance. - */ - property var canvas - /*! - \qmlproperty string PickLocationOverlay::objType - Type of object whose position the user is picking. - */ - property string objType: 'Point' - /*! - \qmlproperty string PickLocationOverlay::objType - Name of the object whose position the user is picking. - */ - property string objName: 'A' - /*! - \qmlproperty bool PickLocationOverlay::pickX - true if the property in propertyX is pickable. - */ - property bool pickX: true - /*! - \qmlproperty bool PickLocationOverlay::pickY - true if the property in propertyY is pickable. - */ - property bool pickY: true - /*! - \qmlproperty string PickLocationOverlay::propertyX - Name of the object's property whose x value is being changed. - */ - property string propertyX: 'x' - /*! - \qmlproperty string PickLocationOverlay::propertyY - Name of the object's property whose y value is being changed. - */ - property string propertyY: 'y' - /*! - \qmlproperty int PickLocationOverlay::precision - Precision of the picked value (post-dot precision). - */ - property alias precision: precisionSlider.value - /*! - \qmlproperty bool PickLocationOverlay::userPickX - true if the user can and wants to be picking a position on the x axis. - */ - readonly property bool userPickX: pickX && pickXCheckbox.checked - /*! - \qmlproperty bool PickLocationOverlay::userPickY - true if the user can and wants to be picking a position on the y axis. - */ - readonly property bool userPickY: pickY && pickYCheckbox.checked - - Rectangle { - anchors.fill: parent - color: sysPalette.window - opacity: 0.35 - } - - MouseArea { - id: picker - anchors.fill: parent - hoverEnabled: parent.visible - cursorShape: Qt.CrossCursor - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: function(mouse) { - if(mouse.button == Qt.LeftButton) { // Validate - let newValueX = !parent.userPickX ? null : parseValue(picked.mouseX.toString(), objType, propertyX) - let newValueY = !parent.userPickY ? null : parseValue(picked.mouseY.toString(), objType, propertyY) - let obj = Modules.Objects.currentObjectsByName[objName] - // Set values - if(parent.userPickX && parent.userPickY) { - Modules.History.addToHistory(new JS.HistoryLib.EditedPosition( - objName, objType, obj[propertyX], newValueX, obj[propertyY], newValueY - )) - obj[propertyX] = newValueX - obj[propertyY] = newValueY - obj.update() - objectLists.update() - pickerRoot.picked(obj) - } else if(parent.userPickX) { - Modules.History.addToHistory(new JS.HistoryLib.EditedProperty( - objName, objType, propertyX, obj[propertyX], newValueX - )) - obj[propertyX] = newValueX - obj.update() - objectLists.update() - pickerRoot.picked(obj) - } else if(parent.userPickY) { - Modules.History.addToHistory(new JS.HistoryLib.EditedProperty( - objName, objType, propertyY, obj[propertyY], newValueY - )) - obj[propertyY] = newValueY - obj.update() - objectLists.update() - pickerRoot.picked(obj) - } - } - pickerRoot.visible = false; - } - } - - - - Rectangle { - id: pickerSettings - radius: 15 - color: sysPalette.window - width: pickerSettingsColumn.width + 30; - height: pickerSettingsColumn.height + 20 - property bool folded: false; - x: -15 - ((width-55) * folded); - y: 10 - z: 2 - - Row { - id: pickerSettingsColumn - anchors { - left: parent.left - top: parent.top - leftMargin: 20 - topMargin: 10 - } - spacing: 15 - property int cellHeight: 15 - - Column { - spacing: 5 - // width: 100 - - Text { - text: qsTr("Pointer precision:") - color: sysPalette.windowText - verticalAlignment: Text.AlignVCenter - height: pickerSettingsColumn.cellHeight - } - - Text { - text: qsTr("Snap to grid:") - color: sysPalette.windowText - verticalAlignment: Text.AlignVCenter - height: pickerSettingsColumn.cellHeight - } - - CheckBox { - id: pickXCheckbox - height: pickerSettingsColumn.cellHeight - text: qsTr("Pick X") - checked: pickX - visible: pickX - } - } - - Column { - spacing: 5 - - Slider { - id: precisionSlider - from: 0 - value: 2 - to: 10 - stepSize: 1 - height: pickerSettingsColumn.cellHeight - - ToolTip { - parent: precisionSlider.handle - visible: precisionSlider.pressed - text: precisionSlider.value.toFixed(0) - } - } - - CheckBox { - id: snapToGridCheckbox - height: pickerSettingsColumn.cellHeight - // text: qsTr("Snap to grid") - checked: false - } - - CheckBox { - id: pickYCheckbox - height: pickerSettingsColumn.cellHeight - text: qsTr("Pick Y") - checked: pickY - visible: pickY - } - } - - Button { - width: 24 - anchors.top: parent.top - anchors.bottom: parent.bottom - flat: true - - onClicked: pickerSettings.folded = !pickerSettings.folded - - ToolTip.visible: hovered - ToolTip.delay: 200 - ToolTip.text: pickerSettings.folded ? qsTr("Open picker settings") : qsTr("Hide picker settings") - - Setting.Icon { - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - width: 18 - height: 18 - color: sysPalette.windowText - source: `../icons/common/settings.svg` - } - } - } - } - - Rectangle { - id: xCursor - width: 1 - height: parent.height - color: 'black' - anchors.top: parent.top - anchors.left: parent.left - anchors.leftMargin: Modules.Canvas.x2px(picked.mouseX) - visible: parent.userPickX - } - - Rectangle { - id: yCursor - width: parent.width - height: 1 - color: 'black' - anchors.top: parent.top - anchors.left: parent.left - anchors.topMargin: Modules.Canvas.y2px(picked.mouseY) - visible: parent.userPickY - } - - Text { - id: picked - x: picker.mouseX - width - 5 - y: picker.mouseY - height - 5 - color: 'black' - property double mouseX: { - const axisX = Modules.Canvas.axesSteps.x.value - const xpos = Modules.Canvas.px2x(picker.mouseX) - if(snapToGridCheckbox.checked) { - if(Modules.Settings.logscalex) { - // Calculate the logged power - let pow = Math.pow(10, Math.floor(Math.log10(xpos))) - return pow*Math.round(xpos/pow) - } else { - return axisX*Math.round(xpos/axisX) - } - } else { - return xpos.toFixed(parent.precision) - } - } - property double mouseY: { - const axisY = Modules.Canvas.axesSteps.y.value - const ypos = Modules.Canvas.px2y(picker.mouseY) - if(snapToGridCheckbox.checked) { - return axisY*Math.round(ypos/axisY) - } else { - return ypos.toFixed(parent.precision) - } - } - text: { - if(parent.userPickX && parent.userPickY) - return `(${mouseX}, ${mouseY})` - else if(parent.userPickX) - return `X = ${mouseX}` - else if(parent.userPickY) - return `Y = ${mouseY}` - else - return qsTr('(no pick selected)') - } - } - - - /*! - \qmlmethod void History::parseValue(string value, string objType, string propertyName) - Parses a given \c value as an expression or a number depending on the type of \c propertyName of all \c objType. - */ - function parseValue(value, objType, propertyName) { - if(Modules.Objects.types[objType].properties()[propertyName] == 'number') - return parseFloat(value) - else - return new JS.MathLib.Expression(value) - } -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/ViewPositionChange.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/ViewPositionChange.qml deleted file mode 100644 index 78329a7..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/ViewPositionChange.qml +++ /dev/null @@ -1,146 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick - -/*! - \qmltype ViewPositionChange.Overlay - \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 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 multiplied/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, isEnd) { - const unauthorized = [NaN, Infinity, -Infinity] - const xmin = (Modules.Canvas.px2x(Modules.Canvas.x2px(Modules.Settings.xmin)-deltaX)) - const ymax = Modules.Settings.ymax + deltaY/Modules.Settings.yzoom - if(!unauthorized.includes(xmin)) - Modules.Settings.set("xmin", xmin, isEnd) - if(!unauthorized.includes(ymax)) - Modules.Settings.set("ymax", ymax.toDecimalPrecision(6), isEnd) - Modules.Canvas.requestPaint() - 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 - parent.prevX - let deltaY = mouse.y - parent.prevY - updatePosition(deltaX, deltaY, false) - prevX = mouse.x - prevY = mouse.y - positionChangeTimer = 0 - } - } - - onReleased: function(mouse) { - let deltaX = mouse.x - parent.prevX - let deltaY = mouse.y - parent.prevY - updatePosition(deltaX, deltaY, true) - parent.endPositionChange(deltaX, deltaY) - } - - onWheel: function(wheel) { - // Scrolling - let scrollSteps = Math.round(wheel.angleDelta.y / 120) - let zoomMultiplier = Math.pow(1+parent.baseZoomMultiplier, Math.abs(scrollSteps)) - // Avoid floating-point rounding errors by removing the zoom *after* - let xZoomDelta = (Modules.Settings.xzoom*zoomMultiplier - Modules.Settings.xzoom) - let yZoomDelta = (Modules.Settings.yzoom*zoomMultiplier - Modules.Settings.yzoom) - if(scrollSteps < 0) { // Negative scroll - xZoomDelta *= -1 - yZoomDelta *= -1 - } - let newXZoom = (Modules.Settings.xzoom+xZoomDelta).toDecimalPrecision(0) - let newYZoom = (Modules.Settings.yzoom+yZoomDelta).toDecimalPrecision(0) - // Check if we need to have more precision - if(newXZoom < 10) - newXZoom = (Modules.Settings.xzoom+xZoomDelta).toDecimalPrecision(4) - if(newYZoom < 10) - newYZoom = (Modules.Settings.yzoom+yZoomDelta).toDecimalPrecision(4) - if(newXZoom > 0.5) - Modules.Settings.set("xzoom", newXZoom) - if(newYZoom > 0.5) - Modules.Settings.set("yzoom", newYZoom) - Modules.Canvas.requestPaint() - } - } -} - diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/qmldir b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/qmldir deleted file mode 100644 index 0288c9e..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/qmldir +++ /dev/null @@ -1,5 +0,0 @@ -module eu.ad5001.LogarithmPlotter.Overlay - -Loading 1.0 Loading.qml -PickLocation 1.0 PickLocation.qml -ViewPositionChange 1.0 ViewPositionChange.qml diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/About.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/About.qml deleted file mode 100644 index e82f39c..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/About.qml +++ /dev/null @@ -1,137 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -import QtQuick.Controls - -/*! - \qmltype About - \inqmlmodule eu.ad5001.LogarithmPlotter.Popup - \brief About popup of LogarithmPlotter. - - \sa LogarithmPlotter -*/ -BaseDialog { - id: about - title: qsTr("About LogarithmPlotter") - width: 400 - minimumHeight: 600 - - Item { - anchors { - top: parent.top; - left: parent.left; - bottom: parent.bottom; - right: parent.right; - topMargin: margin; - leftMargin: margin; - bottomMargin: margin; - rightMargin: margin; - } - - Image { - id: logo - source: "../icons/logarithmplotter.svg" - sourceSize.width: 64 - sourceSize.height: 64 - width: 64 - height: 64 - anchors.horizontalCenter: parent.horizontalCenter - anchors.rightMargin: width/2 - anchors.top: parent.top - anchors.topMargin: 10 - } - - Label { - id: appName - anchors.top: logo.bottom - anchors.left: parent.left - anchors.topMargin: 10 - horizontalAlignment: Text.AlignHCenter - width: parent.width - wrapMode: Text.WordWrap - font.pixelSize: 25 - text: qsTr("LogarithmPlotter v%1").arg(Helper.getVersion()) - } - - Label { - id: description - anchors.top: appName.bottom - anchors.left: parent.left - anchors.topMargin: 10 - horizontalAlignment: Text.AlignHCenter - width: parent.width - wrapMode: Text.WordWrap - font.pixelSize: 18 - text: qsTr("2D plotter software to make BODE plots, sequences and repartition functions.") - } - - Label { - id: debugInfos - anchors.top: description.bottom - anchors.left: parent.left - anchors.topMargin: 10 - horizontalAlignment: Text.AlignHCenter - width: parent.width - wrapMode: Text.WordWrap - font.pixelSize: 14 - text: Helper.getDebugInfos() - } - - Label { - id: copyrightInfos - anchors.top: debugInfos.bottom - anchors.horizontalCenter: parent.horizontalCenter - anchors.topMargin: 10 - width: Math.min(410, parent.width) - wrapMode: Text.WordWrap - textFormat: Text.RichText - font.pixelSize: 13 - text: "Copyright © 2021-2025 Ad5001 <mail@ad5001.eu>
    -
    -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 http://www.gnu.org/licenses/." - onLinkActivated: Qt.openUrlExternally(link) - } - - Row { - id: buttonsRow - anchors.top: copyrightInfos.bottom - anchors.horizontalCenter: parent.horizontalCenter - anchors.topMargin: 10 - spacing: 5 - - Button { - id: openIssueButton - text: qsTr('Report a bug') - icon.name: 'tools-report-bug' - onClicked: Qt.openUrlExternally('https://git.ad5001.eu/Ad5001/LogarithmPlotter') - } - - Button { - id: officialWebsiteButton - text: qsTr('Official website') - icon.name: 'web-browser' - onClicked: Qt.openUrlExternally('https://apps.ad5001.eu/logarithmplotter/') - } - } - } -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/BaseDialog.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/BaseDialog.qml deleted file mode 100644 index a9c73a6..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/BaseDialog.qml +++ /dev/null @@ -1,59 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -import QtQuick.Controls - -/*! - \qmltype BaseDialog - \inqmlmodule eu.ad5001.LogarithmPlotter.Popup - \brief Base dialog window in replacement of Dialog Popup from Qt 5. - - \sa LogarithmPlotter -*/ - -Window { - id: base - color: sysPalette.window - visible: false; - flags: Qt.Dialog | Qt.Popup | Qt.MSWindowsFixedSizeDialogHint - modality: Qt.WindowModal - minimumWidth: width - maximumWidth: width - height: minimumHeight - property int margin: 10 - - Button { - id: closeButton - anchors.bottom: parent.bottom - anchors.right: parent.right - anchors.bottomMargin: margin - anchors.rightMargin: margin - text: qsTr('Close') - onClicked: close() - } - - Shortcut { - sequence: "Esc" - onActivated: base.close() - } - - function open() { - show() - } -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/GreetScreen.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/GreetScreen.qml deleted file mode 100644 index a083d64..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/GreetScreen.qml +++ /dev/null @@ -1,156 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -import QtQuick.Controls -import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting - -/*! - \qmltype GreetScreen - \inqmlmodule eu.ad5001.LogarithmPlotter.Popup - \brief Overlay displayed when LogarithmPlotter is launched for the first time or when it was just updated. - - It contains several settings as well as an easy access to the changelog - - \sa LogarithmPlotter, Settings, AppMenuBar, Changelog -*/ -Popup { - id: greetingPopup - x: (parent.width-width)/2 - y: Math.max(20, (parent.height-height)/2) - width: greetingLayout.width+20 - height: Math.min(parent.height-40, 700) - modal: true - focus: true - clip: true - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside - - Column { - id: greetingLayout - width: 600 - 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()) - } - } - - 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() - - 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.Wrap - clip: true - } - } - } - } - - Component.onCompleted: if(Helper.getSetting("last_install_greet") != Helper.getVersion()+1) { - greetingPopup.open() - } - - onClosed: Helper.setSetting("last_install_greet", Helper.getVersion()) -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/InsertCharacter.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/InsertCharacter.qml deleted file mode 100644 index 8111f85..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/InsertCharacter.qml +++ /dev/null @@ -1,123 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -import QtQuick.Controls -import QtQml.Models - -/*! - \qmltype InsertCharacter - \inqmlmodule eu.ad5001.LogarithmPlotter.Setting - \brief Popup to insert special character. - - \sa TextSetting, ExpressionEditor -*/ -Popup { - id: insertPopup - - 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: insertGrid.width + 10 - height: insertGrid.height + 10 - modal: true - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent - - GridView { - id: insertGrid - width: 280 - height: Math.ceil(model.count/columns)*cellHeight - property int columns: 7 - cellWidth: width/columns - cellHeight: cellWidth - - 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] - } - - model: ListModel {} - - delegate: Button { - id: insertBtn - width: insertGrid.cellWidth - height: insertGrid.cellHeight - text: chr - flat: text == " " - font.pixelSize: 18 - - onClicked: { - insertPopup.selected(text) - insertPopup.close() - } - } - - Component.onCompleted: function() { - for(const chr of insertChars) { - model.append({ 'chr': chr }) - } - } - - Keys.onEscapePressed: parent.close() - } - - function setFocus() { - insertGrid.currentIndex = 0 - insertGrid.forceActiveFocus() - } - -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Preferences.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Preferences.qml deleted file mode 100644 index 9ed40c8..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Preferences.qml +++ /dev/null @@ -1,255 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting -import eu.ad5001.LogarithmPlotter.Common - -/*! - \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.name - 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.name - 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.name - icon: `settings/${setting.icon}.svg` - editable: true - currentIndex: find(setting.value()) - model: setting.defaultValues - onAccepted: function() { - editText = JS.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.name - 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.name - icon: `settings/${setting.icon}.svg` - defValue: JS.Utils.simplifyExpression(setting.value()) - variables: setting.variables - allowGraphObjects: false - property string propertyName: setting.name - 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.modelName = modelData - } - } - } - - 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[modelName] - anchors { - top: parent.top - bottom: parent.bottom - } - ScrollBar.vertical: ScrollBar { } - property string modelName: 'general' - - - header: Text { - id: settingCategoryName - font.pixelSize: 32 - height: 48 - color: sysPalette.windowText - text: qsTranslate('settingCategory', settingView.modelName) - - 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 - 20 - property var setting: Modules.Preferences.categories[settingView.modelName][index] - sourceComponent: { - if(setting.type === "bool") - return boolSettingComponent - else if(setting.type === "enum") - return enumIntSettingComponent - else if(setting.type === "number") - return numberSettingComponent - else if(setting.type === "expression") - return expressionSettingComponent - else if(setting.type === "string") - return stringSettingComponent - else - console.log('Unknown setting type!', setting.constructor.nameInConfig, setting.constructor) - } - } - } - } - } - - // Component.onCompleted: open() -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/ThanksTo.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/ThanksTo.qml deleted file mode 100644 index 85e4a64..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/ThanksTo.qml +++ /dev/null @@ -1,367 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -import QtQuick.Dialogs -import QtQuick.Controls - -/*! - \qmltype ThanksTo - \inqmlmodule eu.ad5001.LogarithmPlotter.Popup - \brief Thanks to popup of LogarithmPlotter. - - \sa LogarithmPlotter -*/ -BaseDialog { - id: thanks - title: qsTr("Thanks and Contributions - LogarithmPlotter") - width: 450 - minimumHeight: 600 - - ScrollView { - - anchors { - top: parent.top; - left: parent.left; - bottom: parent.bottom; - right: parent.right; - topMargin: margin; - leftMargin: margin; - bottomMargin: margin+30; - } - - Column { - - anchors { - left: parent.left; - } - - width: thanks.width - 2*margin - spacing: 10 - - ListView { - id: librariesListView - anchors.left: parent.left - width: parent.width - //height: parent.height - implicitHeight: contentItem.childrenRect.height - interactive: false - - model: ListModel { - Component.onCompleted: { - append({ - libName: 'expr-eval', - license: 'MIT', - licenseLink: 'https://raw.githubusercontent.com/silentmatt/expr-eval/master/LICENSE.txt', - linkName: qsTr('Source code'), - link: 'https://github.com/silentmatt/expr-eval', - authors: [{ - authorLine: qsTr('Original library by Raphael Graf'), - email: 'r@undefined.ch', - website: 'https://web.archive.org/web/20111023001618/http://www.undefined.ch/mparser/index.html', - websiteName: qsTr('Source') - }, { - authorLine: qsTr('Ported to Javascript by Matthew Crumley'), - email: 'email@matthewcrumley.com', - website: 'https://silentmatt.com/', - websiteName: qsTr('Website') - }, { - authorLine: qsTr('Ported to QMLJS by Ad5001'), - email: 'mail@ad5001.eu', - website: 'https://ad5001.eu/', - websiteName: qsTr('Website') - }] - }) - } - } - - header: Label { - id: librariesUsedHeader - wrapMode: Text.WordWrap - font.pixelSize: 25 - text: qsTr("Libraries included") - height: implicitHeight + 10 - } - - delegate: Column { - id: libClmn - width: librariesListView.width - spacing: 10 - - Item { - height: libraryHeader.height - width: parent.width - - Label { - id: libraryHeader - anchors.left: parent.left - wrapMode: Text.WordWrap - font.pixelSize: 18 - text: libName - } - - Row { - anchors.right: parent.right - height: parent.height - spacing: 10 - - Button { - height: parent.height - text: license - icon.name: 'license' - onClicked: Qt.openUrlExternally(licenseLink) - } - - Button { - height: parent.height - text: linkName - icon.name: 'web-browser' - onClicked: Qt.openUrlExternally(link) - } - } - } - - ListView { - id: libAuthors - anchors.left: parent.left - anchors.leftMargin: 10 - model: authors - width: parent.width - 10 - implicitHeight: contentItem.childrenRect.height - interactive: false - - delegate: Item { - id: libAuthor - width: librariesListView.width - 10 - height: 50 - - Label { - id: libAuthorName - anchors.left: parent.left - anchors.right: buttons.left - anchors.verticalCenter: parent.verticalCenter - wrapMode: Text.WordWrap - font.pixelSize: 14 - text: authorLine - } - - Row { - id: buttons - anchors.right: parent.right - height: parent.height - spacing: 10 - - Button { - anchors.verticalCenter: parent.verticalCenter - text: websiteName - icon.name: 'web-browser' - height: parent.height - 10 - onClicked: Qt.openUrlExternally(website) - } - - Button { - anchors.verticalCenter: parent.verticalCenter - text: qsTr('Email') - icon.name: 'email' - height: parent.height - 10 - onClicked: Qt.openUrlExternally('mailto:' + email) - } - } - } - } - - Rectangle { - id: libSeparator - opacity: 0.3 - color: sysPalette.windowText - width: parent.width - height: 1 - } - } - } - - ListView { - id: translationsListView - anchors.left: parent.left - width: parent.width - implicitHeight: contentItem.childrenRect.height - interactive: false - spacing: 3 - - - model: ListModel { - Component.onCompleted: { - const authors = { - Ad5001: { - authorLine: 'Ad5001', - email: 'mail@ad5001.eu', - website: 'https://ad5001.eu', - websiteName: qsTr('Website') - }, - Ovari: { - authorLine: 'Óvári', - website: 'https://github.com/ovari', - websiteName: qsTr('Github') - }, - comradekingu: { - authorLine: 'Allan Nordhøy', - website: 'https://github.com/comradekingu', - websiteName: qsTr('Github') - }, - IngrownMink4: { - authorLine: 'IngrownMink4', - website: 'https://github.com/IngrownMink4', - websiteName: qsTr('Github') - }, - gallegonovato: { - authorLine: 'gallegonovato', - website: '', - websiteName: '' - }, - TamilNeram: { - authorLine: 'தமிழ் நேரம்', - website: 'https://github.com/TamilNeram', - websiteName: qsTr('Github') - } - } - - append({ - tranName: '🇬🇧 ' + qsTr('English'), - link: 'https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/en/', - authors: [authors.Ad5001] - }) - append({ - tranName: '🇫🇷 ' + qsTr('French'), - link: 'https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/fr/', - authors: [authors.Ad5001] - }) - append({ - tranName: '🇩🇪 ' + qsTr('German'), - link: 'https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/de/', - authors: [authors.Ad5001] - }) - append({ - tranName: '🇭🇺 ' + qsTr('Hungarian'), - link: 'https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/hu/', - authors: [authors.Ovari] - }) - append({ - tranName: '🇳🇴 ' + qsTr('Norwegian'), - link: 'https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/no/', - authors: [authors.comradekingu, authors.Ad5001] - }) - append({ - tranName: '🇪🇸 ' + qsTr('Spanish'), - link: 'https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/es/', - authors: [authors.IngrownMink4, authors.gallegonovato] - }) - append({ - tranName: '🇱🇰 ' + qsTr('Tamil'), - link: 'https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/ta/', - authors: [authors.TamilNeram] - }) - } - } - - header: Label { - id: translationsHeader - wrapMode: Text.WordWrap - font.pixelSize: 25 - text: qsTr("Translations included") - height: implicitHeight + 10 - } - - delegate: Column { - id: tranClmn - width: translationsListView.width - - Item { - width: parent.width - height: translationHeader.height + 10 - - Label { - id: translationHeader - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - wrapMode: Text.WordWrap - font.pixelSize: 18 - text: tranName - } - - Row { - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - height: 30 - spacing: 10 - - Button { - height: parent.height - text: qsTr('Improve') - icon.name: 'web-browser' - onClicked: Qt.openUrlExternally(link) - } - } - } - - ListView { - id: tranAuthors - anchors.left: parent.left - anchors.leftMargin: 10 - model: authors - width: parent.width - 10 - implicitHeight: contentItem.childrenRect.height - interactive: false - - delegate: Item { - id: tranAuthor - width: tranAuthors.width - height: 40 - - Label { - id: tranAuthorName - anchors.left: parent.left - //anchors.right: buttons.left - anchors.verticalCenter: parent.verticalCenter - wrapMode: Text.WordWrap - font.pixelSize: 14 - text: authorLine - } - - Row { - id: buttons - anchors.left: tranAuthorName.right - anchors.leftMargin: 10 - anchors.verticalCenter: parent.verticalCenter - height: 30 - spacing: 10 - - Button { - text: websiteName - visible: websiteName !== "" - icon.name: 'web-browser' - height: parent.height - onClicked: Qt.openUrlExternally(website) - } - } - } - } - } - } - } - } -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/AutocompletionCategory.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/AutocompletionCategory.qml deleted file mode 100644 index 897af70..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/AutocompletionCategory.qml +++ /dev/null @@ -1,125 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick -import QtQuick.Controls - -/*! - \qmltype AutocompletionCategory - \inqmlmodule eu.ad5001.LogarithmPlotter.Setting - \brief ListView representing a category of autocompletion. - - \sa ExpressionEditor -*/ -ListView { - id: listFiltered - /*! - \qmlproperty int AutocompletionCategory::itemStartIndex - Start index of the first element in this list compared to the global autocompletion index. - */ - property int itemStartIndex: 0 - /*! - \qmlproperty int AutocompletionCategory::itemSelected - The global autocompletion index. - */ - property int itemSelected: 0 - - /*! - \qmlproperty string AutocompletionCategory::category - Name of the category. - */ - property string category: "" - - /*! - \qmlproperty var AutocompletionCategory::categoryItems - List of items in this category. To be filtered by the autocomplete to filters. - */ - property var categoryItems: [] - - /*! - \qmlproperty var AutocompletionCategory::autocompleteGenerator - Javascript function taking the name of the item to create an autocompletion item (dictionary with - a 'text', 'annotation' 'autocomplete', and 'cursorFinalOffset' keys. - */ - property var autocompleteGenerator: (item) => {return {'text': item, 'autocomplete': item, 'annotation': '', 'cursorFinalOffset': 0}} - - /*! - \qmlproperty string AutocompletionCategory::baseText - Text to autocomplete. - */ - property string baseText: "" - - /*! - \qmlproperty bool AutocompletionCategory::visbilityCondition - Condition to be met for the category to be visible. - */ - property bool visbilityCondition: true - - width: parent.width - visible: model.length > 0 - implicitHeight: contentItem.childrenRect.height - model: visbilityCondition ? categoryItems.filter((item) => item.includes(baseText)).map(autocompleteGenerator) : [] - - header: Column { - width: listFiltered.width - spacing: 2 - topPadding: 5 - bottomPadding: 5 - - Text { - leftPadding: 5 - text: listFiltered.category - color: sysPalette.windowText - } - - Rectangle { - height: 1 - color: 'gray' - width: parent.width - } - } - - delegate: Rectangle { - property bool selected: index + listFiltered.itemStartIndex == listFiltered.itemSelected - - width: listFiltered.width - height: Math.max(autocompleteText.height, annotationText.height) - color: selected ? sysPalette.highlight : 'transparent' - - Text { - id: autocompleteText - topPadding: 2 - bottomPadding: 2 - leftPadding: 15 - text: listFiltered.model[index].text - color: parent.selected ? sysPalette.highlightedText : sysPalette.windowText - } - - Text { - id: annotationText - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - topPadding: 2 - bottomPadding: 2 - rightPadding: 15 - font.pixelSize: autocompleteText.font.pixelSize - 2 - text: listFiltered.model[index].annotation - color: parent.selected ? sysPaletteIn.highlightedText : sysPaletteIn.windowText - } - } -} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml deleted file mode 100644 index 35bba0a..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml +++ /dev/null @@ -1,646 +0,0 @@ -/** - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . - */ - -import QtQuick.Controls -import QtQuick -import Qt.labs.platform as Native -import eu.ad5001.LogarithmPlotter.Popup 1.0 as P -import eu.ad5001.LogarithmPlotter.Common - - -/*! - \qmltype ExpressionEditor - \inqmlmodule eu.ad5001.LogarithmPlotter.Setting - \brief Setting to edit strings and numbers. - - \sa EditorDialog, AutocompletionCategory -*/ -Item { - id: control - height: 30 - - /*! - \qmlsignal ExpressionEditor::changed(var newValue) - - Emitted when the value of the expression has been changed. - The corresponding handler is \c onChanged. - */ - signal changed(var newValue) - - /*! - \qmlproperty string ExpressionEditor::defValue - Default editable expression value of the editor. - */ - property string defValue - /*! - \qmlproperty string ExpressionEditor::value - Value of the editor. - */ - property alias value: editor.text - /*! - \qmlproperty string ExpressionEditor::self - Object or context of the expression to be edited. - Used to prevent circular dependency. - */ - property string self: "" - /*! - \qmlproperty var ExpressionEditor::variables - Accepted variables for the expression. - */ - property var variables: [] - /*! - \qmlproperty string ExpressionEditor::placeholderText - Value of the editor. - */ - property alias placeholderText: editor.placeholderText - /*! - \qmlproperty string ExpressionEditor::label - Label of the editor. - */ - property string label - /*! - \qmlproperty string ExpressionEditor::icon - Icon path of the editor. - */ - 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 - Characters that when pressed, should be immediately followed up by their closing character. - TODO: Make it configurable. - */ - readonly property var openAndCloseMatches: { - "(": ")", - "[": "]", - "'": "'", - '"': '"' - } - - /*! - \qmlproperty string ExpressionEditor::colorSchemes - Color schemes of the editor. - */ - readonly property var colorSchemes: [ - { // Breeze Light - 'NORMAL': "#1F1C1B", - 'VARIABLE': "#0057AE", - 'CONSTANT': "#006E28", - 'FUNCTION': "#644A9B", - 'OPERATOR': "#CA60CA", - 'STRING': "#BF0303", - 'NUMBER': "#B08000" - }, - { // Breeze Dark - 'NORMAL': "#CFCFC2", - 'VARIABLE': "#2980B9", - 'CONSTANT': "#27AE60", - 'FUNCTION': "#8E44AD", - 'OPERATOR': "#A44EA4", - 'STRING': "#F44F4F", - 'NUMBER': "#F67400" - }, - { // Solarized - 'NORMAL': "#839496", - 'VARIABLE': "#B58900", - 'CONSTANT': "#859900", - 'FUNCTION': "#268BD2", - 'OPERATOR': "#859900", - 'STRING': "#2AA198", - 'NUMBER': "#2AA198" - }, - { // GitHub Light - 'NORMAL': "#24292E", - 'VARIABLE': "#D73A49", - 'CONSTANT': "#6F42C1", - 'FUNCTION': "#6F42C1", - 'OPERATOR': "#24292E", - 'STRING': "#032F62", - 'NUMBER': "#005CC5" - }, - { // GitHub Dark - 'NORMAL': "#E1E4E8", - 'VARIABLE': "#F97583", - 'CONSTANT': "#B392f0", - 'FUNCTION': "#B392f0", - 'OPERATOR': "#E1E4E8", - 'STRING': "#9ECBFF", - 'NUMBER': "#79B8FF" - }, - { // Nord - 'NORMAL': "#D8DEE9", - 'VARIABLE': "#81A1C1", - 'CONSTANT': "#8FBCBB", - 'FUNCTION': "#88C0D0", - 'OPERATOR': "#81A1C1", - 'STRING': "#A3BE8C", - 'NUMBER': "#B48EAD" - }, - { // Monokai - 'NORMAL': "#F8F8F2", - 'VARIABLE': "#66D9EF", - 'CONSTANT': "#F92672", - 'FUNCTION': "#A6E22E", - 'OPERATOR': "#F8F8F2", - 'STRING': "#E6DB74", - 'NUMBER': "#AE81FF" - } - ] - - Icon { - id: iconLabel - anchors.top: parent.top - anchors.topMargin: parent.icon == "" ? 0 : 3 - source: control.visible && parent.icon != "" ? "../icons/" + control.icon : "" - width: height - height: parent.icon == "" || !visible ? 0 : 24 - color: sysPalette.windowText - } - - Label { - id: labelItem - anchors.left: iconLabel.right - anchors.leftMargin: parent.icon == "" ? 0 : 5 - anchors.top: parent.top - height: parent.height - width: Math.max(85, implicitWidth) - verticalAlignment: TextInput.AlignVCenter - //color: sysPalette.windowText - text: visible ? qsTranslate("control", "%1: ").arg(control.label) : "" - visible: control.label != "" - } - - Native.MessageDialog { - id: parsingErrorDialog - title: qsTranslate("expression", "LogarithmPlotter - Parsing error") - text: "" - function showDialog(propName, propValue, error) { - text = qsTranslate("expression", "Error while parsing expression for property %1:\n%2\n\nEvaluated expression: %3") - .arg(qsTranslate('prop', propName)) - .arg(error).arg(propValue) - open() - } - } - - TextField { - id: editor - anchors.top: parent.top - anchors.left: labelItem.right - anchors.leftMargin: 5 - width: control.width - (labelItem.visible ? labelItem.width + 5 : 0) - iconLabel.width - 5 - height: parent.height - verticalAlignment: TextInput.AlignVCenter - horizontalAlignment: control.label == "" ? TextInput.AlignLeft : TextInput.AlignHCenter - text: control.defValue - color: syntaxHighlightingEnabled ? sysPalette.window : sysPalette.windowText - focus: true - selectByMouse: true - - property bool autocompleteEnabled: Helper.getSetting("autocompletion.enabled") - property bool syntaxHighlightingEnabled: Helper.getSetting("expression_editor.colorize") - property bool autoClosing: Helper.getSetting("expression_editor.autoclose") - property var tokens: autocompleteEnabled || syntaxHighlightingEnabled ? parent.tokens(text) : [] - - Keys.priority: Keys.BeforeItem // Required for knowing which key the user presses. - - onEditingFinished: { - if(insertButton.focus || insertPopup.focus) return - let value = text - if(value != "" && value.toString() != parent.defValue) { - let expr = parent.parse(value) - if(expr != null) { - control.changed(expr) - defValue = expr.toEditableString() - } - } - } - - onActiveFocusChanged: { - if(activeFocus && autocompleteEnabled) - autocompletePopup.open() - else - autocompletePopup.close() - } - - cursorDelegate: Rectangle { - visible: editor.cursorVisible - color: sysPalette.windowText - width: editor.cursorRectangle.width - } - - Keys.onUpPressed: function(event) { - if(autocompleteEnabled) - if(acPopupContent.itemSelected == 0) - acPopupContent.itemSelected = acPopupContent.itemCount-1 - else - acPopupContent.itemSelected = acPopupContent.itemSelected-1 - event.accepted = true - } - - Keys.onDownPressed: function(event) { - if(autocompleteEnabled) - if(acPopupContent.itemSelected == Math.min(acPopupContent.itemCount-1)) - acPopupContent.itemSelected = 0 - else - acPopupContent.itemSelected = acPopupContent.itemSelected+1 - event.accepted = true - } - - Keys.onPressed: function(event) { - // Autocomplete popup events - if(autocompleteEnabled && (event.key == Qt.Key_Enter || event.key == Qt.Key_Return) && acPopupContent.itemCount > 0) { - acPopupContent.autocomplete() - event.accepted = true - } else - acPopupContent.itemSelected = 0 - - - if(event.text in parent.openAndCloseMatches && autoClosing) { - let start = selectionStart - insert(selectionStart, event.text) - insert(selectionEnd, parent.openAndCloseMatches[event.text]) - cursorPosition = start+1 - event.accepted = true - } - } - - Text { - id: colorizedEditor - anchors.fill: editor - verticalAlignment: TextInput.AlignVCenter - horizontalAlignment: control.label == "" ? TextInput.AlignLeft : TextInput.AlignHCenter - textFormat: Text.StyledText - text: parent.syntaxHighlightingEnabled ? colorize(parent.tokens) : "" - color: sysPalette.windowText - visible: parent.syntaxHighlightingEnabled - //font.pixelSize: parent.font.pixelSize - //opacity: editor.activeFocus ? 0 : 1 - } - - Popup { - id: autocompletePopup - x: 0 - y: parent.height - closePolicy: Popup.NoAutoClose - - width: editor.width - height: acPopupContent.height - padding: 0 - - Column { - id: acPopupContent - width: parent.width - - readonly property var identifierTokenTypes: [ - JS.Parsing.TokenType.VARIABLE, - JS.Parsing.TokenType.FUNCTION, - JS.Parsing.TokenType.CONSTANT - ] - property var currentToken: generateTokenInformation(getTokenAt(editor.tokens, editor.cursorPosition)) - property var previousToken: generateTokenInformation(getPreviousToken(currentToken.token)) - property var previousToken2: generateTokenInformation(getPreviousToken(previousToken.token)) - property var previousToken3: generateTokenInformation(getPreviousToken(previousToken2.token)) - visible: currentToken.exists - - // Focus handling. - readonly property var lists: [objectPropertiesList, variablesList, constantsList, functionsList, executableObjectsList, objectsList] - readonly property int itemCount: objectPropertiesList.model.length + variablesList.model.length + constantsList.model.length + functionsList.model.length + executableObjectsList.model.length + objectsList.model.length - property int itemSelected: 0 - - /*! - \qmlmethod var ExpressionEditor::generateTokenInformation(var token) - Generates basic information about the given token (existence and type) used in autocompletion). - */ - function generateTokenInformation(token) { - let exists = token != null - return { - 'token': token, - 'exists': exists, - 'value': exists ? token.value : null, - 'type': exists ? token.type : null, - 'startPosition': exists ? token.startPosition : 0, - 'dot': exists ? (token.type == JS.Parsing.TokenType.PUNCT && token.value == ".") : false, - 'identifier': exists ? identifierTokenTypes.includes(token.type) : false - } - } - /*! - \qmlmethod void ExpressionEditor::autocompleteInfoAt(int idx) - Returns the autocompletion text information at a given position. - The information contains key 'text' (description text), 'autocomplete' (text to insert) - and 'cursorFinalOffset' (amount to add to the cursor's position after the end of the autocomplete) - */ - function autocompleteInfoAt(idx) { - if(idx >= itemCount) return "" - let startIndex = 0 - for(let list of lists) { - if(idx < startIndex + list.model.length) - return list.model[idx-startIndex] - startIndex += list.model.length - } - } - - /*! - \qmlmethod void ExpressionEditor::autocomplete() - Autocompletes with the current selected word. - */ - function autocomplete() { - let autotext = autocompleteInfoAt(itemSelected) - let startPos = currentToken.startPosition - console.log("Replacing", currentToken.value, "at", startPos, "with", autotext.autocomplete) - editor.remove(startPos, startPos+currentToken.value.length) - editor.insert(startPos, autotext.autocomplete) - editor.cursorPosition = startPos+autotext.autocomplete.length+autotext.cursorFinalOffset - } - - /*! - \qmlmethod var ExpressionEditor::getPreviousToken(var token) - Returns the token before this one. - */ - function getPreviousToken(token) { - let newToken = getTokenAt(editor.tokens, token.startPosition) - if(newToken != null && newToken.type == JS.Parsing.TokenType.WHITESPACE) - return getPreviousToken(newToken) - return newToken - } - - AutocompletionCategory { - id: objectPropertiesList - - category: qsTr("Object Properties") - visbilityCondition: control.allowGraphObjects && doesObjectExist - itemStartIndex: 0 - itemSelected: parent.itemSelected - property bool isEnteringProperty: ( - // Current token is dot. - (parent.currentToken.dot && parent.previousToken.identifier && !parent.previousToken2.dot) || - // Current token is property identifier - (parent.currentToken.identifier && parent.previousToken.dot && parent.previousToken2.identifier && !parent.previousToken3.dot)) - property string objectName: isEnteringProperty ? - (parent.currentToken.dot ? parent.previousToken.value : parent.previousToken2.value) - : "" - property bool doesObjectExist: isEnteringProperty && (objectName in Modules.Objects.currentObjectsByName) - property var objectProperties: doesObjectExist ? - Modules.Objects.currentObjectsByName[objectName].constructor.properties() : - {} - categoryItems: Object.keys(objectProperties) - autocompleteGenerator: (item) => { - let propType = objectProperties[item] - return { - 'text': item, 'annotation': propType == null ? '' : propType.toString(), - 'autocomplete': parent.currentToken.dot ? `.${item} ` : `${item} `, - 'cursorFinalOffset': 0 - } - - } - baseText: parent.visible && !parent.currentToken.dot ? parent.currentToken.value : "" - } - - AutocompletionCategory { - id: variablesList - - category: qsTr("Variables") - visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot - itemStartIndex: objectPropertiesList.model.length - itemSelected: parent.itemSelected - categoryItems: control.variables - autocompleteGenerator: (item) => {return { - 'text': item, 'annotation': '', - 'autocomplete': item + " ", 'cursorFinalOffset': 0 - }} - baseText: parent.visible ? parent.currentToken.value : "" - } - - AutocompletionCategory { - id: constantsList - - category: qsTr("Constants") - visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot - itemStartIndex: variablesList.itemStartIndex + variablesList.model.length - itemSelected: parent.itemSelected - categoryItems: JS.Parsing.CONSTANTS_LIST - autocompleteGenerator: (item) => {return { - 'text': item, 'annotation': JS.Parsing.CONSTANTS[item], - 'autocomplete': item + " ", 'cursorFinalOffset': 0 - }} - baseText: parent.visible ? parent.currentToken.value : "" - } - - AutocompletionCategory { - id: functionsList - - category: qsTr("Functions") - visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot - itemStartIndex: constantsList.itemStartIndex + constantsList.model.length - itemSelected: parent.itemSelected - categoryItems: JS.Parsing.FUNCTIONS_LIST - autocompleteGenerator: (item) => {return { - 'text': item, 'annotation': JS.Parsing.FUNCTIONS_USAGE[item].join(', '), - 'autocomplete': item+'()', 'cursorFinalOffset': -1 - }} - baseText: parent.visible ? parent.currentToken.value : "" - } - - AutocompletionCategory { - id: executableObjectsList - - category: qsTr("Executable Objects") - visbilityCondition: control.allowGraphObjects && parent.currentToken.identifier && !parent.previousToken.dot - itemStartIndex: functionsList.itemStartIndex + functionsList.model.length - itemSelected: parent.itemSelected - categoryItems: Modules.Objects.getObjectsName("ExecutableObject").filter(obj => obj != self) - autocompleteGenerator: (item) => {return { - 'text': item, 'annotation': Modules.Objects.currentObjectsByName[item] == null ? '' : Modules.Objects.currentObjectsByName[item].constructor.displayType(), - 'autocomplete': item+'()', 'cursorFinalOffset': -1 - }} - baseText: parent.visible ? parent.currentToken.value : "" - } - - AutocompletionCategory { - id: objectsList - - category: qsTr("Objects") - visbilityCondition: control.allowGraphObjects && parent.currentToken.identifier && !parent.previousToken.dot - itemStartIndex: executableObjectsList.itemStartIndex + executableObjectsList.model.length - itemSelected: parent.itemSelected - categoryItems: Object.keys(Modules.Objects.currentObjectsByName).filter(obj => obj != self) - autocompleteGenerator: (item) => {return { - 'text': item, 'annotation': `${Modules.Objects.currentObjectsByName[item].constructor.displayType()}`, - 'autocomplete': item+'.', 'cursorFinalOffset': 0 - }} - baseText: parent.visible ? parent.currentToken.value : "" - } - } - } - } - - Button { - id: insertButton - anchors.right: parent.right - anchors.rightMargin: 5 - anchors.verticalCenter: parent.verticalCenter - width: 20 - height: width - - Icon { - id: icon - width: 12 - height: 12 - anchors.centerIn: parent - - color: sysPalette.windowText - source: '../icons/properties/expression.svg' - } - - onClicked: { - insertPopup.open() - insertPopup.setFocus() - } - } - - P.InsertCharacter { - id: insertPopup - - x: parent.width - width - y: parent.height - - category: "expression" - - onSelected: function(c) { - editor.insert(editor.cursorPosition, c) - } - - onClosed: function() { - focus = false - editor.focus = true - } - } - - /*! - \qmlmethod var ExpressionEditor::parse(string newExpression) - Parses the \c newExpression as an expression, checks for errors, shows them if any. - Returns the parsed expression if possible, null otherwise.. - */ - function parse(newExpression) { - let expr = null - try { - expr = new JS.MathLib.Expression(value.toString()) - // Check if the expression is valid, throws error otherwise. - if(!expr.allRequirementsFulfilled()) { - let undefVars = expr.undefinedVariables() - if(undefVars.length > 1) - throw new Error(qsTranslate('error', 'No object found with names %1.').arg(undefVars.join(', '))) - else - throw new Error(qsTranslate('error', 'No object found with name %1.').arg(undefVars.join(', '))) - } - if(expr.requiredObjects().includes(control.self)) - throw new Error(qsTranslate('error', 'Object cannot be dependent on itself.')) - // Recursive dependencies - let dependentOnSelfObjects = expr.requiredObjects().filter( - (obj) => Modules.Objects.currentObjectsByName[obj].getDependenciesList() - .includes(Modules.Objects.currentObjectsByName[control.self]) - ) - if(dependentOnSelfObjects.length == 1) - throw new Error(qsTranslate('error', 'Circular dependency detected. Object %1 depends on %2.').arg(dependentOnSelfObjects[0].toString()).arg(control.self)) - else if(dependentOnSelfObjects.length > 1) - throw new Error(qsTranslate('error', 'Circular dependency detected. Objects %1 depend on %2.').arg(dependentOnSelfObjects.map(obj => obj.toString()).join(', ')).arg(control.self)) - //console.log(control.self, propertyName, expr.execute()) - return expr - } catch(e) { - // Error in expression - parsingErrorDialog.showDialog(propertyName, newExpression, e.message) - return null - } - } - - /*! - \qmlmethod var ExpressionEditor::tokens(string expressionText) - Generates a list of tokens from the given. - */ - function tokens(text) { - let tokenizer = new JS.Parsing.Tokenizer(new JS.Parsing.Input(text), true, false) - let tokenList = [] - let token - while((token = tokenizer.next()) != null) - tokenList.push(token) - return tokenList - } - - /*! - \qmlmethod var ExpressionEditor::getTokenAt(var tokens, int position) - Gets the token at the given position within the text. - Returns null if out of bounds. - */ - function getTokenAt(tokenList, position) { - let currentPosition = 0 - for(let token of tokenList) - if(position <= (currentPosition + token.value.length)) - return token - else - currentPosition += token.value.length - return null - } - - /*! - \qmlmethod var ExpressionEditor::colorize(var tokenList) - Creates an HTML colorized string of the given tokens. - Returns the colorized and escaped expression if possible, null otherwise.. - */ - function colorize(tokenList) { - let parsedText = "" - let scheme = colorSchemes[Helper.getSetting("expression_editor.color_scheme")] - for(let token of tokenList) { - switch(token.type) { - case JS.Parsing.TokenType.VARIABLE: - parsedText += `${token.value}` - break; - case JS.Parsing.TokenType.CONSTANT: - parsedText += `${token.value}` - break; - case JS.Parsing.TokenType.FUNCTION: - parsedText += `${JS.Utils.escapeHTML(token.value)}` - break; - case JS.Parsing.TokenType.OPERATOR: - parsedText += `${JS.Utils.escapeHTML(token.value)}` - break; - case JS.Parsing.TokenType.NUMBER: - parsedText += `${JS.Utils.escapeHTML(token.value)}` - break; - case JS.Parsing.TokenType.STRING: - parsedText += `${token.limitator}${JS.Utils.escapeHTML(token.value)}${token.limitator}` - break; - case JS.Parsing.TokenType.WHITESPACE: - case JS.Parsing.TokenType.PUNCT: - default: - parsedText += JS.Utils.escapeHTML(token.value).replace(/ /g, ' ') - break; - } - } - return parsedText - } -} - diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/qmldir b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/qmldir deleted file mode 100644 index c80cae5..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/qmldir +++ /dev/null @@ -1,5 +0,0 @@ -module eu.ad5001.LogarithmPlotter - -AppMenuBar 1.0 AppMenuBar.qml -LogGraphCanvas 1.0 LogGraphCanvas.qml -Settings 1.0 Settings.qml diff --git a/runtime-pyside6/LogarithmPlotter/util/debug.py b/runtime-pyside6/LogarithmPlotter/util/debug.py deleted file mode 100644 index c977253..0000000 --- a/runtime-pyside6/LogarithmPlotter/util/debug.py +++ /dev/null @@ -1,105 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -from PySide6.QtCore import QtMsgType, qInstallMessageHandler, QMessageLogContext -from math import ceil, log10 -from os import path -from re import compile - -CURRENT_PATH = path.dirname(path.realpath(__file__)) -SOURCEMAP_PATH = path.realpath(f"{CURRENT_PATH}/../qml/eu/ad5001/LogarithmPlotter/Common/index.mjs.map") -SOURCEMAP_INDEX = None -INDEX_REG = compile(r"build\/runtime-pyside6\/LogarithmPlotter\/qml\/eu\/ad5001\/LogarithmPlotter\/Common\/index.mjs:(\d+)") - - -class LOG_COLORS: - GRAY = "\033[90m" - BLUE = "\033[94m" - ORANGE = "\033[38;5;166m" - RED = "\033[38;5;204m" - INVERT = "\033[7m" - RESET_INVERT = "\033[27m" - RESET = "\033[0m" - - -MODES = { - QtMsgType.QtInfoMsg: ['info', LOG_COLORS.BLUE], - QtMsgType.QtWarningMsg: ['warning', LOG_COLORS.ORANGE], - QtMsgType.QtCriticalMsg: ['critical', LOG_COLORS.RED], - QtMsgType.QtFatalMsg: ['critical', LOG_COLORS.RED] -} - -DEFAULT_MODE = ['debug', LOG_COLORS.GRAY] - - -def map_javascript_source(source_file: str, line: str) -> tuple[str, str]: - """ - Maps a line from the compiled javascript to its source. - """ - try: - if SOURCEMAP_INDEX is not None: - token = SOURCEMAP_INDEX.lookup(line, 20) - source_file = token.src.split("../")[-1] - line = token.src_line - except IndexError: - pass # Unable to find source, leave as is. - return source_file, line - - -def create_log_terminal_message(mode: QtMsgType, context: QMessageLogContext, message: str): - """ - Parses a qt log message and returns it. - """ - mode = MODES[mode] if mode in MODES else DEFAULT_MODE - line = context.line - source_file = context.file - # Remove source and line from message - if source_file is not None: - if message.startswith(source_file): - message = message[len(source_file) + 1:] - source_file = "LogarithmPlotter/qml/" + source_file.split("/qml/")[-1] # We're only interested in that part. - if line is not None and message.startswith(str(line)): - line_length = ceil(log10((line + 1) if line > 0 else 1)) - message = message[line_length + 2:] - # Check MJS - if line is not None and source_file is not None and source_file.endswith("index.mjs"): - source_file, line = map_javascript_source(source_file, line) - # Parse message - prefix = f"{LOG_COLORS.INVERT}{mode[1]}[{mode[0].upper()}]{LOG_COLORS.RESET_INVERT}" - message = message + LOG_COLORS.RESET - context = f"{context.function} at {source_file}:{line}" - return f"{prefix} {message} ({context})" - - -def log_qt_debug(mode: QtMsgType, context: QMessageLogContext, message: str): - """ - Parses and renders qt log messages. - """ - print(create_log_terminal_message(mode, context, message)) - - -def setup(): - global SOURCEMAP_INDEX - try: - with open(SOURCEMAP_PATH, "r") as f: - from sourcemap import loads - SOURCEMAP_INDEX = loads(f.read()) - except Exception as e: - log_qt_debug(QtMsgType.QtWarningMsg, QMessageLogContext(), - f"Could not setup JavaScript source mapper in logs: {repr(e)}") - qInstallMessageHandler(log_qt_debug) diff --git a/runtime-pyside6/LogarithmPlotter/util/helper.py b/runtime-pyside6/LogarithmPlotter/util/helper.py deleted file mode 100644 index 8adb81e..0000000 --- a/runtime-pyside6/LogarithmPlotter/util/helper.py +++ /dev/null @@ -1,164 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" -from PySide6.QtWidgets import QMessageBox, QApplication -from PySide6.QtCore import QRunnable, QThreadPool, QThread, QObject, Signal, Slot, QCoreApplication, __version__ as PySide6_version -from PySide6.QtQml import QJSValue -from PySide6.QtGui import QImage - -from os import chdir, path -from json import loads -from sys import version as sys_version, argv -from urllib.request import urlopen -from urllib.error import HTTPError, URLError - -from LogarithmPlotter import __VERSION__ -from LogarithmPlotter.util import config -from LogarithmPlotter.util.promise import PyPromise - -SHOW_GUI_MESSAGES = "--test-build" not in argv -CHANGELOG_VERSION = __VERSION__ -CHANGELOG_CACHE_PATH = path.join(path.dirname(path.realpath(__file__)), "CHANGELOG.md") - - -class InvalidFileException(Exception): pass - - -def show_message(msg: str) -> None: - """ - Shows a GUI message if GUI messages are enabled - """ - if SHOW_GUI_MESSAGES: - QMessageBox.warning(None, "LogarithmPlotter", msg) - else: - raise InvalidFileException(msg) - - -def fetch_changelog(): - msg_text = "Unknown changelog error." - try: - # Fetching version - r = urlopen("https://api.ad5001.eu/changelog/logarithmplotter/?version=" + CHANGELOG_VERSION) - lines = r.readlines() - r.close() - msg_text = "".join(map(lambda x: x.decode('utf-8'), lines)).strip() - except HTTPError as e: - msg_text = QCoreApplication.translate("changelog", "Could not fetch changelog: Server error {}.").format( - str(e.code)) - except URLError as e: - msg_text = QCoreApplication.translate("changelog", "Could not fetch update: {}.").format(str(e.reason)) - return msg_text - - -def read_changelog(): - f = open(CHANGELOG_CACHE_PATH, 'r', -1) - data = f.read().strip() - f.close() - return data - - -class Helper(QObject): - def __init__(self, cwd: str, tmpfile: str): - QObject.__init__(self) - self.cwd = cwd - self.tmpfile = tmpfile - - @Slot(str, str) - def write(self, filename, filedata): - chdir(self.cwd) - if path.exists(path.dirname(path.realpath(filename))): - if filename.split(".")[-1] == "lpf": - # Add header to file - filedata = "LPFv1" + filedata - f = open(path.realpath(filename), 'w', -1, 'utf8') - f.write(filedata) - f.close() - chdir(path.dirname(path.realpath(__file__))) - - @Slot(str, result=str) - def load(self, filename): - chdir(self.cwd) - data = '{}' - if path.exists(path.realpath(filename)): - f = open(path.realpath(filename), 'r', -1, 'utf8') - data = f.read() - f.close() - try: - if data[:5] == "LPFv1": - # V1 version of the file - data = data[5:] - elif data[:3] == "LPF": - # More recent version of LogarithmPlotter file, but incompatible with the current format - msg = QCoreApplication.translate('main', - "This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}.\nPlease update LogarithmPlotter to open this file.") - raise InvalidFileException(msg.format(__VERSION__)) - else: - raise InvalidFileException("Invalid LogarithmPlotter file.") - except InvalidFileException as e: # If file can't be loaded - msg = QCoreApplication.translate('main', 'Could not open file "{}":\n{}') - show_message(msg.format(filename, e)) # Cannot parse file - else: - msg = QCoreApplication.translate('main', 'Could not open file: "{}"\nFile does not exist.') - show_message(msg.format(filename)) # Cannot parse file - try: - chdir(path.dirname(path.realpath(__file__))) - except NotADirectoryError as e: - # Triggered on bundled versions of MacOS when it shouldn't. Prevents opening files. - # See more at https://git.ad5001.eu/Ad5001/LogarithmPlotter/issues/1 - pass - return data - - @Slot(result=str) - def gettmpfile(self): - return self.tmpfile - - @Slot() - def copyImageToClipboard(self): - clipboard = QApplication.clipboard() - clipboard.setImage(QImage(self.tmpfile)) - - @Slot(result=str) - def getVersion(self): - return __VERSION__ - - @Slot(str, result=QJSValue) - def getSetting(self, namespace: str) -> QJSValue: - return QJSValue(config.getSetting(namespace)) - - @Slot(str, QJSValue) - def setSetting(self, namespace: str, value: QJSValue): - return config.setSetting(namespace, value.toPrimitive().toVariant()) - - @Slot(result=str) - def getDebugInfos(self): - """ - Returns the version info about Qt, PySide6 & Python - """ - msg = QCoreApplication.translate('main', "Built with PySide6 (Qt) v{} and python v{}") - return msg.format(PySide6_version, sys_version.split("\n")[0]) - - @Slot(result=PyPromise) - def fetchChangelog(self): - """ - Fetches the changelog and returns a Promise. - """ - if path.exists(CHANGELOG_CACHE_PATH): - # We have a cached version of the changelog, for env that don't have access to the internet. - return PyPromise(read_changelog) - else: - # Fetch it from the internet. - return PyPromise(fetch_changelog) diff --git a/runtime-pyside6/LogarithmPlotter/util/js.py b/runtime-pyside6/LogarithmPlotter/util/js.py deleted file mode 100644 index 380ab97..0000000 --- a/runtime-pyside6/LogarithmPlotter/util/js.py +++ /dev/null @@ -1,110 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" -from re import Pattern -from typing import Callable -from PySide6.QtCore import QMetaObject, QObject, QDateTime -from PySide6.QtQml import QJSValue - -class InvalidAttributeValueException(Exception): pass -class NotAPrimitiveException(Exception): pass - -class URL: pass - -class PyJSValue: - """ - Wrapper to provide easy way to interact with JavaScript values in Python directly. - """ - - def __init__(self, js_value: QJSValue, parent: QJSValue = None): - self.qjs_value = js_value - self._parent = parent - - def __getattr__(self, item): - return PyJSValue(self.qjs_value.property(item), self.qjs_value) - - def __setattr__(self, key, value): - if key in ['qjs_value', '_parent']: - # Fallback - object.__setattr__(self, key, value) - elif isinstance(value, PyJSValue): - # Set property - self.qjs_value.setProperty(key, value.qjs_value) - elif isinstance(value, QJSValue): - self.qjs_value.setProperty(key, value) - elif type(value) in (int, float, str, bool): - self.qjs_value.setProperty(key, QJSValue(value)) - else: - raise InvalidAttributeValueException(f"Invalid value {value} of type {type(value)} being set to {key}.") - - def __eq__(self, other): - if isinstance(other, PyJSValue): - return self.qjs_value.strictlyEquals(other.qjs_value) - elif isinstance(other, QJSValue): - return self.qjs_value.strictlyEquals(other) - elif type(other) in (int, float, str, bool): - return self.qjs_value.strictlyEquals(QJSValue(other)) - else: - return False - - def __call__(self, *args, **kwargs): - value = None - if self.qjs_value.isCallable(): - if self._parent is None: - value = self.qjs_value.call(args) - else: - value = self.qjs_value.callWithInstance(self._parent, args) - else: - raise InvalidAttributeValueException('Cannot call non-function JS value.') - if isinstance(value, QJSValue): - value = PyJSValue(value) - return value - - def type(self) -> any: - ret = None - matcher = [ - (lambda: self.qjs_value.isArray(), list), - (lambda: self.qjs_value.isBool(), bool), - (lambda: self.qjs_value.isCallable(), Callable), - (lambda: self.qjs_value.isDate(), QDateTime), - (lambda: self.qjs_value.isError(), Exception), - (lambda: self.qjs_value.isNull(), None), - (lambda: self.qjs_value.isNumber(), float), - (lambda: self.qjs_value.isQMetaObject(), QMetaObject), - (lambda: self.qjs_value.isQObject(), QObject), - (lambda: self.qjs_value.isRegExp(), Pattern), - (lambda: self.qjs_value.isUndefined(), None), - (lambda: self.qjs_value.isUrl(), URL), - (lambda: self.qjs_value.isString(), str), - (lambda: self.qjs_value.isObject(), object), - ] - for (test, value) in matcher: - if test(): - ret = value - break - return ret - - def primitive(self): - """ - Returns the pythonic value of the given primitive data. - Raises a NotAPrimitiveException() if this JS value is not a primitive. - """ - if self.type() not in [bool, float, str, None]: - raise NotAPrimitiveException() - return self.qjs_value.toPrimitive().toVariant() - - diff --git a/runtime-pyside6/LogarithmPlotter/util/latex.py b/runtime-pyside6/LogarithmPlotter/util/latex.py deleted file mode 100644 index 5b127dc..0000000 --- a/runtime-pyside6/LogarithmPlotter/util/latex.py +++ /dev/null @@ -1,298 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" -from time import sleep - -from PySide6.QtCore import QObject, Slot, Property, QCoreApplication, Signal -from PySide6.QtGui import QImage, QColor -from PySide6.QtWidgets import QMessageBox - -from os import path, remove, makedirs -from string import Template -from subprocess import Popen, TimeoutExpired, PIPE -from hashlib import sha512 -from shutil import which -from sys import argv - -from LogarithmPlotter.util import config -from LogarithmPlotter.util.promise import PyPromise - -""" -Searches for a valid Latex and DVIPNG (http://savannah.nongnu.org/projects/dvipng/) -installation and collects the binary path in the DVIPNG_PATH variable. -If not found, it will send an alert to the user. -""" -LATEX_PATH = which('latex') -DVIPNG_PATH = which('dvipng') -PACKAGES = ["calligra", "amsfonts", "inputenc"] -SHOW_GUI_MESSAGES = "--test-build" not in argv - -DEFAULT_LATEX_DOC = Template(r""" -\documentclass[]{minimal} -\usepackage[utf8]{inputenc} -\usepackage{calligra} -\usepackage{amsfonts} - -\title{} -\author{} - -\begin{document} - -$$$$ $markup $$$$ - -\end{document} -""") - - -def show_message(msg: str) -> None: - """ - Shows a GUI message if GUI messages are enabled - """ - if SHOW_GUI_MESSAGES: - QMessageBox.warning(None, "LogarithmPlotter - Latex", msg) - - -class MissingPackageException(Exception): pass - - -class RenderError(Exception): pass - - -class Latex(QObject): - """ - Base class to convert Latex equations into PNG images with custom font color and size. - It doesn't have any python dependency, but requires a working latex installation and - dvipng to be installed on the system. - """ - - def __init__(self, cache_path): - QObject.__init__(self) - self.tempdir = path.join(cache_path, "latex") - self.render_pipeline_locks = {} - makedirs(self.tempdir, exist_ok=True) - - @Property(bool) - def latexSupported(self) -> bool: - return LATEX_PATH is not None and DVIPNG_PATH is not None - - @Property(bool) - def supportsAsyncRender(self) -> bool: - return config.getSetting("enable_latex_threaded") - - @Slot(result=bool) - def checkLatexInstallation(self) -> bool: - """ - Checks if the current latex installation is valid. - """ - valid_install = True - if LATEX_PATH is None: - print("No Latex installation found.") - msg = QCoreApplication.translate("latex", - "No Latex installation found.\nIf you already have a latex distribution installed, make sure it's installed on your path.\nOtherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/.") - show_message(msg) - valid_install = False - elif DVIPNG_PATH is None: - print("DVIPNG not found.") - msg = QCoreApplication.translate("latex", - "DVIPNG was not found. Make sure you include it from your Latex distribution.") - show_message(msg) - valid_install = False - else: - try: - self.renderSync("", 14, QColor(0, 0, 0, 255)) - except MissingPackageException: - valid_install = False # Should have sent an error message if failed to render - return valid_install - - def lock(self, markup_hash, render_hash, promise): - """ - Locks the render pipeline for a given markup hash and render hash. - """ - # print("Locking", markup_hash, render_hash) - if markup_hash not in self.render_pipeline_locks: - self.render_pipeline_locks[markup_hash] = promise - self.render_pipeline_locks[render_hash] = promise - - - def release_lock(self, markup_hash, render_hash): - """ - Release locks on the markup and render hashes. - """ - # print("Releasing", markup_hash, render_hash) - if markup_hash in self.render_pipeline_locks: - del self.render_pipeline_locks[markup_hash] - del self.render_pipeline_locks[render_hash] - - @Slot(str, float, QColor, result=PyPromise) - def renderAsync(self, latex_markup: str, font_size: float, color: QColor) -> PyPromise: - """ - Prepares and renders a latex string into a png file asynchronously. - """ - markup_hash, render_hash, export_path = self.create_export_path(latex_markup, font_size, color) - promise = None - if render_hash in self.render_pipeline_locks: - # A PyPromise for this specific render is already running. - # print("Already running render of", latex_markup) - promise = self.render_pipeline_locks[render_hash] - elif markup_hash in self.render_pipeline_locks: - # A PyPromise with the same markup, but not the same color or font size is already running. - # print("Chaining render of", latex_markup) - existing_promise = self.render_pipeline_locks[markup_hash] - promise = self._create_async_promise(latex_markup, font_size, color) - existing_promise.then(promise.start) - else: - # No such PyPromise is running. - promise = self._create_async_promise(latex_markup, font_size, color) - promise.start() - return promise - - def _create_async_promise(self, latex_markup: str, font_size: float, color: QColor) -> PyPromise: - """ - Createsa PyPromise to render a latex string into a PNG file. - Internal method. Use renderAsync that makes use of locks. - """ - markup_hash, render_hash, export_path = self.create_export_path(latex_markup, font_size, color) - promise = PyPromise(self.renderSync, [latex_markup, font_size, color], start_automatically=False) - self.lock(markup_hash, render_hash, promise) - # Make the lock release at the end. - def unlock(data, markup_hash=markup_hash, render_hash=render_hash): - self.release_lock(markup_hash, render_hash) - promise.then(unlock, unlock) - return promise - - @Slot(str, float, QColor, result=str) - def renderSync(self, latex_markup: str, font_size: float, color: QColor) -> str: - """ - Prepares and renders a latex string into a png file. - """ - markup_hash, render_hash, export_path = self.create_export_path(latex_markup, font_size, color) - if self.latexSupported and not path.exists(export_path + ".png"): - # Generating file - latex_path = path.join(self.tempdir, str(markup_hash)) - # If the formula is just recolored or the font is just changed, no need to recreate the DVI. - if not path.exists(latex_path + ".dvi"): - self.create_latex_doc(latex_path, latex_markup) - self.convert_latex_to_dvi(latex_path) - self.cleanup(latex_path) - # Creating four pictures of different sizes to better handle dpi. - self.convert_dvi_to_png(latex_path, export_path, font_size, color) - # self.convert_dvi_to_png(latex_path, export_path+"@2", font_size*2, color) - # self.convert_dvi_to_png(latex_path, export_path+"@3", font_size*3, color) - # self.convert_dvi_to_png(latex_path, export_path+"@4", font_size*4, color) - img = QImage(export_path) - # Small hack, not very optimized since we load the image twice, but you can't pass a QImage to QML and expect it to be loaded - return f'file:///{export_path}.png,{img.width()},{img.height()}' - - @Slot(str, float, QColor, result=str) - def findPrerendered(self, latex_markup: str, font_size: float, color: QColor) -> str: - """ - Finds a prerendered image and returns its data if possible, and an empty string if not. - """ - markup_hash, render_hash, export_path = self.create_export_path(latex_markup, font_size, color) - data = "" - if path.exists(export_path + ".png"): - img = QImage(export_path) - data = f'file:///{export_path}.png,{img.width()},{img.height()}' - return data - - def create_export_path(self, latex_markup: str, font_size: float, color: QColor): - """ - Standardizes export path for renders. - Markup hash is unique for the markup - Render hash is unique for the markup, the font size and the color. - """ - markup_hash = "render" + str(sha512(latex_markup.encode()).hexdigest()) - render_hash = f'{markup_hash}_{int(font_size)}_{color.rgb()}' - export_path = path.join(self.tempdir, render_hash) - return markup_hash, render_hash, export_path - - def create_latex_doc(self, export_path: str, latex_markup: str): - """ - Creates a temporary latex document with base file_hash as file name and a given expression markup latex_markup. - """ - f = open(export_path + ".tex", 'w') - f.write(DEFAULT_LATEX_DOC.substitute(markup=latex_markup)) - f.close() - - def convert_latex_to_dvi(self, export_path: str): - """ - Converts a TEX file to a DVI file. - """ - self.run([ - LATEX_PATH, - export_path + ".tex" - ]) - - def convert_dvi_to_png(self, dvi_path: str, export_path: str, font_size: float, color: QColor): - """ - Converts a DVI file to a PNG file. - Documentation: https://linux.die.net/man/1/dvipng - """ - fg = color.convertTo(QColor.Rgb) - fg = f'rgb {fg.redF()} {fg.greenF()} {fg.blueF()}' - depth = int(font_size * 72.27 / 100) * 10 - self.run([ - DVIPNG_PATH, - '-T', 'tight', # Make sure image borders are as tight around the equation as possible to avoid blank space. - '--truecolor', # Make sure it's rendered in 24 bit colors. - '-D', f'{depth}', # Depth of the image - '-bg', 'Transparent', # Transparent background - '-fg', f'{fg}', # Foreground of the wanted color. - f'{dvi_path}.dvi', # Input file - '-o', f'{export_path}.png', # Output file - ]) - - def run(self, process: list): - """ - Runs a subprocess and handles exceptions and messages them to the user. - """ - cmd = " ".join(process) - proc = Popen(process, stdout=PIPE, stderr=PIPE, cwd=self.tempdir) - try: - out, err = proc.communicate(timeout=2) # 2 seconds is already FAR too long. - if proc.returncode != 0: - # Process errored - output = str(out, 'utf8') + "\n" + str(err, 'utf8') - msg = QCoreApplication.translate("latex", - "An exception occured within the creation of the latex formula.\nProcess '{}' ended with a non-zero return code {}:\n\n{}\nPlease make sure your latex installation is correct and report a bug if so.") - show_message(msg.format(cmd, proc.returncode, output)) - raise RenderError( - f"{cmd} process exited with return code {str(proc.returncode)}:\n{str(out, 'utf8')}\n{str(err, 'utf8')}") - except TimeoutExpired: - # Process timed out - proc.kill() - out, err = proc.communicate() - output = str(out, 'utf8') + "\n" + str(err, 'utf8') - if 'not found' in output: - for pkg in PACKAGES: - if f'{pkg}.sty' in output: - # Package missing. - msg = QCoreApplication.translate("latex", - "Your LaTeX installation does not include some required packages:\n\n- {} (https://ctan.org/pkg/{})\n\nMake sure said package is installed, or disable the LaTeX rendering in LogarithmPlotter.") - show_message(msg.format(pkg, pkg)) - raise MissingPackageException("Latex: Missing package " + pkg) - msg = QCoreApplication.translate("latex", - "An exception occured within the creation of the latex formula.\nProcess '{}' took too long to finish:\n{}\nPlease make sure your latex installation is correct and report a bug if so.") - show_message(msg.format(cmd, output)) - raise RenderError(f"{cmd} process timed out:\n{output}") - - def cleanup(self, export_path): - """ - Removes auxiliary, logs and Tex temporary files. - """ - for i in [".tex", ".aux", ".log"]: - remove(export_path + i) diff --git a/runtime-pyside6/LogarithmPlotter/util/promise.py b/runtime-pyside6/LogarithmPlotter/util/promise.py deleted file mode 100644 index 8e17651..0000000 --- a/runtime-pyside6/LogarithmPlotter/util/promise.py +++ /dev/null @@ -1,175 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" -from typing import Callable - -from PySide6.QtCore import QRunnable, Signal, Property, QObject, Slot, QThreadPool -from PySide6.QtQml import QJSValue - -from LogarithmPlotter.util.js import PyJSValue - -NO_RETURN = [None, QJSValue.SpecialValue.UndefinedValue] - - -def check_callable(function: Callable|QJSValue) -> Callable|None: - """ - Checks if the given function can be called (either a python callable - or a QJSValue function), and returns the object that can be called directly. - Returns None if not a function. - """ - if isinstance(function, QJSValue) and function.isCallable(): - return PyJSValue(function) - elif callable(function): - return function - return None - -class InvalidReturnValue(Exception): pass - - -class PyPromiseRunner(QRunnable): - """ - QRunnable for running Promises in different threads. - """ - def __init__(self, runner, promise, args): - QRunnable.__init__(self) - self.runner = runner - self.promise = promise - self.args = args - - def run(self): - try: - data = self.runner(*self.args) - if type(data) in [int, str, float, bool]: - data = QJSValue(data) - elif data is None: - data = QJSValue.SpecialValue.UndefinedValue - elif isinstance(data, QJSValue): - data = data - elif isinstance(data, PyJSValue): - data = data.qjs_value - else: - raise InvalidReturnValue("Must return either a primitive, a JS Value, or None.") - self.promise.fulfilled.emit(data) - except Exception as e: - try: - self.promise.rejected.emit(repr(e)) - except RuntimeError as e2: - # Happens when the PyPromise has already been garbage collected. - # In other words, nothing to report to nowhere. - pass - - -class PyPromise(QObject): - """ - Threaded A+/Promise implementation meant to interface between Python and Javascript easily. - Runs to_run in another thread, and calls fulfilled (populated by then) with its return value. - """ - fulfilled = Signal(QJSValue) - rejected = Signal(str) - - def __init__(self, to_run: Callable|QJSValue, args=[], start_automatically=True): - QObject.__init__(self) - self._fulfills = [] - self._rejects = [] - self._state = "pending" - self._started = False - self.fulfilled.connect(self._fulfill) - self.rejected.connect(self._reject) - to_run = check_callable(to_run) - if to_run is None: - raise ValueError("New PyPromise created with invalid function") - self._runner = PyPromiseRunner(to_run, self, args) - if start_automatically: - self.start() - - @Slot() - def start(self, *args, **kwargs): - """ - Starts the thread that will run the promise. - """ - if not self._started: # Avoid getting started twice. - print("Starting", self._runner.args) - QThreadPool.globalInstance().start(self._runner) - self._started = True - - @Property(str) - def state(self): - return self._state - - @Slot(QJSValue, result=QObject) - @Slot(QJSValue, QJSValue, result=QObject) - def then(self, on_fulfill: QJSValue | Callable, on_reject: QJSValue | Callable = None): - """ - Adds listeners for both fulfilment and catching errors of the Promise. - """ - on_fulfill = check_callable(on_fulfill) - on_reject = check_callable(on_reject) - self._fulfills.append(on_fulfill) - self._rejects.append(on_reject) - return self - - def calls_upon_fulfillment(self, function: Callable | QJSValue) -> bool: - """ - Returns True if the given function will be callback upon the promise fulfillment. - False otherwise. - """ - return self._calls_in(function, self._fulfills) - - def calls_upon_rejection(self, function: Callable | QJSValue) -> bool: - """ - Returns True if the given function will be callback upon the promise rejection. - False otherwise. - """ - return self._calls_in(function, self._rejects) - - def _calls_in(self, function: Callable | QJSValue, within: list) -> bool: - """ - Returns True if the given function resides in the given within list, False otherwise. - Internal method of calls_upon_fulfill - """ - function = check_callable(function) - ret = False - if isinstance(function, PyJSValue): - found = next((f for f in within if f.qjs_value == function.qjs_value), None) - ret = found is not None - elif callable(function): - found = next((f for f in within if f == function), None) - ret = found is not None - return ret - - @Slot(QJSValue) - @Slot(QObject) - def _fulfill(self, data): - self._state = "fulfilled" - print("Finished", self._runner.args) - for i in range(len(self._fulfills)): - try: - result = self._fulfills[i](data) - result = result.qjs_value if isinstance(result, PyJSValue) else result - data = result if result not in NO_RETURN else data # Forward data. - except Exception as e: - self._reject(repr(e), start_at=i) - break - - @Slot(QJSValue) - @Slot(str) - def _reject(self, error, start_at=0): - self._state = "rejected" - for i in range(start_at, len(self._rejects)): - result = self._rejects[i](error) - result = result.qjs_value if isinstance(result, PyJSValue) else result - error = result if result not in NO_RETURN else error # Forward data. diff --git a/runtime-pyside6/poetry.lock b/runtime-pyside6/poetry.lock deleted file mode 100644 index 9a66e3e..0000000 --- a/runtime-pyside6/poetry.lock +++ /dev/null @@ -1,476 +0,0 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. - -[[package]] -name = "altgraph" -version = "0.17.4" -description = "Python graph (network) package" -optional = false -python-versions = "*" -files = [ - {file = "altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"}, - {file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "coverage" -version = "7.8.0" -description = "Code coverage measurement for Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "coverage-7.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2931f66991175369859b5fd58529cd4b73582461877ecfd859b6549869287ffe"}, - {file = "coverage-7.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52a523153c568d2c0ef8826f6cc23031dc86cffb8c6aeab92c4ff776e7951b28"}, - {file = "coverage-7.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c8a5c139aae4c35cbd7cadca1df02ea8cf28a911534fc1b0456acb0b14234f3"}, - {file = "coverage-7.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a26c0c795c3e0b63ec7da6efded5f0bc856d7c0b24b2ac84b4d1d7bc578d676"}, - {file = "coverage-7.8.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:821f7bcbaa84318287115d54becb1915eece6918136c6f91045bb84e2f88739d"}, - {file = "coverage-7.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a321c61477ff8ee705b8a5fed370b5710c56b3a52d17b983d9215861e37b642a"}, - {file = "coverage-7.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ed2144b8a78f9d94d9515963ed273d620e07846acd5d4b0a642d4849e8d91a0c"}, - {file = "coverage-7.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:042e7841a26498fff7a37d6fda770d17519982f5b7d8bf5278d140b67b61095f"}, - {file = "coverage-7.8.0-cp310-cp310-win32.whl", hash = "sha256:f9983d01d7705b2d1f7a95e10bbe4091fabc03a46881a256c2787637b087003f"}, - {file = "coverage-7.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a570cd9bd20b85d1a0d7b009aaf6c110b52b5755c17be6962f8ccd65d1dbd23"}, - {file = "coverage-7.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27"}, - {file = "coverage-7.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea"}, - {file = "coverage-7.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7"}, - {file = "coverage-7.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040"}, - {file = "coverage-7.8.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543"}, - {file = "coverage-7.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2"}, - {file = "coverage-7.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318"}, - {file = "coverage-7.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9"}, - {file = "coverage-7.8.0-cp311-cp311-win32.whl", hash = "sha256:e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c"}, - {file = "coverage-7.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78"}, - {file = "coverage-7.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc"}, - {file = "coverage-7.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6"}, - {file = "coverage-7.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d"}, - {file = "coverage-7.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05"}, - {file = "coverage-7.8.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a"}, - {file = "coverage-7.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6"}, - {file = "coverage-7.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47"}, - {file = "coverage-7.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe"}, - {file = "coverage-7.8.0-cp312-cp312-win32.whl", hash = "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545"}, - {file = "coverage-7.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b"}, - {file = "coverage-7.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ac46d0c2dd5820ce93943a501ac5f6548ea81594777ca585bf002aa8854cacd"}, - {file = "coverage-7.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:771eb7587a0563ca5bb6f622b9ed7f9d07bd08900f7589b4febff05f469bea00"}, - {file = "coverage-7.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42421e04069fb2cbcbca5a696c4050b84a43b05392679d4068acbe65449b5c64"}, - {file = "coverage-7.8.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:554fec1199d93ab30adaa751db68acec2b41c5602ac944bb19187cb9a41a8067"}, - {file = "coverage-7.8.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008"}, - {file = "coverage-7.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:581a40c7b94921fffd6457ffe532259813fc68eb2bdda60fa8cc343414ce3733"}, - {file = "coverage-7.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f319bae0321bc838e205bf9e5bc28f0a3165f30c203b610f17ab5552cff90323"}, - {file = "coverage-7.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04bfec25a8ef1c5f41f5e7e5c842f6b615599ca8ba8391ec33a9290d9d2db3a3"}, - {file = "coverage-7.8.0-cp313-cp313-win32.whl", hash = "sha256:dd19608788b50eed889e13a5d71d832edc34fc9dfce606f66e8f9f917eef910d"}, - {file = "coverage-7.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:a9abbccd778d98e9c7e85038e35e91e67f5b520776781d9a1e2ee9d400869487"}, - {file = "coverage-7.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:18c5ae6d061ad5b3e7eef4363fb27a0576012a7447af48be6c75b88494c6cf25"}, - {file = "coverage-7.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:95aa6ae391a22bbbce1b77ddac846c98c5473de0372ba5c463480043a07bff42"}, - {file = "coverage-7.8.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e013b07ba1c748dacc2a80e69a46286ff145935f260eb8c72df7185bf048f502"}, - {file = "coverage-7.8.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d766a4f0e5aa1ba056ec3496243150698dc0481902e2b8559314368717be82b1"}, - {file = "coverage-7.8.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad80e6b4a0c3cb6f10f29ae4c60e991f424e6b14219d46f1e7d442b938ee68a4"}, - {file = "coverage-7.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b87eb6fc9e1bb8f98892a2458781348fa37e6925f35bb6ceb9d4afd54ba36c73"}, - {file = "coverage-7.8.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d1ba00ae33be84066cfbe7361d4e04dec78445b2b88bdb734d0d1cbab916025a"}, - {file = "coverage-7.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f3c38e4e5ccbdc9198aecc766cedbb134b2d89bf64533973678dfcf07effd883"}, - {file = "coverage-7.8.0-cp313-cp313t-win32.whl", hash = "sha256:379fe315e206b14e21db5240f89dc0774bdd3e25c3c58c2c733c99eca96f1ada"}, - {file = "coverage-7.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2e4b6b87bb0c846a9315e3ab4be2d52fac905100565f4b92f02c445c8799e257"}, - {file = "coverage-7.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa260de59dfb143af06dcf30c2be0b200bed2a73737a8a59248fcb9fa601ef0f"}, - {file = "coverage-7.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:96121edfa4c2dfdda409877ea8608dd01de816a4dc4a0523356067b305e4e17a"}, - {file = "coverage-7.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8af63b9afa1031c0ef05b217faa598f3069148eeee6bb24b79da9012423b82"}, - {file = "coverage-7.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89b1f4af0d4afe495cd4787a68e00f30f1d15939f550e869de90a86efa7e0814"}, - {file = "coverage-7.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ec0be97723ae72d63d3aa41961a0b9a6f5a53ff599813c324548d18e3b9e8c"}, - {file = "coverage-7.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8a1d96e780bdb2d0cbb297325711701f7c0b6f89199a57f2049e90064c29f6bd"}, - {file = "coverage-7.8.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f1d8a2a57b47142b10374902777e798784abf400a004b14f1b0b9eaf1e528ba4"}, - {file = "coverage-7.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cf60dd2696b457b710dd40bf17ad269d5f5457b96442f7f85722bdb16fa6c899"}, - {file = "coverage-7.8.0-cp39-cp39-win32.whl", hash = "sha256:be945402e03de47ba1872cd5236395e0f4ad635526185a930735f66710e1bd3f"}, - {file = "coverage-7.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:90e7fbc6216ecaffa5a880cdc9c77b7418c1dcb166166b78dbc630d07f278cc3"}, - {file = "coverage-7.8.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd"}, - {file = "coverage-7.8.0-py3-none-any.whl", hash = "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7"}, - {file = "coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501"}, -] - -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "exceptiongroup" -version = "1.2.2" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "importlib-metadata" -version = "8.6.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.9" -files = [ - {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, - {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, -] - -[package.dependencies] -zipp = ">=3.20" - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["pytest-mypy"] - -[[package]] -name = "iniconfig" -version = "2.1.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.8" -files = [ - {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, - {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, -] - -[[package]] -name = "macholib" -version = "1.16.3" -description = "Mach-O header analysis and editing" -optional = false -python-versions = "*" -files = [ - {file = "macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c"}, - {file = "macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30"}, -] - -[package.dependencies] -altgraph = ">=0.17" - -[[package]] -name = "packaging" -version = "25.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, -] - -[[package]] -name = "pefile" -version = "2023.2.7" -description = "Python PE parsing module" -optional = false -python-versions = ">=3.6.0" -files = [ - {file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"}, - {file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"}, -] - -[[package]] -name = "pluggy" -version = "1.5.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pyinstaller" -version = "6.13.0" -description = "PyInstaller bundles a Python application and all its dependencies into a single package." -optional = false -python-versions = "<3.14,>=3.8" -files = [ - {file = "pyinstaller-6.13.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:aa404f0b02cd57948098055e76ee190b8e65ccf7a2a3f048e5000f668317069f"}, - {file = "pyinstaller-6.13.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:92efcf2f09e78f07b568c5cb7ed48c9940f5dad627af4b49bede6320fab2a06e"}, - {file = "pyinstaller-6.13.0-py3-none-manylinux2014_i686.whl", hash = "sha256:9f82f113c463f012faa0e323d952ca30a6f922685d9636e754bd3a256c7ed200"}, - {file = "pyinstaller-6.13.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:db0e7945ebe276f604eb7c36e536479556ab32853412095e19172a5ec8fca1c5"}, - {file = "pyinstaller-6.13.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:92fe7337c5aa08d42b38d7a79614492cb571489f2cb0a8f91dc9ef9ccbe01ed3"}, - {file = "pyinstaller-6.13.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:bc09795f5954135dd4486c1535650958c8218acb954f43860e4b05fb515a21c0"}, - {file = "pyinstaller-6.13.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:589937548d34978c568cfdc39f31cf386f45202bc27fdb8facb989c79dfb4c02"}, - {file = "pyinstaller-6.13.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:b7260832f7501ba1d2ce1834d4cddc0f2b94315282bc89c59333433715015447"}, - {file = "pyinstaller-6.13.0-py3-none-win32.whl", hash = "sha256:80c568848529635aa7ca46d8d525f68486d53e03f68b7bb5eba2c88d742e302c"}, - {file = "pyinstaller-6.13.0-py3-none-win_amd64.whl", hash = "sha256:8d4296236b85aae570379488c2da833b28828b17c57c2cc21fccd7e3811fe372"}, - {file = "pyinstaller-6.13.0-py3-none-win_arm64.whl", hash = "sha256:d9f21d56ca2443aa6a1e255e7ad285c76453893a454105abe1b4d45e92bb9a20"}, - {file = "pyinstaller-6.13.0.tar.gz", hash = "sha256:38911feec2c5e215e5159a7e66fdb12400168bd116143b54a8a7a37f08733456"}, -] - -[package.dependencies] -altgraph = "*" -importlib_metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} -macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""} -packaging = ">=22.0" -pefile = {version = ">=2022.5.30,<2024.8.26 || >2024.8.26", markers = "sys_platform == \"win32\""} -pyinstaller-hooks-contrib = ">=2025.2" -pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""} -setuptools = ">=42.0.0" - -[package.extras] -completion = ["argcomplete"] -hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] - -[[package]] -name = "pyinstaller-hooks-contrib" -version = "2025.3" -description = "Community maintained hooks for PyInstaller" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyinstaller_hooks_contrib-2025.3-py3-none-any.whl", hash = "sha256:70cba46b1a6b82ae9104f074c25926e31f3dde50ff217434d1d660355b949683"}, - {file = "pyinstaller_hooks_contrib-2025.3.tar.gz", hash = "sha256:af129da5cd6219669fbda360e295cc822abac55b7647d03fec63a8fcf0a608cf"}, -] - -[package.dependencies] -importlib_metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} -packaging = ">=22.0" -setuptools = ">=42.0.0" - -[[package]] -name = "pyside6-addons" -version = "6.9.0" -description = "Python bindings for the Qt cross-platform application and UI framework (Addons)" -optional = false -python-versions = "<3.14,>=3.9" -files = [ - {file = "PySide6_Addons-6.9.0-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:98f9ad4b65820736e12d49c18db2e570eac63727407fbb59a62ac753e89dc201"}, - {file = "PySide6_Addons-6.9.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:fc9dcd63a0ce7565f238cb11c44494435a50eb6cb72b8dbce3b709618989c3dc"}, - {file = "PySide6_Addons-6.9.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:d8a650644e0b9d1e7a092f6bcd11f25a63706d12f77d442b6ace75d346ab5d30"}, - {file = "PySide6_Addons-6.9.0-cp39-abi3-win_amd64.whl", hash = "sha256:8cf54065b3d1b4698448fad825378a25c10ef52017d9dff48cead03200636d8d"}, - {file = "PySide6_Addons-6.9.0-cp39-abi3-win_arm64.whl", hash = "sha256:260a56da59539f476c1635a3ff13591e10f1b04d92155c0617129bc53ca8b5f8"}, -] - -[package.dependencies] -PySide6-Essentials = "6.9.0" -shiboken6 = "6.9.0" - -[[package]] -name = "pyside6-essentials" -version = "6.9.0" -description = "Python bindings for the Qt cross-platform application and UI framework (Essentials)" -optional = false -python-versions = "<3.14,>=3.9" -files = [ - {file = "PySide6_Essentials-6.9.0-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:b18e3e01b507e8a57481fe19792eb373d5f10a23a50702ce540da1435e722f39"}, - {file = "PySide6_Essentials-6.9.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:45eaf7f17688d1991f39680dbfd3c41674f3cbb78f278aa10fe0b5f2f31c1989"}, - {file = "PySide6_Essentials-6.9.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:69aedfad77119c5bec0005ca31d5620e9bac8ba5ae66c7389160530cfd698ed8"}, - {file = "PySide6_Essentials-6.9.0-cp39-abi3-win_amd64.whl", hash = "sha256:94a0096d6bb1d3e5cef29ca4a5366d0f229d42480fbb17aa25ad85d72b1b7947"}, - {file = "PySide6_Essentials-6.9.0-cp39-abi3-win_arm64.whl", hash = "sha256:d2dc45536f2269ad111991042e81257124f1cd1c9ed5ea778d7224fd65dc9e2b"}, -] - -[package.dependencies] -shiboken6 = "6.9.0" - -[[package]] -name = "pytest" -version = "8.3.5" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, - {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=1.5,<2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} - -[package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-cov" -version = "5.0.0" -description = "Pytest plugin for measuring coverage." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, - {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, -] - -[package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} -pytest = ">=4.6" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] - -[[package]] -name = "pytest-qt" -version = "4.4.0" -description = "pytest support for PyQt and PySide applications" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-qt-4.4.0.tar.gz", hash = "sha256:76896142a940a4285339008d6928a36d4be74afec7e634577e842c9cc5c56844"}, - {file = "pytest_qt-4.4.0-py3-none-any.whl", hash = "sha256:001ed2f8641764b394cf286dc8a4203e40eaf9fff75bf0bfe5103f7f8d0c591d"}, -] - -[package.dependencies] -pluggy = ">=1.1" -pytest = "*" - -[package.extras] -dev = ["pre-commit", "tox"] -doc = ["sphinx", "sphinx-rtd-theme"] - -[[package]] -name = "pywin32-ctypes" -version = "0.2.3" -description = "A (partial) reimplementation of pywin32 using ctypes/cffi" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, - {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, -] - -[[package]] -name = "setuptools" -version = "75.9.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.9" -files = [ - {file = "setuptools-75.9.1-py3-none-any.whl", hash = "sha256:0a6f876d62f4d978ca1a11ab4daf728d1357731f978543ff18ecdbf9fd071f73"}, - {file = "setuptools-75.9.1.tar.gz", hash = "sha256:b6eca2c3070cdc82f71b4cb4bb2946bc0760a210d11362278cf1ff394e6ea32c"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] -core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] - -[[package]] -name = "shiboken6" -version = "6.9.0" -description = "Python/C++ bindings helper module" -optional = false -python-versions = "<3.14,>=3.9" -files = [ - {file = "shiboken6-6.9.0-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:c4d8e3a5907154ac4789e52c77957db95bcf584238c244d7743cb39e9b66dd26"}, - {file = "shiboken6-6.9.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3f585caae5b814a7e23308db0a077355a7dc20c34d58ca4c339ff7625e9a1936"}, - {file = "shiboken6-6.9.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:b61579b90bf9c53ecc174085a69429166dfe57a0b8b894f933d1281af9df6568"}, - {file = "shiboken6-6.9.0-cp39-abi3-win_amd64.whl", hash = "sha256:121ea290ed1afa5ad6abf690b377612693436292b69c61b0f8e10b1f0850f935"}, - {file = "shiboken6-6.9.0-cp39-abi3-win_arm64.whl", hash = "sha256:24f53857458881b54798d7e35704611d07f6b6885bcdf80f13a4c8bb485b8df2"}, -] - -[[package]] -name = "sourcemap" -version = "0.2.1" -description = "Parse JavaScript source maps." -optional = false -python-versions = "*" -files = [ - {file = "sourcemap-0.2.1-py2.py3-none-any.whl", hash = "sha256:c448a8c48f9482e522e4582106b0c641a83b5dbc7f13927b178848e3ea20967b"}, - {file = "sourcemap-0.2.1.tar.gz", hash = "sha256:be00a90185e7a16b87bbe62a68ffd5e38bc438ef4700806d9b90e44d8027787c"}, -] - -[[package]] -name = "stdeb" -version = "0.10.0" -description = "Python to Debian source package conversion utility" -optional = false -python-versions = "*" -files = [ - {file = "stdeb-0.10.0.tar.gz", hash = "sha256:08c22c9c03b28a140fe3ec5064b53a5288279f22e596ca06b0be698d50c93cf2"}, -] - -[[package]] -name = "tomli" -version = "2.2.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, -] - -[[package]] -name = "zipp" -version = "3.21.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.9" -files = [ - {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, - {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] -type = ["pytest-mypy"] - -[metadata] -lock-version = "2.0" -python-versions = ">=3.9,<3.13" -content-hash = "b0920ba14495c199b201802e11473e624a133c5b7b97d2e96ba5744b8bc0e4c4" diff --git a/runtime-pyside6/pyproject.toml b/runtime-pyside6/pyproject.toml deleted file mode 100644 index ef68cb8..0000000 --- a/runtime-pyside6/pyproject.toml +++ /dev/null @@ -1,27 +0,0 @@ -[tool.poetry] -name = "LogarithmPlotter" -version = "0.6.0" -description = "Create and edit Bode plots" -authors = ["Ad5001 "] -license = "GPL-3.0-or-later" -readme = "README.md" -package-mode = false - -[tool.poetry.dependencies] -python = ">=3.9,<3.13" -PySide6-Essentials = "^6.9" -PySide6-Addons = "^6.9" - -[tool.poetry.group.packaging.dependencies] -pyinstaller = "^6.10.0" -stdeb = "^0.10.0" -setuptools = "^75.1.0" - -[tool.poetry.group.test.dependencies] -pytest = "^8.3.3" -pytest-cov = "^5.0.0" -pytest-qt = "^4.4.0" - -[tool.poetry.group.dev.dependencies] -sourcemap = "^0.2.1" - diff --git a/runtime-pyside6/setup.py b/runtime-pyside6/setup.py deleted file mode 100644 index 4100042..0000000 --- a/runtime-pyside6/setup.py +++ /dev/null @@ -1,156 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -import setuptools -import os -import sys -from shutil import copyfile - -print(sys.argv) - -current_dir = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) - -# Check where to install by default -# if "PREFIX" not in os.environ and sys.platform == 'linux': -# from getopt import getopt -# optlist, args = getopt(sys.argv, '', ['prefix=', 'root=']) -# for arg,value in optlist: -# if arg == "prefix" or arg == "root": -# os.environ["PREFIX"] = value -# if "PREFIX" not in os.environ and sys.platform == 'linux': -# if "XDG_DATA_HOME" in os.environ: -# os.environ["PREFIX"] = os.environ["XDG_DATA_HOME"] -# else: -# try: -# # Checking if we have permission to write to root. -# from os import makedirs, rmdir -# makedirs("/usr/share/applications/test") -# rmdir("/usr/share/applications/test") -# os.environ["PREFIX"] = "/usr/share" -# except: -# if ".pybuild" in os.environ["HOME"]: # Launchpad building. -# os.environ["PREFIX"] = "share" -# else: -# os.environ["PREFIX"] = os.environ["HOME"] + "/.local/share" - -from LogarithmPlotter import __VERSION__ as pkg_version - -if "--remove-git-version" in sys.argv: - pkg_version = pkg_version.split(".dev0")[0] - sys.argv.remove("--remove-git-version") - -CLASSIFIERS = """ -Environment :: Graphic -Environment :: X11 Applications :: Qt -License :: OSI Approved :: GNU General Public License v3 (GPLv3) -Natural Language :: English -Development Status :: 3 - Alpha -Operating System :: MacOS :: MacOS X -Operating System :: Microsoft :: Windows -Operating System :: POSIX -Operating System :: POSIX :: BSD -Operating System :: POSIX :: Linux -Programming Language :: Python :: 3.8 -Programming Language :: Python :: 3.9 -Programming Language :: Python :: 3.10 -Programming Language :: Python :: 3.11 -Programming Language :: Python :: 3.12 -Programming Language :: Python :: Implementation :: CPython -Topic :: Utilities -Topic :: Scientific/Engineering -""".strip().splitlines() - -def read_file(file_name): - f = open(file_name, 'r', -1) - data = f.read() - f.close() - return data - -def package_data(): - pkg_data = ["logarithmplotter.svg"] - for d,folders,files in os.walk("LogarithmPlotter/qml"): - d = d[17:] - pkg_data += [os.path.join(d, f) for f in files] - for d,folders,files in os.walk("LogarithmPlotter/i18n"): - d = d[17:] - pkg_data += [os.path.join(d, f) for f in files] - if "FLATPAK_INSTALL" in os.environ: - pkg_data += ["CHANGELOG.md"] - - return pkg_data - -data_files = [] -if sys.platform == 'linux': - data_files.append(('share/applications/', ['assets/native/linux/logarithmplotter.desktop'])) - data_files.append(('share/mime/packages/', ['assets/native/linux/x-logarithm-plot.xml'])) - data_files.append(('share/icons/hicolor/scalable/mimetypes/', ['assets/native/linux/application-x-logarithm-plot.svg'])) - data_files.append(('share/icons/hicolor/scalable/apps/', ['assets/logarithmplotter.svg'])) - # data_files.append((os.environ["PREFIX"] + '/applications/', ['linux/logarithmplotter.desktop'])) - # data_files.append((os.environ["PREFIX"] + '/mime/packages/', ['linux/x-logarithm-plot.xml'])) - # data_files.append((os.environ["PREFIX"] + '/icons/hicolor/scalable/mimetypes/', ['linux/application-x-logarithm-plot.svg'])) - # data_files.append((os.environ["PREFIX"] + '/icons/hicolor/scalable/apps/', ['logplotter.svg'])) - # if len(sys.argv) > 1: - # if sys.argv[1] == "install": - # os.makedirs(os.environ["PREFIX"] + '/applications/', exist_ok=True) - # os.makedirs(os.environ["PREFIX"] + '/mime/packages/', exist_ok=True) - # os.makedirs(os.environ["PREFIX"] + '/icons/hicolor/scalable/mimetypes/', exist_ok=True) - # os.makedirs(os.environ["PREFIX"] + '/icons/hicolor/scalable/apps/', exist_ok=True) - # os.makedirs(os.environ["PREFIX"] + '/metainfo/', exist_ok=True) - # copyfile(current_dir + '/linux/x-logarithm-plot.xml', os.environ["PREFIX"] + '/mime/packages/x-logarithm-plot.xml') - # copyfile(current_dir + '/linux/application-x-logarithm-plot.svg', - # os.environ["PREFIX"] + '/icons/hicolor/scalable/mimetypes/application-x-logarithm-plot.svg') - # copyfile(current_dir + '/logplotter.svg', os.environ["PREFIX"] + '/icons/hicolor/scalable/apps/logplotter.svg') - # elif sys.argv[1] == "uninstall": - # os.remove(os.environ["PREFIX"] + '/applications/logarithmplotter.desktop') - # os.remove(os.environ["PREFIX"] + '/mime/packages/x-logarithm-plot.xml') - # os.remove(os.environ["PREFIX"] + '/icons/hicolor/scalable/mimetypes/application-x-logarithm-plot.svg') - # os.remove(os.environ["PREFIX"] + '/icons/hicolor/scalable/apps/logplotter.svg') - -setuptools.setup( - install_requires=([] if "FLATPAK_INSTALL" in os.environ else ["PySide6-Essentials"]), - python_requires='>=3.9', - - name='logarithmplotter', - version=pkg_version, - - description='Create and edit Bode plots.', - long_description=read_file("README.md"), - keywords='logarithm plotter graph creator bode diagram', - - author='Ad5001', - author_email='mail@ad5001.eu', - - license=('GPLv3'), - url='https://apps.ad5001.eu/logarithmplotter/', - - classifiers=CLASSIFIERS, - zip_safe=False, - packages=["LogarithmPlotter", "LogarithmPlotter.util"], - - package_data={ - 'LogarithmPlotter':package_data(), - }, - include_package_data=True, - data_files = data_files, - entry_points={ - 'console_scripts': [ - 'logarithmplotter = LogarithmPlotter.logarithmplotter:run', - ], - } -) - diff --git a/runtime-pyside6/tests/__init__.py b/runtime-pyside6/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/runtime-pyside6/tests/plugins/__init__.py b/runtime-pyside6/tests/plugins/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/runtime-pyside6/tests/plugins/natural/__init__.py b/runtime-pyside6/tests/plugins/natural/__init__.py deleted file mode 100644 index 180969f..0000000 --- a/runtime-pyside6/tests/plugins/natural/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -from .spy import Spy -from .that import that -from .interfaces.base import Assertion - diff --git a/runtime-pyside6/tests/plugins/natural/interfaces/__init__.py b/runtime-pyside6/tests/plugins/natural/interfaces/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/runtime-pyside6/tests/plugins/natural/interfaces/assertion.py b/runtime-pyside6/tests/plugins/natural/interfaces/assertion.py deleted file mode 100644 index b81d0e5..0000000 --- a/runtime-pyside6/tests/plugins/natural/interfaces/assertion.py +++ /dev/null @@ -1,39 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -class Assertion(Exception): - def __init__(self, assertion: bool, message: str, invert: bool): - self.assertion = assertion - self.message = message - self.invert = invert - - def _invert_message(self): - for verb in ('is', 'was', 'has', 'have'): - for negative in ("n't", ' not', ' never', ' no'): - self.message = self.message.replace(f"{verb}{negative}", verb.upper()) - - def __str__(self): - return self.message - - def __bool__(self): - if not self.invert and not self.assertion: - raise self - if self.invert and self.assertion: - self._invert_message() - raise self - return True # Raises otherwise. \ No newline at end of file diff --git a/runtime-pyside6/tests/plugins/natural/interfaces/base.py b/runtime-pyside6/tests/plugins/natural/interfaces/base.py deleted file mode 100644 index c96c146..0000000 --- a/runtime-pyside6/tests/plugins/natural/interfaces/base.py +++ /dev/null @@ -1,171 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" -from typing import Self, Callable, Any - -from .assertion import Assertion -from .utils import repr_ - - -class AssertionInterface: - """ - Most basic assertion interface. - You probably want to use BaseAssertionInterface - """ - - def __init__(self, value, parent: Self = None): - self._value = value - self._parent = parent - if parent is None: - self.__not = False - - @property - def _not(self) -> bool: - """ - Internal state of whether the expression was negated. - Use "not_" to set it. - :return: - """ - return self.__not if self._parent is None else self._parent._not - - @_not.setter - def _not(self, value: bool): - if self._not is True: - raise RuntimeError("Cannot call is_not or was_not twice in the same statement.") - if self._parent is None: - self.__not = True - else: - self._parent._not = True - - def instance_of(self, type_: type) -> Assertion: - """ - Checks if the current value is equal to the provided value - """ - value_type_name = type(self._value).__name__ - if not isinstance(type_, type): - raise RuntimeError("Provided 'type' provided is not a class.") - return Assertion( - isinstance(self._value, type_), - f"The value ({value_type_name} {repr_(self._value)}) is not a {type_.__name__}.", - self._not - ) - - def __call__(self, condition: Callable[[Any], bool]) -> Assertion: - """ - Apply condition to value that returns whether or not the value is valid. - """ - return Assertion( - condition(self._value), - f"The value ({repr_(self._value)}) did not match given conditions.", - self._not - ) - - """ - NOT Properties. - """ - - @property - def NOT(self) -> Self: - self._not = True - return self - - @property - def not_(self) -> Self: - self._not = True - return self - - @property - def never(self) -> Self: - self._not = True - return self - - """ - Chain self properties to sound natural - """ - - @property - def that(self) -> Self: - return self - - @property - def is_(self) -> Self: - return self - - @property - def does(self) -> Self: - return self - - @property - def was(self) -> Self: - return self - - @property - def been(self) -> Self: - return self - - @property - def have(self) -> Self: - return self - - @property - def has(self) -> Self: - return self - - @property - def a(self) -> Self: - return self - - @property - def an(self) -> Self: - return self - - -class EqualAssertionInterface(AssertionInterface): - """ - Interface created for when its value should be checked for equality - """ - - def __init__(self, value, parent: AssertionInterface = None): - super().__init__(value, parent) - - def __call__(self, value) -> Assertion: - return Assertion( - value == self._value, - f"The value {repr_(self._value)} is different from {repr(value)}.", - self._not - ) - - @property - def to(self) -> Self: - return self - - -class BaseAssertionInterface(AssertionInterface): - - @property - def equals(self) -> EqualAssertionInterface: - """ - Checks if the current value is equal to the provided value - """ - return EqualAssertionInterface(self._value, self) - - @property - def equal(self) -> EqualAssertionInterface: - """ - Checks if the current value is equal to the provided value - """ - return self.equals diff --git a/runtime-pyside6/tests/plugins/natural/interfaces/basic.py b/runtime-pyside6/tests/plugins/natural/interfaces/basic.py deleted file mode 100644 index 85720da..0000000 --- a/runtime-pyside6/tests/plugins/natural/interfaces/basic.py +++ /dev/null @@ -1,83 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" -from .assertion import Assertion -from .base import BaseAssertionInterface -from .int import NumberInterface -from .utils import repr_ - - -class FixedIteratorInterface(BaseAssertionInterface): - @property - def length(self) -> NumberInterface: - return NumberInterface(len(self._value), self) - - def elements(self, *elements) -> Assertion: - tests = [repr_(elem) for elem in elements if elem not in self._value] - return Assertion( - len(tests) == 0, - f"This value ({repr_(self._value)}) does not have elements {', '.join(tests)}.", - self._not - ) - - def element(self, element) -> Assertion: - return Assertion( - element in self._value, - f"This value ({repr_(self._value)}) does not have element {repr_(element)}.", - self._not - ) - - def contains(self, *elements) -> Assertion: - """ - Check if the element(s) are contained in the iterator. - """ - if len(elements) == 1: - return self.element(elements[0]) - else: - return self.elements(*elements) - - def contain(self, *elements): - """ - Check if the element(s) are contained in the iterator. - """ - return self.contains(*elements) - - -class BoolInterface(BaseAssertionInterface): - @property - def true(self): - return Assertion( - self._value == True, - f"The value ({repr_(self._value)}) is not True.", - self._not - ) - - @property - def false(self): - return Assertion( - self._value == False, - f"The value ({repr_(self._value)}) is not False.", - self._not - ) - - -class StringInterface(FixedIteratorInterface): - pass - - -class ListInterface(FixedIteratorInterface): - pass diff --git a/runtime-pyside6/tests/plugins/natural/interfaces/int.py b/runtime-pyside6/tests/plugins/natural/interfaces/int.py deleted file mode 100644 index b282e4d..0000000 --- a/runtime-pyside6/tests/plugins/natural/interfaces/int.py +++ /dev/null @@ -1,320 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" -from math import log10, floor -from typing import Self - -from .assertion import Assertion -from .base import AssertionInterface -from .utils import repr_ - - -class NumberComparisonAssertionInterface(AssertionInterface): - def __init__(self, value, parent: AssertionInterface = None): - super().__init__(value, parent) - self._compare_stack = [] - - def _generate_compare_to(self) -> int: - """ - The number generated by the comparison stack. - E.g. can parse one.hundred.million.and.thirty.three.thousand.and.twelve.hundred.and.seven - as ['one', 'hundred', 'million', 'thirty', 'three', 'thousand', 'twelve', 'hundred', 'seven'] - which results 100,034,207 - """ - minus = len(self._compare_stack) > 0 and self._compare_stack[0] == -1 - if len(self._compare_stack) < (2 if minus else 1): - raise RuntimeError("No number to compare the value to provided.") - if minus: - self._compare_stack.pop(0) - # Compute the number - add_stack = [self._compare_stack.pop(0)] - for element in self._compare_stack: - last_power = floor(log10(abs(add_stack[-1]))) - current_power = floor(log10(abs(element))) - if last_power < current_power: # E.g. one hundred - add_stack[-1] *= element - elif last_power == 1 and current_power == 0: # E.g thirty four - add_stack[-1] += element - elif last_power > current_power: # E.g a hundred and five - add_stack.append(element) - else: - raise RuntimeError(f"Cannot chain two numbers with the same power ({add_stack[-1]} => {element}.") - total = sum(add_stack) - return -total if minus else total - - def _compare(self) -> Assertion: - raise RuntimeError(f"No comparison method defined in {type(self).__name__}.") - - def __bool__(self) -> bool: - return bool(self._compare()) - - def __call__(self, compare_to: int) -> Self: - if type(compare_to) not in (float, int): - raise RuntimeError(f"Cannot compare number ({self._value}) to non number ({repr_(compare_to)}).") - self._compare_stack.append(compare_to) - return self - - """ - Chain self properties - """ - - @property - def and_(self) -> Self: - return self - - @property - def AND(self) -> Self: - return self - - """ - Number shorthands - """ - - @property - def once(self) -> Self: - return self(1) - - @property - def twice(self) -> Self: - return self(2) - - @property - def thrice(self) -> Self: - return self(3) - - @property - def minus(self) -> Self: - return self(-1) - - @property - def zero(self) -> Self: - return self(0) - - @property - def one(self) -> Self: - return self(1) - - @property - def two(self) -> Self: - return self(2) - - @property - def three(self) -> Self: - return self(3) - - @property - def four(self) -> Self: - return self(4) - - @property - def five(self) -> Self: - return self(5) - - @property - def six(self) -> Self: - return self(6) - - @property - def seven(self) -> Self: - return self(7) - - @property - def eight(self) -> Self: - return self(8) - - @property - def nine(self) -> Self: - return self(9) - - @property - def ten(self) -> Self: - return self(10) - - @property - def eleven(self) -> Self: - return self(11) - - @property - def twelve(self) -> Self: - return self(12) - - @property - def thirteen(self) -> Self: - return self(13) - - @property - def fourteen(self) -> Self: - return self(14) - - @property - def fifteen(self) -> Self: - return self(15) - - @property - def sixteen(self) -> Self: - return self(16) - - @property - def seventeen(self) -> Self: - return self(17) - - @property - def eighteen(self) -> Self: - return self(18) - - @property - def nineteen(self) -> Self: - return self(19) - - @property - def twenty(self) -> Self: - return self(20) - - @property - def thirty(self) -> Self: - return self(30) - - @property - def forty(self) -> Self: - return self(40) - - @property - def fifty(self) -> Self: - return self(50) - - @property - def sixty(self) -> Self: - return self(60) - - @property - def seventy(self) -> Self: - return self(70) - - @property - def eighty(self) -> Self: - return self(80) - - @property - def ninety(self) -> Self: - return self(90) - - @property - def hundred(self) -> Self: - return self(100) - - @property - def thousand(self) -> Self: - return self(1_000) - - @property - def million(self) -> Self: - return self(1_000_000) - - @property - def billion(self) -> Self: - return self(1_000_000_000) - - -class LessThanComparisonInterface(NumberComparisonAssertionInterface): - def _compare(self) -> Assertion: - compare = self._generate_compare_to() - return Assertion( - self._value < compare, - f"The value ({repr_(self._value)}) is not less than to {repr_(compare)}.", - self._not - ) - - -class MoreThanComparisonInterface(NumberComparisonAssertionInterface): - def _compare(self) -> Assertion: - compare = self._generate_compare_to() - return Assertion( - self._value > compare, - f"The value ({repr_(self._value)}) is not more than to {repr_(compare)}.", - self._not - ) - - -class AtLeastComparisonInterface(NumberComparisonAssertionInterface): - def _compare(self) -> Assertion: - compare = self._generate_compare_to() - return Assertion( - self._value >= compare, - f"The value ({repr_(self._value)}) is not at least to {repr_(compare)}.", - self._not - ) - - -class AtMostComparisonInterface(NumberComparisonAssertionInterface): - def _compare(self) -> Assertion: - compare = self._generate_compare_to() - return Assertion( - self._value <= compare, - f"The value ({repr_(self._value)}) is not at least to {repr_(compare)}.", - self._not - ) - - -class EqualComparisonInterface(NumberComparisonAssertionInterface): - def _compare(self) -> Assertion: - compare = self._generate_compare_to() - return Assertion( - self._value == compare, - f"The value ({repr_(self._value)}) is not equal to {repr_(compare)}.", - self._not - ) - - @property - def to(self) -> Self: - return self - - -class NumberInterface(AssertionInterface): - def __call__(self, value): - return EqualComparisonInterface(self._value, self)(value) - - @property - def equals(self) -> EqualComparisonInterface: - return EqualComparisonInterface(self._value, self) - - @property - def equal(self) -> EqualComparisonInterface: - return EqualComparisonInterface(self._value, self) - - @property - def exactly(self) -> EqualComparisonInterface: - return EqualComparisonInterface(self._value, self) - - @property - def of(self) -> EqualComparisonInterface: - return EqualComparisonInterface(self._value, self) - - @property - def less_than(self) -> LessThanComparisonInterface: - return LessThanComparisonInterface(self._value, self) - - @property - def more_than(self) -> MoreThanComparisonInterface: - return MoreThanComparisonInterface(self._value, self) - - @property - def at_least(self) -> AtLeastComparisonInterface: - return AtLeastComparisonInterface(self._value, self) - - @property - def at_most(self) -> AtMostComparisonInterface: - return AtMostComparisonInterface(self._value, self) diff --git a/runtime-pyside6/tests/plugins/natural/interfaces/spy.py b/runtime-pyside6/tests/plugins/natural/interfaces/spy.py deleted file mode 100644 index ce0b8ff..0000000 --- a/runtime-pyside6/tests/plugins/natural/interfaces/spy.py +++ /dev/null @@ -1,218 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -from typing import Callable, Self - -from .base import Assertion, repr_, AssertionInterface -from .int import NumberComparisonAssertionInterface - -PRINT_PREFIX = (" " * 3) - - -class SpyAssertion(Assertion): - def __init__(self, assertion: bool, message: str, calls: list, invert: bool): - super().__init__(assertion, message + "\n", invert) - if len(calls) > 0: - self.message += self.render_calls(calls) - else: - self.message += f"{PRINT_PREFIX}0 registered calls." - - def render_calls(self, calls): - lines = [f"{PRINT_PREFIX}{len(calls)} registered call(s):"] - for call in calls: - repr_args = [repr_(arg) for arg in call[0]] - repr_kwargs = [f"{key}={repr_(arg)}" for key, arg in call[1].items()] - lines.append(f" - {', '.join([*repr_args, *repr_kwargs])}") - return ("\n" + PRINT_PREFIX).join(lines) - - -class Methods: - AT_LEAST_ONCE = "AT_LEAST_ONCE" - EXACTLY = "EXACTLY" - AT_LEAST = "AT_LEAST" - AT_MOST = "AT_MOST" - MORE_THAN = "MORE_THAN" - LESS_THAN = "LESS_THAN" - - -class CalledInterface(NumberComparisonAssertionInterface): - """ - Internal class generated by Spy.called. - """ - - def __init__(self, calls: list[tuple[list, dict]], parent: AssertionInterface): - super().__init__(len(calls), parent) - self.__calls = calls - self.__method = Methods.AT_LEAST_ONCE - - def __apply_method(self, calls): - required = None if self._compare_stack == [] else self._generate_compare_to() - calls_count = len(calls) - match self.__method: - case Methods.AT_LEAST_ONCE: - compare = len(calls) >= 1 - error = f"Method was not called" - case Methods.EXACTLY: - compare = len(calls) == required - error = f"Method was not called {required} times ({required} != {calls_count})" - case Methods.AT_LEAST: - compare = len(calls) >= required - error = f"Method was not called at least {required} times ({required} >= {calls_count})" - case Methods.AT_MOST: - compare = len(calls) <= required - error = f"Method was not called at most {required} times ({required} <= {calls_count})" - case Methods.MORE_THAN: - compare = len(calls) > required - error = f"Method was not called more than {required} times ({required} > {calls_count})" - case Methods.LESS_THAN: - compare = len(calls) < required - error = f"Method was not called less than {required} times ({required} < {calls_count})" - case _: - raise RuntimeError(f"Unknown method {self.__method}.") - return compare, error - - def __bool__(self) -> bool: - """ - Converts to boolean on assertion. - """ - compare, error = self.__apply_method(self.__calls) - return bool(SpyAssertion(compare, error + ".", self.__calls, self._not)) - - """ - Chaining methods - """ - - def __call__(self, compare_to: int) -> Self: - super().__call__(compare_to) - if self.__method == Methods.AT_LEAST_ONCE: - self.__method = Methods.EXACTLY - return self - - @property - def at_least(self) -> Self: - if self.__method == Methods.AT_LEAST_ONCE: - self.__method = Methods.AT_LEAST - else: - raise RuntimeError(f"Cannot redefine method from {self.__method} to {Methods.AT_MOST}") - return self - - @property - def at_most(self) -> Self: - if self.__method == Methods.AT_LEAST_ONCE: - self.__method = Methods.AT_MOST - else: - raise RuntimeError(f"Cannot redefine method from {self.__method} to {Methods.AT_MOST}") - return self - - @property - def more_than(self) -> Self: - if self.__method == Methods.AT_LEAST_ONCE: - self.__method = Methods.MORE_THAN - else: - raise RuntimeError(f"Cannot redefine method from {self.__method} to {Methods.MORE_THAN}") - return self - - @property - def less_than(self) -> Self: - if self.__method == Methods.AT_LEAST_ONCE: - self.__method = Methods.LESS_THAN - else: - raise RuntimeError(f"Cannot redefine method from {self.__method} to {Methods.LESS_THAN}") - return self - - @property - def time(self) -> Self: - return self - - @property - def times(self) -> Self: - return self - - """ - Class properties. - """ - - def __match_calls_for_condition(self, condition: Callable[[list, dict], bool]) -> tuple[bool, str]: - calls = [] - for call in self.__calls: - if condition(call[0], call[1]): - calls.append(call) - compare, error = self.__apply_method(calls) - return compare, error - - def with_arguments(self, *args, **kwargs) -> SpyAssertion: - """ - Checks if the Spy has been called the given number of times - with at least the given arguments. - """ - - def some_args_matched(a, kw): - args_matched = all(( - arg in a - for arg in args - )) - kwargs_matched = all(( - key in kw and kw[key] == arg - for key, arg in kwargs.items() - )) - return args_matched and kwargs_matched - - compare, error = self.__match_calls_for_condition(some_args_matched) - repr_args = ', '.join([repr(arg) for arg in args]) - repr_kwargs = ', '.join([f"{key}={repr(arg)}" for key, arg in kwargs.items()]) - msg = f"{error} with arguments ({repr_args}) and keyword arguments ({repr_kwargs})." - return SpyAssertion(compare, msg, self.__calls, self._not) - - def with_arguments_matching(self, test_condition: Callable[[list, dict], bool]) -> SpyAssertion: - """ - Checks if the Spy has been called the given number of times - with arguments matching the given conditions. - """ - compare, error = self.__match_calls_for_condition(test_condition) - msg = f"{error} with arguments matching given conditions." - return SpyAssertion(compare, msg, self.__calls, self._not) - - def with_exact_arguments(self, *args, **kwargs) -> SpyAssertion: - """ - Checks if the Spy has been called the given number of times - with all the given arguments. - """ - compare, error = self.__match_calls_for_condition(lambda a, kw: a == args and kw == kwargs) - repr_args = ', '.join([repr(arg) for arg in args]) - repr_kwargs = ', '.join([f"{key}={repr(arg)}" for key, arg in kwargs.items()]) - msg = f"{error} with exact arguments ({repr_args}) and keyword arguments ({repr_kwargs})." - return SpyAssertion(compare, msg, self.__calls, self._not) - - def with_no_argument(self) -> SpyAssertion: - """ - Checks if the Spy has been called the given number of times - with all the given arguments. - """ - compare, error = self.__match_calls_for_condition(lambda a, kw: len(a) == 0 and len(kw) == 0) - return SpyAssertion(compare, f"{error} with no arguments.", self.__calls, self._not) - - -class SpyAssertionInterface(AssertionInterface): - - @property - def called(self) -> CalledInterface: - """ - Returns a boolean-able interface to check conditions for a given number of - time the spy was called. - """ - return CalledInterface(self._value.calls, self) diff --git a/runtime-pyside6/tests/plugins/natural/interfaces/utils.py b/runtime-pyside6/tests/plugins/natural/interfaces/utils.py deleted file mode 100644 index 5200975..0000000 --- a/runtime-pyside6/tests/plugins/natural/interfaces/utils.py +++ /dev/null @@ -1,27 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -from PySide6.QtQml import QJSValue - - -def repr_(data): - if isinstance(data, QJSValue): - variant = data.toVariant() - return f"QJSValue<{type(variant).__name__}>({repr(variant)})" - else: - return repr(data) \ No newline at end of file diff --git a/runtime-pyside6/tests/plugins/natural/spy.py b/runtime-pyside6/tests/plugins/natural/spy.py deleted file mode 100644 index 4026b20..0000000 --- a/runtime-pyside6/tests/plugins/natural/spy.py +++ /dev/null @@ -1,33 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" -from typing import Callable - - -class Spy: - """ - Class to spy into method calls with natural language expressions. - """ - - def __init__(self, function: Callable = None): - self.function = function - self.calls = [] - - def __call__(self, *args, **kwargs): - self.calls.append((args, kwargs)) - if self.function is not None: - return self.function(*args, **kwargs) diff --git a/runtime-pyside6/tests/plugins/natural/that.py b/runtime-pyside6/tests/plugins/natural/that.py deleted file mode 100644 index 60ad4b6..0000000 --- a/runtime-pyside6/tests/plugins/natural/that.py +++ /dev/null @@ -1,68 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" -from typing import overload, Generic, TypeVar - -from . import Spy -from .interfaces.base import AssertionInterface, BaseAssertionInterface -from .interfaces.basic import StringInterface, BoolInterface, ListInterface -from .interfaces.int import NumberInterface -from .interfaces.spy import SpyAssertionInterface - -Interface = TypeVar("Interface", bound=AssertionInterface) - -MATCHES = [ - (str, StringInterface), - (bool, BoolInterface), - (int, NumberInterface), - (float, NumberInterface), - (list, ListInterface), - (Spy, SpyAssertionInterface) -] - - -@overload -def that(value: str) -> StringInterface: ... - - -@overload -def that(value: bool) -> BoolInterface: ... - - -@overload -def that(value: int) -> NumberInterface: ... - - -@overload -def that(value: float) -> NumberInterface: ... - -@overload -def that(value: Spy) -> SpyAssertionInterface: ... - - -@overload -def that[Interface](value: Interface) -> Interface: ... - - -def that(value: any) -> AssertionInterface: - if not isinstance(value, AssertionInterface): - interface = next((i for t, i in MATCHES if isinstance(value, t)), None) - if interface is not None: - value = interface(value) - else: - value = BaseAssertionInterface(value) - return value diff --git a/runtime-pyside6/tests/plugins/tests/__init__.py b/runtime-pyside6/tests/plugins/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/runtime-pyside6/tests/plugins/tests/test_natural.py b/runtime-pyside6/tests/plugins/tests/test_natural.py deleted file mode 100644 index 31f85c0..0000000 --- a/runtime-pyside6/tests/plugins/tests/test_natural.py +++ /dev/null @@ -1,217 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" -import pytest - -from ..natural import that, Assertion, Spy - - -def test_string(): - assert that("QWERTY").is_.an.instance_of(str) - assert that("QWERTY").is_.not_.an.instance_of(int) - assert that("QWERTY").is_.equal.to("QWERTY") - assert that("QWERTY").is_.NOT.equal.to("QWERTYUIOP") - assert that("QWERTY").is_.NOT.equal.to(3) - assert that("QWERTY").has.a.length.of(6) - assert that("QWERTY").does.NOT.have.a.length.of(7) - assert that("QWERTY").has.a.length.that.is_.NOT(5) - assert that("QWERTY").contains("WER") - assert that("QWERTY").contains("WER", "TY") - assert that("QWERTY").does.not_.contain("AZERTY") - with pytest.raises(Assertion): - assert that("QWERTY").is_.an.instance_of(int) - with pytest.raises(Assertion): - assert that("QWERTY").is_.equal.to(False) - with pytest.raises(Assertion): - assert that("QWERTY").has.a.length.of(1) - with pytest.raises(Assertion): - assert that("QWERTY").contains("AZERTY") - with pytest.raises(Assertion): - assert that("QWERTY").does.NOT.contain("QWE") - -def test_bool(): - assert that(True).is_.an.instance_of(bool) - assert that(True).is_.an.instance_of(int) - assert that(True).is_.NOT.an.instance_of(str) - assert that(True).equals(True) - assert that(True).is_.true - assert that(True).is_.NOT.false - assert that(False).is_.equal.to(False) - assert that(False).is_.false - assert that(False).is_.NOT.true - with pytest.raises(Assertion): - assert that(True).is_.false - with pytest.raises(Assertion): - assert that(True).is_.NOT.true - -def test_int(): - assert that(2).is_.an.instance_of(int) - assert that(2).is_.NOT.an.instance_of(bool) - assert that(2).is_.NOT.an.instance_of(str) - assert that(2).is_.more_than(1) - assert that(2).is_.NOT.less_than(1) - assert that(2).is_.less_than(3) - assert that(2).is_.NOT.more_than(3) - assert that(2).is_.at_least(1) - assert that(2).is_.NOT.at_most(1) - assert that(2).is_.at_most(3) - assert that(2).is_.NOT.at_least(3) - assert that(2).is_.at_most(2) - assert that(2).is_.at_least(2) - # Equality - assert that(2).is_(2) - assert that(2).was(2) - assert that(2).is_.exactly(2) - assert that(2).is_.equal.to(2) - assert that(2).equals(2) - assert that(2).is_.NOT(3) - assert that(2).does.NOT.equal(3) - -def test_int_shorthands(): - # Direct numbers - assert that(0).equals.zero - assert that(1).equals.one - assert that(2).equals.two - assert that(3).equals.three - assert that(4).equals.four - assert that(5).equals.five - assert that(6).equals.six - assert that(7).equals.seven - assert that(8).equals.eight - assert that(9).equals.nine - assert that(10).equals.ten - assert that(11).equals.eleven - assert that(12).equals.twelve - assert that(13).equals.thirteen - assert that(14).equals.fourteen - assert that(15).equals.fifteen - assert that(16).equals.sixteen - assert that(17).equals.seventeen - assert that(18).equals.eighteen - assert that(19).equals.nineteen - assert that(20).equals.twenty - assert that(30).equals.thirty - assert that(40).equals.forty - assert that(50).equals.fifty - assert that(60).equals.sixty - assert that(70).equals.seventy - assert that(80).equals.eighty - assert that(90).equals.ninety - assert that(100).equals.a.hundred - assert that(1000).equals.a.thousand - assert that(1_000_000).equals.a.million - -def test_add_natural_complex(): - # Test composed - assert that(34).equals.thirty.four - assert that(-34).equals.minus.thirty.four - assert that(100_033_207).equals.one.hundred.million.AND.thirty.three.thousand.AND.two.hundred.AND.seven - assert that(-1_200_033_207).equals.minus.one.billion.AND.two.hundred.million.AND.thirty.three.thousand.AND.two.hundred.AND.seven - assert that(7890).equals.seven.thousand.eight.hundred.and_.ninety - assert that(7890).equals.seventy.eight.hundred.and_.ninety - assert that(7890).equals(78)(100)(90) - with pytest.raises(RuntimeError): - assert that(1_000_000).equals.a.thousand.thousand - with pytest.raises(RuntimeError): - assert that(600).equals.one.twenty.thirty - with pytest.raises(RuntimeError): - assert that(2).equals - with pytest.raises(RuntimeError): - assert that(2).equals.one.minus.two - -def test_spy(): - spy = Spy(lambda *args, **kw: 10) - assert that(spy).is_.an.instance_of(Spy) - assert that(spy).is_(callable) - # Check calls - assert that(spy).was.never.called - assert that(spy).was.called.zero.times - assert spy(30, arg="string") == 10 - assert that(spy).was.called - assert that(spy).was.called.once - assert that(spy).was.called.one.time - assert that(spy).was.NOT.called.more_than.once - assert that(spy).was.called.with_arguments(30) - assert that(spy).was.called.with_arguments_matching(lambda args, kwargs: len(args) == 1 and len(kwargs) == 1) - assert that(spy).was.NOT.called.with_arguments(50) - assert that(spy).was.NOT.called.with_exact_arguments(30) - assert that(spy).was.NOT.called.with_no_argument() - assert that(spy).was.called.with_exact_arguments(30, arg="string") - with pytest.raises(Assertion): - assert that(spy).was.called.with_arguments(50) - with pytest.raises(Assertion): - assert that(spy).was.called.with_exact_arguments(30) - with pytest.raises(Assertion): - assert that(spy).was.called.with_no_argument() - -def test_spy_seral_calls(): - spy = Spy() - obj = object() - spy() - spy(30, arg="string") - spy(obj, 30, example=obj, none=None) - assert that(spy).was.called - assert that(spy).was.called.more_than.once - assert that(spy).was.called.more_than.twice - assert that(spy).was.NOT.called.more_than.thrice - assert that(spy).was.called.at_most.thrice - assert that(spy).was.called.at_least.thrice - assert that(spy).was.called.three.times - assert that(spy).was.called.less_than(4).times - # Check arguments - assert that(spy).was.called.once.with_no_argument() - assert that(spy).was.called.at_most.once.with_no_argument() - assert that(spy).was.called.twice.with_arguments(30) - assert that(spy).was.NOT.called.less_than.twice.with_arguments(30) - assert that(spy).was.called.once.with_arguments(obj) - assert that(spy).was.called.once.with_arguments(arg="string") - assert that(spy).was.called.once.with_arguments(30, obj) - assert that(spy).was.called.once.with_arguments(none=None) - assert that(spy).was.NOT.called.with_arguments(None) - assert that(spy).was.NOT.called.with_arguments(obj, 30, arg="string") - with pytest.raises(Assertion): - assert that(spy).was.called.with_arguments(obj, 30, arg="string") - # Checking with exact arguments - assert that(spy).was.called.once.with_exact_arguments(30, arg="string") - assert that(spy).was.called.once.with_exact_arguments(obj, 30, example=obj, none=None) - assert that(spy).was.NOT.called.with_exact_arguments(obj, 30, arg="string") - with pytest.raises(Assertion): - assert that(spy).was.called.with_exact_arguments(obj, 30, arg="string") - # Check arguments matching - assert that(spy).has.NOT.been.called.with_arguments_matching(lambda a, kw: len(a) + len(kw) == 3) - assert that(spy).was.called.once.with_arguments_matching(lambda a, kw: len(a) + len(kw) == 2) - assert that(spy).was.called.once.with_arguments_matching(lambda a, kw: len(a) + len(kw) == 4) - with pytest.raises(Assertion): - assert that(spy).was.called.with_arguments_matching(lambda a, kw: len(a) + len(kw) == 3) - - -def test_wrongful_expressions(): - spy = Spy() - with pytest.raises(RuntimeError): - assert that(3).is_.less_than("str") - with pytest.raises(RuntimeError): - assert that(3).does.NOT.NOT.equal(3) - with pytest.raises(RuntimeError): - assert that(3).is_.an.instance_of("non type") - with pytest.raises(RuntimeError): - assert that(spy).was.called.more_than.at_least.once - with pytest.raises(RuntimeError): - assert that(spy).was.called.more_than.at_most.once - with pytest.raises(RuntimeError): - assert that(spy).was.called.more_than.less_than.once - with pytest.raises(RuntimeError): - assert that(spy).was.called.more_than.more_than.once \ No newline at end of file diff --git a/runtime-pyside6/tests/test_config.py b/runtime-pyside6/tests/test_config.py deleted file mode 100644 index a95a9d9..0000000 --- a/runtime-pyside6/tests/test_config.py +++ /dev/null @@ -1,60 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -import pytest -from LogarithmPlotter.util import config -from tempfile import TemporaryDirectory -from os.path import join - - -@pytest.fixture() -def resource(): - directory = TemporaryDirectory() - config.CONFIG_FILE = join(directory.name, "config.json") - config.init() - yield config.CONFIG_FILE - directory.cleanup() - - -class TestConfig: - - def test_init(self, resource): - assert config.current_config == config.DEFAULT_SETTINGS - - def test_get(self, resource): - assert config.getSetting("expression_editor.autoclose") == True - with pytest.raises(config.UnknownNamespaceError): - config.getSetting("unknown_setting") - - def test_set(self, resource): - assert config.setSetting("expression_editor.autoclose", False) is None - assert config.getSetting("expression_editor.autoclose") == False # Ensure set is working. - with pytest.raises(config.UnknownNamespaceError): - config.setSetting("unknown_dict.unknown_setting", False) - - def test_reinit(self, resource): - default_value = config.getSetting("expression_editor.autoclose") - config.setSetting("expression_editor.autoclose", not default_value) - config.init() - assert config.getSetting("expression_editor.autoclose") != default_value # Ensure setting has been reset. - - def test_save(self, resource): - config.setSetting("expression_editor.autoclose", False) - config.save(resource) - config.init() - assert config.getSetting("expression_editor.autoclose") == False # Ensure setting has been saved. diff --git a/runtime-pyside6/tests/test_debug.py b/runtime-pyside6/tests/test_debug.py deleted file mode 100644 index 467e5b8..0000000 --- a/runtime-pyside6/tests/test_debug.py +++ /dev/null @@ -1,68 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -import pytest - -from os.path import exists -from PySide6.QtCore import QtMsgType, QMessageLogContext - -from LogarithmPlotter.util import debug - - -def test_setup(): - sourcemap_installed = False - try: - import sourcemap - sourcemap_installed = True - except: - pass - if sourcemap_installed: - file = debug.SOURCEMAP_PATH - debug.SOURCEMAP_PATH = None - debug.setup() # Nothing could be setup. - debug.SOURCEMAP_PATH = file - debug.setup() - assert (sourcemap_installed and exists(debug.SOURCEMAP_PATH)) == (debug.SOURCEMAP_INDEX is not None) - - -def test_map_source(): - sourcemap_available = debug.SOURCEMAP_INDEX is not None - if sourcemap_available: - assert debug.map_javascript_source("js/index.mjs", 22) != ("js/module/index.mjs", 22) - assert debug.map_javascript_source("js/index.mjs", 100000) == ("js/index.mjs", 100000) # Too long, not found - debug.SOURCEMAP_INDEX = None - assert debug.map_javascript_source("js/index.mjs", 21) == ("js/index.mjs", 21) - - -def test_log_terminal_message(): - msg1 = debug.create_log_terminal_message( - QtMsgType.QtWarningMsg, QMessageLogContext(), - "a long and random message" - ) - assert "[WARNING]" in msg1 - assert "a long and random message" in msg1 - msg2 = debug.create_log_terminal_message( - QtMsgType.QtCriticalMsg, - QMessageLogContext("LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Index.qml", 15, "anotherFunctionName", - "aCategoryDifferent"), - "LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Index.qml:15: a potentially potential error message" - ) - assert "[CRITICAL]" in msg2 - assert "Index.qml" in msg2 - assert "a potentially potential error message" in msg2 - assert "anotherFunctionName" in msg2 diff --git a/runtime-pyside6/tests/test_helper.py b/runtime-pyside6/tests/test_helper.py deleted file mode 100644 index f2f6d70..0000000 --- a/runtime-pyside6/tests/test_helper.py +++ /dev/null @@ -1,181 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -import pytest -from os import getcwd, remove, path -from os.path import join -from tempfile import TemporaryDirectory -from json import loads -from shutil import copy2 - -from PySide6.QtCore import QObject, Signal, QThreadPool -from PySide6.QtGui import QImage -from PySide6.QtQml import QJSValue -from PySide6.QtWidgets import QApplication - -from LogarithmPlotter import __VERSION__ as version -from LogarithmPlotter.util import config, helper -from LogarithmPlotter.util.helper import Helper, fetch_changelog, read_changelog, InvalidFileException - -pwd = getcwd() -helper.SHOW_GUI_MESSAGES = False - -@pytest.fixture() -def temporary(): - directory = TemporaryDirectory() - config.CONFIG_PATH = join(directory.name, "config.json") - tmpfile = join(directory.name, "graph.png") - yield tmpfile, directory - directory.cleanup() - - -def create_changelog_callback_asserter(promise, expect_404=False): - def cb(changelog, expect_404=expect_404): - # print("Got changelog", changelog) - assert isinstance(changelog, QJSValue) - assert changelog.isString() - changlogValue = changelog.toVariant() - assert ('404' in changlogValue) == expect_404 - def error(e): - raise eval(e) - promise.then(cb, error) - -CHANGELOG_BASE_PATH = path.realpath(path.join(path.dirname(path.realpath(__file__)), "..", "CHANGELOG.md")) - - -class TestHelper: - def test_changelog(self, temporary, qtbot): - # Exists - helper.CHANGELOG_VERSION = '0.5.0' - tmpfile, directory = temporary - obj = Helper(pwd, tmpfile) - promise = obj.fetchChangelog() - create_changelog_callback_asserter(promise, expect_404=False) - with qtbot.waitSignal(promise.fulfilled, timeout=10000): - pass - assert type(fetch_changelog()) == str - # Does not exist - helper.CHANGELOG_VERSION = '2.0.0' - tmpfile, directory = temporary - obj = Helper(pwd, tmpfile) - promise = obj.fetchChangelog() - create_changelog_callback_asserter(promise, expect_404=True) - with qtbot.waitSignal(promise.fulfilled, timeout=10000): - pass - # Local - tmpfile, directory = temporary - obj = Helper(pwd, tmpfile) - assert path.exists(CHANGELOG_BASE_PATH) - copy2(CHANGELOG_BASE_PATH, helper.CHANGELOG_CACHE_PATH) - assert path.exists(helper.CHANGELOG_CACHE_PATH) - promise = obj.fetchChangelog() - create_changelog_callback_asserter(promise, expect_404=False) - with qtbot.waitSignal(promise.fulfilled, timeout=100): # Local - pass - assert type(read_changelog()) == str - - def test_read(self, temporary): - # Test file reading and information loading. - tmpfile, directory = temporary - obj = Helper(pwd, tmpfile) - data = obj.load("ci/test1.lpf") - assert type(data) == str - data = loads(data) - assert data['type'] == "logplotv1" - # Checking data['types'] of valid file. - # See https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/LogarithmPlotter-file-format-v1.0 - assert type(data['width']) == int - assert type(data['height']) == int - assert type(data['xzoom']) in (int, float) - assert type(data['yzoom']) in (int, float) - assert type(data['xmin']) in (int, float) - assert type(data['ymax']) in (int, float) - assert type(data['xaxisstep']) == str - assert type(data['yaxisstep']) == str - assert type(data['xaxislabel']) == str - assert type(data['yaxislabel']) == str - assert type(data['logscalex']) == bool - assert type(data['linewidth']) in (int, float) - assert type(data['showxgrad']) == bool - assert type(data['showygrad']) == bool - assert type(data['textsize']) in (int, float) - assert type(data['history']) == list and len(data['history']) == 2 - assert type(data['history'][0]) == list - assert type(data['history'][1]) == list - for action_list in data['history']: - for action in action_list: - assert type(action[0]) == str - assert type(action[1]) == list - assert type(data['objects']) == dict - for obj_type, objects in data['objects'].items(): - assert type(obj_type) == str - assert type(objects) == list - for obj in objects: - assert type(obj) == list - - def test_read_newer(self, temporary): - tmpfile, directory = temporary - obj = Helper(pwd, tmpfile) - newer_file_path = join(directory.name, "newer.lpf") - with open(newer_file_path, "w") as f: - f.write("LPFv2[other invalid data]") - with pytest.raises(InvalidFileException): - obj.load(newer_file_path) - - def test_read_invalid_file(self, temporary): - tmpfile, directory = temporary - obj = Helper(pwd, tmpfile) - with pytest.raises(InvalidFileException): - obj.load("./inexistant.lpf") - with pytest.raises(InvalidFileException): - obj.load("./pyproject.toml") - - def test_write(self, temporary): - tmpfile, directory = temporary - obj = Helper(pwd, tmpfile) - target = join(directory.name, "target.lpf") - data = "example_data" - obj.write(target, data) - with open(target, "r") as f: - read_data = f.read() - # Ensure data has been written. - assert read_data == "LPFv1" + data - - def test_tmp_graphic(self, temporary): - tmpfile, directory = temporary - obj = Helper(pwd, tmpfile) - assert obj.gettmpfile() == tmpfile - obj.copyImageToClipboard() - clipboard = QApplication.clipboard() - assert type(clipboard.image()) == QImage - - def test_strings(self, temporary): - tmpfile, directory = temporary - obj = Helper(pwd, tmpfile) - assert obj.getVersion() == version - assert type(obj.getDebugInfos()) == str - assert type(obj.getSetting("last_install_greet").toVariant()) == str - assert type(obj.getSetting("check_for_updates").toVariant()) == bool - assert type(obj.getSetting("default_graph.xzoom").toVariant()) in [float, int] - - def test_set_config(self, temporary): - tmpfile, directory = temporary - obj = Helper(pwd, tmpfile) - obj.setSetting("last_install_greet", obj.getSetting("last_install_greet")) - obj.setSetting("check_for_updates", obj.getSetting("check_for_updates")) - obj.setSetting("default_graph.xzoom", obj.getSetting("default_graph.xzoom")) diff --git a/runtime-pyside6/tests/test_latex.py b/runtime-pyside6/tests/test_latex.py deleted file mode 100644 index e410995..0000000 --- a/runtime-pyside6/tests/test_latex.py +++ /dev/null @@ -1,123 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -import pytest -from tempfile import TemporaryDirectory -from shutil import which -from os.path import exists -from re import match -from PySide6.QtGui import QColor -from PySide6.QtQml import QJSValue - -from LogarithmPlotter.util import latex - -latex.SHOW_GUI_MESSAGES = False - - -@pytest.fixture() -def latex_obj(): - directory = TemporaryDirectory() - obj = latex.Latex(directory.name) - if not obj.checkLatexInstallation(): - raise Exception("Cannot run LaTeX tests without a proper LaTeX installation. Make sure to install a LaTeX distribution, DVIPNG, and the calligra package, and run the tests again.") - yield obj - directory.cleanup() - - -BLACK = QColor(0, 0, 0, 255) -BLUE = QColor(128, 128, 255, 255) - -def check_render_results(result): - if isinstance(result, QJSValue): - result = result.toVariant() - assert type(result) == str - [path, width, height] = result.split(",") - assert path.startswith("file:///") - assert exists(path[len("file:///"):]) - assert match(r"\d+", width) - assert match(r"\d+", height) - return True - -class TestLatex: - def test_check_install(self, latex_obj: latex.Latex) -> None: - assert latex_obj.latexSupported == True - assert latex_obj.checkLatexInstallation() == True - assert type(latex_obj.supportsAsyncRender) is bool - bkp = [latex.DVIPNG_PATH, latex.LATEX_PATH] - # Check what happens when one is missing. - latex.DVIPNG_PATH = None - assert latex_obj.latexSupported == False - assert latex_obj.checkLatexInstallation() == False - latex.DVIPNG_PATH = bkp[0] - latex.LATEX_PATH = None - assert latex_obj.latexSupported == False - assert latex_obj.checkLatexInstallation() == False - # Reset - [latex.DVIPNG_PATH, latex.LATEX_PATH] = bkp - - def test_render_sync(self, latex_obj: latex.Latex) -> None: - result = latex_obj.renderSync("\\frac{d \\sqrt{\\mathrm{f}(x \\times 2.3)}}{dx}", 14, BLACK) - # Ensure result format - check_render_results(result) - # Ensure it returns errors on invalid latex. - with pytest.raises(latex.RenderError): - latex_obj.renderSync("\\nonexistant", 14, BLACK) - # Replace latex bin with one that returns errors - bkp = latex.LATEX_PATH - latex.LATEX_PATH = which("false") - with pytest.raises(latex.RenderError): - latex_obj.renderSync("\\mathrm{f}(x)", 14, BLACK) - # Replace latex bin with one goes indefinitely - # latex.LATEX_PATH = which("import") # TODO: Find one such executable - # with pytest.raises(latex.RenderError): - # latex_obj.renderSync("\\mathrm{f}(x)", 14, BLACK) - latex.LATEX_PATH = bkp - - def test_prerendered(self, latex_obj: latex.Latex) -> None: - args = ["\\frac{d \\sqrt{\\mathrm{f}(x \\times 2.3)}}{dx}", 14, BLACK] - latex_obj.renderSync(*args) - prerendered = latex_obj.findPrerendered(*args) - assert type(prerendered) == str - check_render_results(prerendered) - prerendered2 = latex_obj.findPrerendered(args[0], args[1]+2, args[2]) - assert prerendered2 == "" - - def test_render_async(self, latex_obj: latex.Latex, qtbot) -> None: - formula = "\\int\\limits^{3x}_{-\\infty}9\\mathrm{f}(x)^3+t dx" - og_promise = latex_obj.renderAsync(formula, 14, BLACK) - # Ensure we get the same locked one if we try to render it again. - assert og_promise == latex_obj.renderAsync(formula, 14, BLACK) - # Ensure queued renders. - promises = [ - latex_obj.renderAsync(formula, 14, BLUE), - latex_obj.renderAsync(formula, 10, BLACK), - latex_obj.renderAsync(formula, 10, BLUE), - ] - for prom in promises: - assert og_promise.calls_upon_fulfillment(prom.start) - # Ensure other renders get done in parallel. - other_promise = latex_obj.renderAsync(formula+" dt", 10, BLACK) - assert not og_promise.calls_upon_fulfillment(other_promise.start) - # Ensure all of them render. - proms = [og_promise, *promises, other_promise] - with qtbot.waitSignals( - [p.fulfilled for p in proms], - raising=True, timeout=10000, - check_params_cbs=[check_render_results]*len(proms) - ): - pass diff --git a/runtime-pyside6/tests/test_main.py b/runtime-pyside6/tests/test_main.py deleted file mode 100644 index 1595236..0000000 --- a/runtime-pyside6/tests/test_main.py +++ /dev/null @@ -1,108 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -import pytest -from os import environ -from os.path import exists, join -from PySide6.QtGui import QIcon -from tempfile import TemporaryDirectory - -from .globals import app - -from LogarithmPlotter.logarithmplotter import get_linux_theme, LINUX_THEMES, get_platform_qt_style, \ - register_icon_directories, install_translation, create_engine -from LogarithmPlotter.util import config -from LogarithmPlotter.util.helper import Helper -from LogarithmPlotter.util.latex import Latex - -THEMES = [ - "Basic", - "Universal", - "Material", - "Fusion", - "Windows", - "macOS" -] - -OS_PLATFORMS = [ - "Linux", - "Windows", - "Darwin", - "Android" -] - -@pytest.fixture() -def temporary(): - directory = TemporaryDirectory() - config.CONFIG_PATH = join(directory.name, "config.json") - tmpfile = join(directory.name, "graph.png") - yield tmpfile, directory - directory.cleanup() - -class TestMain: - def test_linux_themes(self): - # Check without a desktop - if "XDG_SESSION_DESKTOP" in environ: - del environ["XDG_SESSION_DESKTOP"] - assert get_linux_theme() in THEMES - # Test various environments. - environ["XDG_SESSION_DESKTOP"] = "GNOME" - assert get_linux_theme() in THEMES - # Test various environments. - environ["XDG_SESSION_DESKTOP"] = "NON-EXISTENT" - assert get_linux_theme() in THEMES - # Check all linux themes are in list - for desktop, theme in LINUX_THEMES.items(): - assert theme in THEMES - - def test_os_themes(self): - for platform in OS_PLATFORMS: - assert get_platform_qt_style(platform) in THEMES - - def test_icon_directories(self): - base_paths = QIcon.fallbackSearchPaths() - register_icon_directories() - # Check if registered - assert len(base_paths) < len(QIcon.fallbackSearchPaths()) - # Check if all exists - for p in QIcon.fallbackSearchPaths(): - assert exists(p) - - def test_app(self, temporary): - assert not app.windowIcon().isNull() - # Translations - translator = install_translation(app) - assert not translator.isEmpty() - # Engine - tmpfile, tempdir = temporary - helper = Helper(".", tmpfile) - latex = Latex(tempdir.name) - engine, js_globals = create_engine(helper, latex, 0) - assert len(engine.rootObjects()) > 0 # QML File loaded. - assert type(engine.rootContext().contextProperty("TestBuild")) is bool - assert engine.rootContext().contextProperty("StartTime") == 0 - assert js_globals.Latex.type() is not None - assert js_globals.Helper.type() is not None - assert js_globals.Modules.type() is not None - # Check if modules have loaded - assert js_globals.Modules.History.type() is not None - assert js_globals.Modules.Latex.type() is not None - assert js_globals.Modules.Canvas.type() is not None - assert js_globals.Modules.IO.type() is not None - assert js_globals.Modules.Objects.type() is not None - assert js_globals.Modules.Preferences.type() is not None diff --git a/runtime-pyside6/tests/test_native.py b/runtime-pyside6/tests/test_native.py deleted file mode 100644 index d60b907..0000000 --- a/runtime-pyside6/tests/test_native.py +++ /dev/null @@ -1,56 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -import pytest - -from PySide6.QtCore import QEvent, QObject, QUrl -from PySide6.QtGui import QActionEvent, QFileOpenEvent - -from LogarithmPlotter.util.native import MacOSFileOpenHandler - - -class LoadDiagramCalledSuccessfully(Exception): pass - - -class MockIO: - def loadDiagram(self, file_name): - assert type(file_name) == str - raise LoadDiagramCalledSuccessfully() - - -class MockFileOpenEvent(QEvent): - def __init__(self, file): - QEvent.__init__(self, QEvent.FileOpen) - self._file = file - - def file(self): - return self._file - - -def test_native(): - event_filter = MacOSFileOpenHandler() - # Nothing should happen here. The module hasn't been initialized - event_filter.eventFilter(None, QFileOpenEvent(QUrl.fromLocalFile("ci/test1.lpf"))) - with pytest.raises(LoadDiagramCalledSuccessfully): - event_filter.init_io(MockIO()) # Now that we've initialized, the loadDiagram function should be called. - with pytest.raises(LoadDiagramCalledSuccessfully): - # And now it will do so every time an event is loaded. - event_filter.eventFilter(None, QFileOpenEvent(QUrl.fromLocalFile("ci/test1.lpf"))) - # Check what happens when a non file open qevent is launched against it. - event_filter.eventFilter(QObject(), QEvent(QEvent.ActionAdded)) - diff --git a/runtime-pyside6/tests/test_promise.py b/runtime-pyside6/tests/test_promise.py deleted file mode 100644 index ef71a55..0000000 --- a/runtime-pyside6/tests/test_promise.py +++ /dev/null @@ -1,177 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" -from time import sleep - -import pytest -from PySide6.QtQml import QJSValue - -from .plugins.natural import that, Spy -from LogarithmPlotter.util.js import PyJSValue -from LogarithmPlotter.util.promise import PyPromise - - -def check_promise_result(value): - def got_result(args, kwargs, val=value): - valid = len(args) == 1 and len(kwargs) == 0 - if valid: - got_value = args[0].toVariant() if isinstance(args[0], QJSValue) else args[0] - valid = got_value == val - return valid - return got_result - -def create_async_func(value): - def async_function(val=value): - sleep(1) - return val - - return async_function - - -def qjs_eq(origin): - def compare(result): - res = result.toVariant() == origin - print("Unknown res!", res, repr(result.toVariant()), repr(origin)) - return res - return compare - - -def async_throw(): - sleep(1) - raise Exception("aaaa") - - -class TestPyPromise: - - def test_invalid_function(self): - with pytest.raises(ValueError): - promise = PyPromise("not a function") - - def test_fulfill_values(self, qtbot): - qjsv = QJSValue(3) - values = [ - [True, qjs_eq(True)], - [3, qjs_eq(3)], - [2.2, qjs_eq(2.2)], - ["String", qjs_eq("String")], - [qjsv, qjs_eq(3)], - [None, qjs_eq(None)], - [PyJSValue(QJSValue("aaa")), qjs_eq("aaa")] - ] - for [value, test] in values: - promise = PyPromise(create_async_func(value)) - with qtbot.assertNotEmitted(promise.rejected, wait=1000): - with qtbot.waitSignal(promise.fulfilled, check_params_cb=test, timeout=2000): - assert promise.state == "pending" - assert promise.state == "fulfilled" - - def test_reject(self, qtbot): - promise = PyPromise(async_throw) - with qtbot.assertNotEmitted(promise.fulfilled, wait=1000): - with qtbot.waitSignal(promise.rejected, timeout=10000, - check_params_cb=lambda t: t == "Exception('aaaa')"): - assert promise.state == "pending" - assert promise.state == "rejected" - - def test_fulfill(self, qtbot): - fulfilled = Spy() - rejected = Spy() - promise = PyPromise(create_async_func(3)) - then_res = promise.then(fulfilled, rejected) - # Check if the return value is the same promise (so we can chain then) - assert that(then_res).does.equal(promise) - # Check on our spy. - with qtbot.waitSignal(promise.fulfilled, timeout=10000): - pass - assert that(fulfilled).was.called.once - assert that(fulfilled).was.NOT.called.with_arguments(3) - assert that(fulfilled).was.called.with_arguments_matching(check_promise_result(3)) - assert that(rejected).was.never.called - - def test_rejected(self, qtbot): - fulfilled = Spy() - rejected = Spy() - promise = PyPromise(async_throw) - then_res = promise.then(fulfilled, rejected) - # Check if the return value is the same promise (so we can chain then) - assert that(then_res).does.equal(promise) - # Check on our spies. - with qtbot.waitSignal(promise.rejected, timeout=10000): - pass - assert that(rejected).was.called.once - assert that(rejected).was.called.with_arguments("Exception('aaaa')") - assert that(fulfilled).has.never.been.called - - def test_chain_fulfill(self, qtbot): - convert = Spy(lambda v: v.toVariant()) - plus = Spy(lambda v: v + 1) - rejected = Spy() - promise = PyPromise(create_async_func(5)) - then_res = promise.then(convert, rejected).then(plus, rejected).then(plus, rejected).then(plus, rejected) - # Check if the return value is the same promise (so we can chain then) - assert that(then_res).does.equal(promise) - with qtbot.waitSignal(promise.fulfilled, timeout=10000): - pass - assert that(convert).was.called.once.with_arguments_matching(check_promise_result(5)) - assert that(rejected).was.never.called - assert that(plus).was.called.three.times - assert that(plus).was.called.once.with_exact_arguments(5) - assert that(plus).was.called.once.with_exact_arguments(6) - assert that(plus).was.called.once.with_exact_arguments(7) - - def test_chain_reject(self, qtbot): - fulfilled = Spy() - convert = Spy(lambda v: len(v)) - minus = Spy(lambda v: v - 1) - promise = PyPromise(async_throw) - then_res = promise.then(fulfilled, convert).then(fulfilled, minus).then(fulfilled, minus).then(fulfilled, minus) - # Check if the return value is the same promise (so we can chain then) - assert that(then_res).does.equal(promise) - with qtbot.waitSignal(promise.rejected, timeout=10000): - pass - assert that(fulfilled).was.never.called - assert that(convert).was.called.once.with_arguments_matching(check_promise_result("Exception('aaaa')")) - assert that(minus).was.called.three.times - assert that(minus).was.called.once.with_exact_arguments(17) - assert that(minus).was.called.once.with_exact_arguments(16) - assert that(minus).was.called.once.with_exact_arguments(15) - - def test_check_calls_upon(self): - promise = PyPromise(async_throw) - fulfilled = Spy() - rejected = Spy() - promise.then(fulfilled, rejected) - assert promise.calls_upon_fulfillment(fulfilled) - assert promise.calls_upon_rejection(rejected) - assert not promise.calls_upon_fulfillment(rejected) - assert not promise.calls_upon_rejection(fulfilled) - - def test_reject_in_fulfill(self, qtbot): - def fulfilled_throw(x): - raise Exception('noooo') - promise = PyPromise(create_async_func("3")) - fulfilled_throw = Spy(fulfilled_throw) - fulfilled = Spy() - rejected = Spy() - then_res = promise.then(fulfilled, rejected).then(fulfilled_throw, rejected).then(fulfilled, rejected).then(fulfilled, rejected) - # Check if the return value is the same promise (so we can chain then) - assert that(then_res).does.equal(promise) - with qtbot.waitSignal(promise.fulfilled, timeout=10000): - pass - assert that(fulfilled_throw).has.been.called.once - assert that(rejected).has.been.called.three.times - assert that(rejected).has.been.called.three.times.with_arguments("Exception('noooo')") \ No newline at end of file diff --git a/runtime-pyside6/tests/test_pyjs.py b/runtime-pyside6/tests/test_pyjs.py deleted file mode 100644 index 9177b4e..0000000 --- a/runtime-pyside6/tests/test_pyjs.py +++ /dev/null @@ -1,76 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" - -import pytest -from re import Pattern -from PySide6.QtQml import QJSEngine, QJSValue - -from LogarithmPlotter.util.js import PyJSValue, InvalidAttributeValueException, NotAPrimitiveException - -@pytest.fixture() -def data(): - engine = QJSEngine() - obj = PyJSValue(engine.globalObject()) - yield engine, obj - -class TestPyJS: - def test_set(self, data): - engine, obj = data - obj.num1 = 2 - obj.num2 = QJSValue(2) - obj.num3 = PyJSValue(QJSValue(2)) - with pytest.raises(InvalidAttributeValueException): - obj.num3 = object() - - def test_eq(self, data): - engine, obj = data - obj.num = QJSValue(2) - assert obj.num == 2 - assert obj.num == QJSValue(2) - assert obj.num == PyJSValue(QJSValue(2)) - assert obj.num != object() - - def test_function(self, data): - engine, obj = data - function = PyJSValue(engine.evaluate("(function(argument) {return argument*2})")) - assert function(3) == 6 - assert function(10) == 20 - function2 = PyJSValue(engine.evaluate("(function(argument) {return argument+3})"), obj.qjs_value) - assert function2(3) == 6 - assert function2(10) == 13 - function3 = PyJSValue(engine.evaluate("2+2")) - with pytest.raises(InvalidAttributeValueException): - function3() - - def test_type(self, data): - engine, obj = data - assert PyJSValue(engine.evaluate("[]")).type() == list - assert PyJSValue(engine.evaluate("undefined")).type() is None - assert PyJSValue(engine.evaluate("/[a-z]/g")).type() == Pattern - assert PyJSValue(QJSValue(2)).type() == float - assert PyJSValue(QJSValue("3")).type() == str - assert PyJSValue(QJSValue(True)).type() == bool - - def test_primitive(self, data): - engine, obj = data - assert PyJSValue(QJSValue(2)).primitive() == 2 - assert PyJSValue(QJSValue("string")).primitive() == "string" - assert PyJSValue(QJSValue(True)).primitive() == True - assert PyJSValue(engine.evaluate("undefined")).primitive() is None - with pytest.raises(NotAPrimitiveException): - assert PyJSValue(engine.evaluate("[]")).primitive() == [] diff --git a/runtime-pyside6/tests/test_update.py b/runtime-pyside6/tests/test_update.py deleted file mode 100644 index 16b562e..0000000 --- a/runtime-pyside6/tests/test_update.py +++ /dev/null @@ -1,68 +0,0 @@ -""" - * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 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 . -""" -from sys import argv - -import pytest -from PySide6.QtCore import QThreadPool - -from LogarithmPlotter import __VERSION__ as version -from LogarithmPlotter.util.update import UpdateInformation, UpdateCheckerRunnable, check_for_updates - - -class MockWindow: - def showAlert(self, msg): raise Exception(msg) - def showUpdateMenu(self, msg): pass - -def check_update_callback_type(show_alert, msg_text, update_available): - assert type(show_alert) == bool - assert type(msg_text) == str - assert type(update_available) == bool - -def test_update(qtbot): - def check_older(show_alert, msg_text, update_available): - check_update_callback_type(show_alert, msg_text, update_available) - assert update_available - assert show_alert - - def check_newer(show_alert, msg_text, update_available): - check_update_callback_type(show_alert, msg_text, update_available) - assert not update_available - assert not show_alert - - update_info_older = UpdateInformation() - update_info_older.got_update_info.connect(check_older) - update_info_newer = UpdateInformation() - update_info_newer.got_update_info.connect(check_newer) - runnable = UpdateCheckerRunnable('1.0.0', update_info_newer) - with qtbot.waitSignal(update_info_newer.got_update_info, timeout=10000): - runnable.run() - runnable = UpdateCheckerRunnable('0.1.0', update_info_older) - with qtbot.waitSignal(update_info_older.got_update_info, timeout=10000): - runnable.run() - runnable = UpdateCheckerRunnable('0.5.0+dev0+git20240101', update_info_older) - with qtbot.waitSignal(update_info_older.got_update_info, timeout=10000): - runnable.run() - -def test_update_checker(qtbot): - update_info = check_for_updates('0.6.0', MockWindow()) - assert QThreadPool.globalInstance().activeThreadCount() >= 1 - with qtbot.waitSignal(update_info.got_update_info, timeout=10000): - pass - argv.append("--no-check-for-updates") - update_info = check_for_updates('0.6.0', MockWindow()) - assert QThreadPool.globalInstance().activeThreadCount() < 2 # No new update checks where added diff --git a/scripts/build-macosx.sh b/scripts/build-macosx.sh index 8f3f38d..c4b7686 100755 --- a/scripts/build-macosx.sh +++ b/scripts/build-macosx.sh @@ -1,35 +1,21 @@ #!/usr/bin/env bash -DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd "$DIR/.." || exit 1 +cd "$(dirname "$(readlink -f "$0" || realpath "$0")")/.." -rebuild=true - -while [ $# -gt 0 ]; do - case "$1" in - --no-rebuild) - rebuild=false - ;; - *) - box "Error: Invalid argument." - exit 1 - esac - shift -done - -if [ "$rebuild" == "true" ]; then - rm -rf build - bash scripts/build.sh -fi - -cd build/runtime-pyside6 || exit 1 +rm $(find . -name "*.qmlc") rm $(find . -name "*.pyc") +python3 -m pip install -U pyinstaller + +# Building translations +cd "LogarithmPlotter/i18n/" +bash release.sh +cd ../../ pyinstaller --add-data "LogarithmPlotter/qml:qml" \ --add-data "LogarithmPlotter/i18n:i18n" \ - --add-data "../../LICENSE.md:." \ - --add-data "../../assets/native/mac/logarithmplotterfile.icns:." \ - --add-data "../../README.md:." \ + --add-data "LICENSE.md:." \ + --add-data "mac/logarithmplotterfile.icns:." \ + --add-data "README.md:." \ --exclude-module "FixTk" \ --exclude-module "tcl" \ --exclude-module "tk" \ @@ -38,19 +24,9 @@ pyinstaller --add-data "LogarithmPlotter/qml:qml" \ --exclude-module "Tkinter" \ --noconsole \ --noconfirm \ - --icon=../../assets/native/mac/logarithmplotter.icns \ + --icon=mac/logarithmplotter.icns \ --osx-bundle-identifier eu.ad5001.LogarithmPlotter \ -n LogarithmPlotter \ LogarithmPlotter/logarithmplotter.py -cp ../../assets/native/mac/Info.plist dist/LogarithmPlotter.app/Contents/Info.plist - -# Remove QtWebEngine, 3D and all other unused libs libs -rm -rf dist/LogarithmPlotter.app/Contents/MacOS/{QtWeb*,*3D*,QtRemote*,QtPdf,QtCharts,QtLocation,QtTest,QtMultimedia,QtSpatialAudio,QtDataVisualization,QtQuickParticles,QtChartsQml,QtScxml,QtDataVisualizationQml,QtTest,QtPositioningQuick,QtQuickTest,QtSql,QtSensorsQuick} -rm -rf dist/LogarithmPlotter.app/Contents/MacOS/PySide6/QtNetwork.abi3.so - -# Removing QtQuick3D -rm -rf dist/LogarithmPlotter.app/Contents/MacOS/PySide6/Qt/qml/{QtQuick3D,Qt3D,QtWebEngine} - -# Remove the QtQuick styles that are unused -rm -rf dist/LogarithmPlotter.app/Contents/MacOS/PySide6/Qt/qml/QtQuick/Controls/{Imagine,Material,iOS,Universal,designer} +cp mac/Info.plist dist/LogarithmPlotter.app/Contents/Info.plist diff --git a/scripts/build-windows.bat b/scripts/build-windows.bat new file mode 100644 index 0000000..a29d291 --- /dev/null +++ b/scripts/build-windows.bat @@ -0,0 +1,9 @@ +rem Need pyinstaller >= 4.3, or there is an issue regarding the PATH of the building. +python -m pip install -U pyinstaller>=4.3 + +rem Building translations +cd "LogarithmPlotter\i18n" +cmd release.sh +cd ..\.. + +pyinstaller --add-data "logplotter.svg;." --add-data "LogarithmPlotter/qml;qml" --add-data "LogarithmPlotter/i18n;i18n" --noconsole LogarithmPlotter/logarithmplotter.py --icon=win/logarithmplotter.ico -n logarithmplotter diff --git a/scripts/build-wine.sh b/scripts/build-wine.sh index fa78bb1..3406bbc 100644 --- a/scripts/build-wine.sh +++ b/scripts/build-wine.sh @@ -1,52 +1,14 @@ #!/bin/bash -cd "$(dirname "$(readlink -f "$0" || realpath "$0")")/.." || exit - -rebuild=true - -while [ $# -gt 0 ]; do - case "$1" in - --no-rebuild) - rebuild=false - ;; - *) - box "Error: Invalid argument." - exit 1 - esac - shift -done - -if [ "$rebuild" == "true" ]; then - rm -rf build - bash scripts/build.sh -fi - -cd build/runtime-pyside6 || exit 1 +cd "$(dirname "$(readlink -f "$0" || realpath "$0")")/.." +# Giving pyinstaller another run +rm $(find . -name "*.qmlc") rm -rf $(find . -name "*.pyc") +wine python -m pip install -U pyinstaller -wine pyinstaller --add-data "LogarithmPlotter/logarithmplotter.svg;." \ - --add-data "LogarithmPlotter/qml;qml" \ - --add-data "LogarithmPlotter/i18n;i18n" \ - --noconsole \ - LogarithmPlotter/logarithmplotter.py \ - --icon=../../assets/native/win/logarithmplotter.ico \ - -n logarithmplotter +# Building translations +cd "LogarithmPlotter/i18n/" +bash release.sh +cd ../../ -# Copy Qt6ShaderTools, a required library for for Qt5Compat -PYSIDE6PATH="$(wine python -c "import PySide6; from os import path; print(path.dirname(PySide6.__file__));")" -# Converting PySide6 path to absolute path -DRIVEC="${WINEPREFIX:-$HOME/.wine}/drive_c" -PYSIDE6PATH="${PYSIDE6PATH%$'\r'}" -PYSIDE6PATH="${PYSIDE6PATH//\\/\/}" -PYSIDE6PATH="${PYSIDE6PATH//C:/$DRIVEC}" -cp "$PYSIDE6PATH/Qt6ShaderTools.dll" dist/logarithmplotter/_internal/PySide6/ - -# Remove QtWebEngine -rm dist/logarithmplotter/_internal/PySide6/Qt6WebEngineCore.dll - -# Remove the QtQuick styles that are unused -rm -rf dist/logarithmplotter/_internal/PySide6/qml/QtQuick/Controls/{Imagine,Material,designer} - -# Remove unused tools -rm -r dist/logarithmplotter/_internal/PySide6/qml/{Qt3D,QtQuick3D} -rm dist/logarithmplotter/_internal/PySide6/Qt6{Pdf.dll,*3D*,Location.dll} +wine pyinstaller --add-data "logplotter.svg;." --add-data "LogarithmPlotter/qml;qml" --add-data "LogarithmPlotter/i18n;i18n" --noconsole LogarithmPlotter/logarithmplotter.py --icon=win/logarithmplotter.ico -n logarithmplotter diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index 8d170bf..0000000 --- a/scripts/build.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash -# -# LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. -# Copyright (C) 2021-2025 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 . -# - -# This script builds a dist version of LogarithmPlotter - -DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd "$DIR/.." || exit 1 - -BUILD_DIR="build/runtime-pyside6" -BUILD_QML_DIR="$BUILD_DIR/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter" - - -box() { - len=${#1} - echo "┌─$(printf '─%.0s' $(seq 1 "$len"))─┐" - echo "│ $1 │" - echo "└─$(printf '─%.0s' $(seq 1 "$len"))─┘" -} - -rm -rf build -mkdir -p "$BUILD_DIR" - -# Copy python -box "Copying pyside6 python runtime..." -cp -r runtime-pyside6/{setup.py,LogarithmPlotter} "$BUILD_DIR" - -box "Building ecmascript modules..." -mkdir -p "$BUILD_QML_DIR/js" -cd common && \ - (npm run build || exit) && \ - cd .. - -box "Building translations..." -cd assets/i18n/ && (bash release.sh || exit) && cd ../../ -mkdir -p "$BUILD_DIR/LogarithmPlotter/i18n" && cp assets/i18n/*.qm "$BUILD_DIR/LogarithmPlotter/i18n/" - -box "Building icons..." -cp -r assets/icons "$BUILD_QML_DIR" -cp assets/logarithmplotter.svg "$BUILD_QML_DIR/icons/" -cp assets/logarithmplotter.svg "$BUILD_DIR/LogarithmPlotter/" diff --git a/scripts/generate_manuals.php b/scripts/generate_manuals.php index 615f988..ec357dc 100644 --- a/scripts/generate_manuals.php +++ b/scripts/generate_manuals.php @@ -1,7 +1,7 @@ +# Copyright (C) 2022 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 diff --git a/scripts/package-deb.sh b/scripts/package-deb.sh deleted file mode 100755 index e01c370..0000000 --- a/scripts/package-deb.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash -cd "$(dirname "$(readlink -f "$0" || realpath "$0")")/.." || exit 1 - -rebuild=true - -while [ $# -gt 0 ]; do - case "$1" in - --no-rebuild) - rebuild=false - ;; - *) - box "Error: Invalid argument." - exit 1 - esac - shift -done - -if [ "$rebuild" == "true" ]; then - rm -rf build - bash scripts/build.sh -fi - -cd build/runtime-pyside6 || exit 1 - -mkdir assets -cp -r ../../assets/{native,*.svg} assets/ -cp ../../README.md . - -# Build for noble -python3 setup.py --remove-git-version --command-packages=stdeb.command sdist_dsc \ - --package logarithmplotter --copyright-file assets/native/linux/debian/copyright \ - --suite noble --depends3 "$(cat assets/native/linux/debian/depends.wheels)" --section science \ - --debian-version +wheels-1 bdist_deb - -mv deb_dist deb_dist.noble - -# Build for oracular (different dependencies) -python3 setup.py --remove-git-version --command-packages=stdeb.command sdist_dsc \ - --package logarithmplotter --copyright-file assets/native/linux/debian/copyright \ - --suite oracular --depends3 "$(cat assets/native/linux/debian/depends.packaged)" --section science \ - --debian-version +packaged-1 bdist_deb - -mv deb_dist deb_dist.oracular diff --git a/scripts/package-linux.sh b/scripts/package-linux.sh new file mode 100755 index 0000000..9f8db9e --- /dev/null +++ b/scripts/package-linux.sh @@ -0,0 +1,30 @@ +#!/bin/bash +cd "$(dirname "$(readlink -f "$0" || realpath "$0")")/.." + +# Building translations +cd "LogarithmPlotter/i18n/" +bash release.sh +cd ../../ + +# Deb +sudo python3 setup.py --remove-git-version --command-packages=stdeb.command sdist_dsc \ + --package logarithmplotter --copyright-file linux/debian/copyright --suite impish --depends3 "$(cat linux/debian/depends)" --section science \ + bdist_deb + +# Flatpak building +FLATPAK_BUILDER=$(which flatpak-builder) +if [ -z $FLATPAK_BUILDER ]; then + echo "flatpak-builder not installed. Will not proceed to build flatpak." +else + cd linux/flatpak + flatpak-builder AppDir eu.ad5001.LogarithmPlotter.json --user --force-clean --install + cd ../../ +fi + +# Snapcraft building +SNAPCRAFT=$(which snapcraft) +if [ -z $SNAPCRAFT ]; then + echo "snapcraft not installed. Will not proceed to build snap" +else + snapcraft +fi diff --git a/scripts/package-macosx.sh b/scripts/package-macosx.sh index c050a32..070a164 100644 --- a/scripts/package-macosx.sh +++ b/scripts/package-macosx.sh @@ -1,24 +1,25 @@ #!/usr/bin/env bash -cd "$(dirname "$(readlink -f "$0" || realpath "$0")")/../build/runtime-pyside6/dist" || exit 1 +cd "$(dirname "$(readlink -f "$0" || realpath "$0")")/.." -VERSION=0.6.0 +VERSION=0.2.0 title="LogarithmPlotter v${VERSION} Setup" finalDMGName="LogarithmPlotter-v${VERSION}-setup.dmg" applicationName=LogarithmPlotter backgroundPictureName=logarithmplotter-installer-background.png source=Installer +cd dist rm -rf Installer mkdir -p Installer mkdir -p Installer/.background -cp ../../../assets/native/mac/install-bg.png "./Installer/.background/${backgroundPictureName}" +cp ../mac/install-bg.png "./Installer/.background/${backgroundPictureName}" cp -r LogarithmPlotter.app Installer/LogarithmPlotter.app -cp ../../../LICENSE.md Installer/LICENSE.md -cp ../../../README.md Installer/README.md +cp ../LICENSE.md Installer/LICENSE.md +cp ../README.md Installer/README.md # Calculating folder size duoutput=$(du -h Installer | tail -n1) -size=$(( ${duoutput%M*} + 2)) # +2 for allowing small space to edit. +size=$(expr ${duoutput%M*} + 2) # +2 for allowing small space to edit. echo "Creating DMG file with size ${size}M." # Adapted from https://stackoverflow.com/a/1513578 @@ -26,7 +27,7 @@ hdiutil create -srcfolder "${source}" -volname "${title}" -fs HFS+ \ -fsargs "-c c=64,a=16,e=16" -format UDRW -size ${size}M pack.temp.dmg device=$(hdiutil attach -readwrite -noverify -noautoopen "pack.temp.dmg" | \ - grep -E '^/dev/' | sed 1q | awk '{print $1}') + egrep '^/dev/' | sed 1q | awk '{print $1}') sleep 3 @@ -53,10 +54,10 @@ echo ' end tell end tell ' | osascript -chmod -Rf go-w "/Volumes/${title}" +chmod -Rf go-w /Volumes/"${title}" sync sync -hdiutil detach "${device}" +hdiutil detach ${device} hdiutil convert "pack.temp.dmg" -format UDZO -imagekey zlib-level=9 -o "${finalDMGName}" rm -f pack.temp.dmg rm -rf Installer diff --git a/scripts/package-windows.bat b/scripts/package-windows.bat new file mode 100644 index 0000000..777f2f9 --- /dev/null +++ b/scripts/package-windows.bat @@ -0,0 +1,7 @@ +XCOPY win\*.* dist\logarithmplotter /C /S /D /Y /I +XCOPY README.md dist\logarithmplotter /C /D /Y +XCOPY LICENSE.md dist\logarithmplotter /C /D /Y +rem Creating installer +cd dist\logarithmplotter +"C:\Program Files (x86)\NSIS\makensis" installer.nsi +cd ..\.. diff --git a/scripts/package-wine.sh b/scripts/package-wine.sh index 89295e7..98209e0 100644 --- a/scripts/package-wine.sh +++ b/scripts/package-wine.sh @@ -1,8 +1,8 @@ #!/bin/bash -cd "$(dirname "$(readlink -f "$0" || realpath "$0")")/.." || exit 1 +cd "$(dirname "$(readlink -f "$0" || realpath "$0")")/.." # Moving files -cp assets/native/win/* README.md LICENSE.md build/runtime-pyside6/dist/logarithmplotter/ +cp win/* README.md LICENSE.md dist/logarithmplotter/ # Creating installer -cd build/runtime-pyside6/dist/logarithmplotter/ || exit 1 +cd dist/logarithmplotter/ makensis installer.nsi diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh deleted file mode 100644 index 3f73e37..0000000 --- a/scripts/run-tests.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -cd "$(dirname "$(readlink -f "$0" || realpath "$0")")/.." || exit 1 - -box() { - len=${#1} - echo "┌─$(printf '─%.0s' $(seq 1 "$len"))─┐" - echo "│ $1 │" - echo "└─$(printf '─%.0s' $(seq 1 "$len"))─┘" -} - -rebuild=true - -cd runtime-pyside6/tests/plugins || exit 1 -box "Testing pytest natural plugins..." -PYTHONPATH="$PYTHONPATH:." pytest --cov=natural --cov-report term-missing . || exit 1 -cd ../../../ - -while [ $# -gt 0 ]; do - case "$1" in - --no-rebuild) - rebuild=false - ;; - *) - box "Error: Invalid argument." - exit 1 - esac - shift -done - -if [ "$rebuild" == "true" ]; then - rm -rf build - bash scripts/build.sh -fi - - - -# Run python tests -rm -rf build/runtime-pyside6/tests -cp -r runtime-pyside6/tests build/runtime-pyside6 -cp -r ci CHANGELOG.md build/runtime-pyside6 -cd build/runtime-pyside6 || exit 1 -box "Testing runtime-pyside6..." -PYTHONPATH="$PYTHONPATH:." pytest --cov=LogarithmPlotter --cov-report term-missing . || exit 1 -cd ../../ - -# Run js tests -cd common || exit 1 -box "Testing common..." -npm test || exit 1 - diff --git a/scripts/sign-deb.sh b/scripts/sign-deb.sh index 05bee26..6939243 100755 --- a/scripts/sign-deb.sh +++ b/scripts/sign-deb.sh @@ -1,26 +1,24 @@ #!/bin/bash # This script is used to sign the LogarithmPlotter deb directly from it's DSC file. # Adapted from https://github.com/astraw/stdeb/issues/181 -cd "$(dirname "$(readlink -f "$0" || realpath "$0")")/../build/runtime-pyside6/" || exit 1 PPA_ARCHIVE="ppa:ad5001/logarithmplotter" -for dist in `echo noble oracular`; do - echo "Signing $dist deb..." - # create a temporary folder - mkdir "deb_dist.$dist/tmp" -p - cd "deb_dist.$dist/tmp" || exit 1 - rm -rf * +cd ../deb_dist - # DSC file variables - dsc_file="$(find ../ -regextype sed -regex ".*/*.dsc" | cut -c 4-)" - source_package_name="$(echo $dsc_file | cut -c -$(expr ${#dsc_file} - 4))" +# create a temporary folder +mkdir tmp -p +cd tmp +rm -rf * - # extract and sign the files - dpkg-source -x "../$dsc_file" - cd "$(find . -type d | head -n 2 | tail -n 1 | cut -c 3-)" # go to the (only) directory. - debuild -S -sa -k"mail@ad5001.eu" +# DSC file variables +dsc_file="$(find ../ -regextype sed -regex ".*/*.dsc" | cut -c 4-)" +source_package_name="$(echo $dsc_file | cut -c -$(expr ${#dsc_file} - 4))" - # upload package to my PPA - dput $PPA_ARCHIVE "../${source_package_name}_source.changes" -done +# extract and sign the files +dpkg-source -x "../$dsc_file" +cd "$(find . -type d | head -n 2 | tail -n 1 | cut -c 3-)" # go to the (only) directory. +debuild -S -sa -k"mail@ad5001.eu" + +# upload package to my PPA +dput $PPA_ARCHIVE "../${source_package_name}_source.changes" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e3fb347 --- /dev/null +++ b/setup.py @@ -0,0 +1,161 @@ +""" + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2022 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 . +""" + +import setuptools +import os +import sys +from shutil import copyfile + +print(sys.argv) + +current_dir = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + +if "PREFIX" not in os.environ and sys.platform == 'linux': + from getopt import getopt + optlist, args = getopt(sys.argv, '', ['prefix=', 'root=']) + for arg,value in optlist: + if arg == "prefix": + os.environ["PREFIX"] = value + if "PREFIX" not in os.environ and sys.platform == 'linux': + if "XDG_DATA_HOME" in os.environ: + os.environ["PREFIX"] = os.environ["XDG_DATA_HOME"] + else: + try: + # Checking if we have permission to write to root. + from os import makedirs, rmdir + makedirs("/usr/share/applications/test") + rmdir("/usr/share/applications/test") + os.environ["PREFIX"] = "/usr/share" + except: + if ".pybuild" in os.environ["HOME"]: # Launchpad building. + os.environ["PREFIX"] = "share" + else: + os.environ["PREFIX"] = os.environ["HOME"] + "/.local/share" + +from LogarithmPlotter import __VERSION__ as pkg_version + +if "--remove-git-version" in sys.argv: + pkg_version = pkg_version.split(".dev0")[0] + sys.argv.remove("--remove-git-version") + +CLASSIFIERS = """ +Environment :: Graphic +Environment :: X11 Applications :: Qt +License :: OSI Approved :: GNU General Public License v3 (GPLv3) +Natural Language :: English +Development Status :: 3 - Alpha +Operating System :: MacOS :: MacOS X +Operating System :: Microsoft :: Windows +Operating System :: POSIX +Operating System :: POSIX :: BSD +Operating System :: POSIX :: Linux +Programming Language :: Python :: 3.8 +Programming Language :: Python :: 3.9 +Programming Language :: Python :: Implementation :: CPython +Topic :: Utilities +Topic :: Scientific/Engineering +""".strip().splitlines() + +def read_file(file_name): + f = open(file_name, 'r', -1) + data = f.read() + f.close() + return data + +def package_data(): + pkg_data = ["logarithmplotter.svg"] + for d,folders,files in os.walk("LogarithmPlotter/qml"): + d = d[17:] + pkg_data += [os.path.join(d, f) for f in files] + for d,folders,files in os.walk("LogarithmPlotter/i18n"): + d = d[17:] + pkg_data += [os.path.join(d, f) for f in files] + if "FLATPAK_INSTALL" in os.environ: + pkg_data += ["CHANGELOG.md"] + + return pkg_data + +data_files = [] +if sys.platform == 'linux': + data_files.append(('share/applications/', ['linux/logarithmplotter.desktop'])) + data_files.append(('share/mime/packages/', ['linux/x-logarithm-plot.xml'])) + data_files.append(('share/icons/hicolor/scalable/mimetypes/', ['linux/application-x-logarithm-plot.svg'])) + data_files.append(('share/icons/hicolor/scalable/apps/', ['logplotter.svg'])) + data_files.append((os.environ["PREFIX"] + '/applications/', ['linux/logarithmplotter.desktop'])) + data_files.append((os.environ["PREFIX"] + '/mime/packages/', ['linux/x-logarithm-plot.xml'])) + data_files.append((os.environ["PREFIX"] + '/icons/hicolor/scalable/mimetypes/', ['linux/application-x-logarithm-plot.svg'])) + data_files.append((os.environ["PREFIX"] + '/icons/hicolor/scalable/apps/', ['logplotter.svg'])) + if len(sys.argv) > 1: + if sys.argv[1] == "install": + os.makedirs(os.environ["PREFIX"] + '/applications/', exist_ok=True) + os.makedirs(os.environ["PREFIX"] + '/mime/packages/', exist_ok=True) + os.makedirs(os.environ["PREFIX"] + '/icons/hicolor/scalable/mimetypes/', exist_ok=True) + os.makedirs(os.environ["PREFIX"] + '/icons/hicolor/scalable/apps/', exist_ok=True) + os.makedirs(os.environ["PREFIX"] + '/metainfo/', exist_ok=True) + copyfile(current_dir + '/linux/x-logarithm-plot.xml', os.environ["PREFIX"] + '/mime/packages/x-logarithm-plot.xml') + copyfile(current_dir + '/linux/application-x-logarithm-plot.svg', + os.environ["PREFIX"] + '/icons/hicolor/scalable/mimetypes/application-x-logarithm-plot.svg') + copyfile(current_dir + '/logplotter.svg', os.environ["PREFIX"] + '/icons/hicolor/scalable/apps/logplotter.svg') + if "FLATPAK_INSTALL" in os.environ: + copyfile(current_dir + '/linux/eu.ad5001.LogarithmPlotter.metainfo.flatpak.xml', + os.environ["PREFIX"] + '/metainfo/eu.ad5001.LogarithmPlotter.metainfo.xml') + copyfile(current_dir + '/linux/flatpak/logarithmplotter.desktop', + os.environ["PREFIX"] + '/applications/logarithmplotter.desktop') + else: + copyfile(current_dir + '/linux/eu.ad5001.LogarithmPlotter.metainfo.xml', + os.environ["PREFIX"] + '/metainfo/eu.ad5001.LogarithmPlotter.metainfo.xml') + #copyfile(current_dir + '/linux/logarithmplotter.desktop', os.environ["PREFIX"] + '/applications/logarithmplotter.desktop') + elif sys.argv[1] == "uninstall": + os.remove(os.environ["PREFIX"] + '/applications/logarithmplotter.desktop') + os.remove(os.environ["PREFIX"] + '/mime/packages/x-logarithm-plot.xml') + os.remove(os.environ["PREFIX"] + '/icons/hicolor/scalable/mimetypes/application-x-logarithm-plot.svg') + os.remove(os.environ["PREFIX"] + '/icons/hicolor/scalable/apps/logplotter.svg') + +setuptools.setup( + install_requires=([] if "FLATPAK_INSTALL" in os.environ else ["PySide2"]), + python_requires='>=3.8', + + name='logarithmplotter', + version=pkg_version, + + description='2D plotter software to make BODE plots, sequences and repartition functions.', + long_description=read_file("README.md"), + keywords='logarithm plotter graph creator bode diagram', + + author='Ad5001', + author_email='mail@ad5001.eu', + + license=('GPLv3'), + url='https://apps.ad5001.eu/logarithmplotter/', + + classifiers=CLASSIFIERS, + zip_safe=False, + packages=["LogarithmPlotter", "LogarithmPlotter.util"], + + package_data={ + 'LogarithmPlotter':package_data(), + }, + include_package_data=True, + data_files = data_files, + entry_points={ + 'console_scripts': [ + 'logarithmplotter = LogarithmPlotter.logarithmplotter:run', + ], + } +) + diff --git a/snapcraft.yaml b/snapcraft.yaml index 12b41d2..9124698 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -1,18 +1,23 @@ name: logarithmplotter title: LogarithmPlotter -version: '0.6.0' -summary: Create and edit Bode plots +version: '0.2.0' +summary: 2D logarithmic-scaled plotter software to create asymptotic Bode plots confinement: strict -base: core22 +base: core20 grade: stable -icon: assets/logarithmplotter.svg +icon: LogarithmPlotter/logarithmplotter.svg adopt-info: linuxfiles license: GPL-3.0+ architectures: - - amd64 + - build-on: amd64 + run-on: amd64 plugs: + #gnome-3-38-2004: + # interface: content + # target: gnome-platform + # default-provider: gnome-3-38-2004:gnome-3-38-2004 gtk-3-themes: default-provider: gtk-common-themes:gtk-3-themes interface: content @@ -57,31 +62,26 @@ parts: # - fcitx-frontend-gtk3 # - libgtk2.0-0 launchers: - source: assets/native/linux/snapcraft/launcher/ + source: linux/snapcraft/launcher/ plugin: dump organize: '*': bin/ linuxfiles: - source: assets/native/linux/ + source: linux/ plugin: dump parse-info: [eu.ad5001.LogarithmPlotter.metainfo.xml] organize: logarithmplotter.desktop: usr/share/applications/logarithmplotter.desktop x-logarithm-plot.xml: usr/share/mime/packages/x-logarithm-plot.xml application-x-logarithm-plot.svg: usr/share/mime/packages/application-x-logarithm-plot.svg - filetypeicon: - source: assets/ - plugin: dump - organize: - logplotterfile.svg: usr/share/mime/packages/application-x-logarithm-plot.svg logarithmplotter: plugin: python - source: build + source: . stage-packages: - breeze-icon-theme # Latex dependencies - #- texlive-latex-base Causes symlink issues and being rejected by the Snap store. - #- dvipng + - texlive-latex-base + - dvipng # Additional dependencies - libxcomposite1 - libxcursor1 @@ -129,7 +129,6 @@ parts: - libxcb-xfixes0 - libxcb-xinerama0 - libxcb-xkb1 - - libxcb-cursor0 - libxkbcommon-x11-0 - libxkbcommon0 - libxcb-render-util0 @@ -150,7 +149,7 @@ parts: source: . plugin: dump organize: - CHANGELOG.md: lib/python3.12/site-packages/LogarithmPlotter/util/ + CHANGELOG.md: lib/python3.8/site-packages/LogarithmPlotter/CHANGELOG.md apps: logarithmplotter: @@ -166,6 +165,7 @@ apps: - desktop-legacy - wayland - x11 + #- gnome-3-38-2004 - gsettings # Theme access for wayland - home # Storing configuration. - opengl # Rendering diff --git a/assets/native/win/inst_banner.bmp b/win/inst_banner.bmp similarity index 100% rename from assets/native/win/inst_banner.bmp rename to win/inst_banner.bmp diff --git a/assets/native/win/installer.nsi b/win/installer.nsi similarity index 94% rename from assets/native/win/installer.nsi rename to win/installer.nsi index bf38199..1d2c260 100644 --- a/assets/native/win/installer.nsi +++ b/win/installer.nsi @@ -11,10 +11,10 @@ Unicode True !define PROG_ID "LogarithmPlotter.File.1" !define DEV_NAME "Ad5001" !define WEBSITE "https://apps.ad5001.eu/logarithmplotter" -!define VERSION_SHORT "0.6.0" +!define VERSION_SHORT "0.2.0" !define APP_VERSION "${VERSION_SHORT}.0" -!define COPYRIGHT "Ad5001 (c) 2021-2025" -!define DESCRIPTION "Create graphs with logarithmic scales." +!define COPYRIGHT "Ad5001 (c) 2022" +!define DESCRIPTION "Create graphs with logarithm scales." !define REG_UNINSTALL "Software\Microsoft\Windows\CurrentVersion\Uninstall\LogarithmPlotter" !define REG_APPPATHS "Software\Microsoft\Windows\CurrentVersion\App Paths\logarithmplotter.exe" @@ -50,7 +50,7 @@ VIAddVersionKey "FileVersion" "${APP_VERSION}" !define MUI_PAGE_HEADER_SUBTEXT "${COPYRIGHT}" !define MUI_WELCOMEPAGE_TITLE "Install ${APP_NAME} v${VERSION_SHORT}" -!define MUI_WELCOMEPAGE_TEXT "Thank you for downloading ${APP_NAME}! Follow the steps provided by this installer to install ${APP_NAME}." +!define MUI_WELCOMEPAGE_TEXT "Welcome to the ${APP_NAME} installer! Follow the steps provided by this installer to install ${APP_NAME}" !define MUI_HEADERIMAGE_RIGHT ;Extra space for the title area ;!insertmacro MUI_WELCOMEPAGE_TITLE_3LINES @@ -89,7 +89,7 @@ Icon "logarithmplotter.ico" ;!define MUI_DIRECTORYPAGE_VARIABLE $INSTDIR !define MUI_INSTFILESPAGE_FINISHHEADER_TEXT "Success!" -!define MUI_INSTFILESPAGE_FINISHHEADER_SUBTEXT "${APP_NAME} v${VERSION_SHORT} was installed on your computer." +!define MUI_INSTFILESPAGE_FINISHHEADER_SUBTEXT "${APP_NAME} v${VERSION_SHORT} was installed on your computer" !define MUI_INSTFILESPAGE_ABORTHEADER_TEXT "There was an error during the installation process." !define MUI_INSTFILESPAGE_ABORTHEADER_SUBTEXT "${APP_NAME} v${VERSION_SHORT} was not installed on your computer." @@ -155,10 +155,16 @@ Icon "logarithmplotter.ico" Section "" SetOutPath $INSTDIR File logarithmplotter.exe + File *.dll + File *.pyd File *.md + ;File *.manifest + File *.zip File *.bmp File *.ico - File /r _internal + File /r qml + File /r PySide2 + File /r shiboken2 CreateShortcut "$SMPROGRAMS\LogarithmPlotter.lnk" "$INSTDIR\logarithmplotter.exe" diff --git a/win/logarithmplotter.ico b/win/logarithmplotter.ico new file mode 100644 index 0000000..f975ab5 Binary files /dev/null and b/win/logarithmplotter.ico differ