diff --git a/.gitignore b/.gitignore index e2308a5..8802201 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,13 @@ -# 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 @@ -26,21 +20,17 @@ assets/linux/flatpak/.flatpak-builder .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..df81e42 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[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 diff --git a/CHANGELOG.md b/CHANGELOG.md index c4b7c6d..be8eec8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## v0.5.0 (11 Jan 2024) +## v0.5.0 (11 Jan 2023) **New** diff --git a/runtime-pyside6/LogarithmPlotter/__init__.py b/LogarithmPlotter/__init__.py similarity index 86% rename from runtime-pyside6/LogarithmPlotter/__init__.py rename to LogarithmPlotter/__init__.py index 08914d3..4b060a5 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,8 +17,9 @@ """ from shutil import which -__VERSION__ = "0.6.0" -is_release = False +__VERSION__ = "0.5.0" +is_release = True + # Check if development version, if so get the date of the latest git patch # and append it to the version string. @@ -26,10 +27,9 @@ 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/common/.mocharc.jsonc b/LogarithmPlotter/__main__.py similarity index 87% rename from common/.mocharc.jsonc rename to LogarithmPlotter/__main__.py index 56a6b31..6c86e82 100644 --- a/common/.mocharc.jsonc +++ b/LogarithmPlotter/__main__.py @@ -1,25 +1,20 @@ -/** +""" * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. * Copyright (C) 2021-2024 Ad5001 - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - */ - -{ - "recursive": true, - "require": [ - "esm", - "./test/hooks.mjs" - ] -} +""" +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..0bb06bf --- /dev/null +++ b/LogarithmPlotter/i18n/lp_de.ts @@ -0,0 +1,1692 @@ + + + + + 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 + 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 + &Changelog + + + + &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 + + + + Changelog + + + Fetching changelog... + Changelog abrufen… + + + + Done + 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 + Funktion + + + + 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 + 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 + + + + 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 + + + + 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) + + + + 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 + + + + 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 + + + + Translations included + Einschließlich Übersetzungen + + + + Improve + Verbessern + + + + 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. + + + + 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. + + + + 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 + + + + 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/. + 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. + + + + 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. + + + + 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 + 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:{}. + + + + usage + + + + Usage: %1 + Verwendung: %1 + + + + + + Usage: %1 or +%2 + Verwendung: %1 oder +%2 + + + + integral(<from: number>, <to: number>, <f: ExecutableObject>) + integral(<von: Zahl>, <bis: Zahl>, <f: ExecutableObject>) + + + + 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: ExecutableObject>, <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/LogarithmPlotter/i18n/lp_en.ts b/LogarithmPlotter/i18n/lp_en.ts new file mode 100644 index 0000000..91b0e51 --- /dev/null +++ b/LogarithmPlotter/i18n/lp_en.ts @@ -0,0 +1,1692 @@ + + + + + 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 + + + + 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 + + + + Changelog + + + Fetching changelog... + Fetching changelog… + + + + Done + Done + + + + 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 + + + + 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 + + + + 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 + + + + 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) + + + + 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 + + + + 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 + + + + Translations included + Translations included + + + + Improve + Improve + + + + 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. + + + + 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. + + + + 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 + + + + 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 + + + + 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 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: {}. + + + + usage + + + + Usage: %1 + Usage: %1 + + + + + + Usage: %1 or +%2 + Usage: %1 or +%2 + + + + integral(<from: number>, <to: number>, <f: ExecutableObject>) + integral(<from: number>, <to: number>, <f: ExecutableObject>) + + + + 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: ExecutableObject>, <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/LogarithmPlotter/i18n/lp_es.ts b/LogarithmPlotter/i18n/lp_es.ts new file mode 100644 index 0000000..11bbbc5 --- /dev/null +++ b/LogarithmPlotter/i18n/lp_es.ts @@ -0,0 +1,1578 @@ + + + + + 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 + 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 + + + + Expression editor + + + + + Automatically close parenthesises and brackets + + + + + Enable syntax highlighting + + + + + Enable autocompletion + + + + + Color Scheme + + + + + &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? + + + + + BaseDialog + + + Close + + + + + Changelog + + + Fetching changelog... + + + + + Done + + + + + 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 + + + + + Label content + + + + + 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 + + + + + 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 + Activar el renderizado de LaTeX + + + + Automatically close parenthesises and brackets in expressions + + + + + Enable syntax highlighting for expressions + + + + + Enable autocompletion interface in expression editor + + + + + Color scheme: + + + + + 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 + + + + + ObjectRow + + + 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: + + + + + Pick X + + + + + Pick Y + + + + + Open picker settings + + + + + Hide picker settings + + + + + (no pick selected) + + + + + 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 + + + + + Translations included + + + + + Improve + + + + + 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. + + + + + error + + + + 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). + + + + + Unknown character "%1". + + + + + + Illegal escape sequence: %1. + + + + + + Parse error [%1:%2]: %3 + + + + + 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. + + + + + + First argument to map is not a function. + + + + + + Second argument to map is not an array. + + + + + + First argument to fold is not a function. + + + + + + Second argument to fold is not an array. + + + + + + + First argument to filter is not a function. + + + + + + + Second argument to filter is not an array. + + + + + + Second argument to indexOf is not a string or array. + + + + + + Second argument to join is not an array. + + + + + EOF + + + + + 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. + + + + + expression + + + + LogarithmPlotter - Parsing error + + + + + Error while parsing expression for property %1: +%2 + +Evaluated expression: %3 + + + + + LogarithmPlotter - Drawing error + + + + + 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 + + + + + position + + + Position of %1 %2 set from "%3" to "%4". + + + + + Position of %1 set from %2 to %3. + + + + + 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: {}. + + + + + usage + + + + Usage: %1 + + + + + + + Usage: %1 or +%2 + + + + + integral(<from: number>, <to: number>, <f: ExecutableObject>) + + + + + 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/LogarithmPlotter/i18n/lp_fr.ts b/LogarithmPlotter/i18n/lp_fr.ts new file mode 100644 index 0000000..aeb408f --- /dev/null +++ b/LogarithmPlotter/i18n/lp_fr.ts @@ -0,0 +1,1701 @@ + + + + + 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 + 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 + &Historique des modifications + + + + &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 + + + + Changelog + + + Fetching changelog... + Récupération de l'historique des modifications… + + + + Done + 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 + 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 + 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 + 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 + + + + 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 + + + + PickLocationOverlay + + + Pointer precision: + Précision du pointeur : + + + Snap to grid + Placement sur la grille + + + + Snap to grid: + Aligner 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é) + + + + 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 + + + + 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 + + + + Translations included + Traductions incluses + + + + Improve + Améliorer + + + + 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. + 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). + + + + 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. + + + + 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 + + + + 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 + + + + 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 + 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. {}. + + + + usage + + + + Usage: %1 + Emploi : %1 + + + + + + Usage: %1 or +%2 + Emploi : %1 ou +%2 + + + + integral(<from: number>, <to: number>, <f: ExecutableObject>) + integral(<de : nombre>, <à : nombre>, <f : Objet exécutable>) + + + + 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 exécutable>, <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/LogarithmPlotter/i18n/lp_hu.ts b/LogarithmPlotter/i18n/lp_hu.ts new file mode 100644 index 0000000..89396ce --- /dev/null +++ b/LogarithmPlotter/i18n/lp_hu.ts @@ -0,0 +1,1684 @@ + + + + + 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... + &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 + 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 + + + + Changelog + + + Fetching changelog... + Változásnapló lekérése… + + + + Done + 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ó + + + + Done + Kész + + + + HistoryBrowser + + + Filter... + Szűrő… + + + + 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 + + + + 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 + + + + 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) + + + + 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 + + + + 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 + + + + Translations included + A felhasználói felület nyelvei + + + + Improve + Fejlesztés + + + + 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. + + + + 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ó. + + + + 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 + + + + 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/. + 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. + + + + 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. + + + + 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 + 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: {}. + + + + usage + + + + Usage: %1 + Használat: %1 + + + + + + Usage: %1 or +%2 + Használat: %1 vagy +%2 + + + + integral(<from: number>, <to: number>, <f: ExecutableObject>) + integrál(<alsó korlát: szám>, <felső korlát: szám>, <függvény: végrehajtható objektum>) + + + + integral(<from: number>, <to: number>, <f: string>, <variable: string>) + integrál(<alsó korlát: szám>, <felső korlát: szám>, <függvény: karakterlánc>, <változó: karakterlánc>) + + + + derivative(<f: ExecutableObject>, <x: number>) + derivált(<függvény: Végrehajtható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/LogarithmPlotter/i18n/lp_nb_NO.ts b/LogarithmPlotter/i18n/lp_nb_NO.ts new file mode 100644 index 0000000..18d0f09 --- /dev/null +++ b/LogarithmPlotter/i18n/lp_nb_NO.ts @@ -0,0 +1,1657 @@ + + + + + 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 + + + + + Expression editor + + + + + Automatically close parenthesises and brackets + + + + + Enable syntax highlighting + + + + + Enable autocompletion + + + + + Color Scheme + + + + + &Help + &Hjelp + + + + &Source code + + + + + &Report a bug + + + + + &User manual + + + + + &Changelog + + + + + &Help translating! + + + + + &Thanks + + + + + &About + &Om + + + + Save unsaved changes? + + + + + This plot contains unsaved changes. By doing this, all unsaved data will be lost. Continue? + + + + + BaseDialog + + + Close + + + + + Changelog + + + Fetching changelog... + + + + + Done + + + + + 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 + + + + + An object with the name '%1' already exists. + + + + + 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 + + + + Enable LaTeX rendering + + + + + Automatically close parenthesises and brackets in expressions + + + + + Enable syntax highlighting for expressions + + + + + Enable autocompletion interface in expression editor + + + + + Color scheme: + + + + + 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 + + + + 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 + + + + PickLocationOverlay + + + Pointer precision: + Peker-presisjon: + + + Snap to grid + Fest til rutenett + + + + Snap to grid: + + + + + Pick X + + + + + Pick Y + + + + + Open picker settings + + + + + Hide picker settings + + + + + (no pick selected) + + + + + 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 + + + + + + + + + Website + + + + + Ported to QMLJS by Ad5001 + + + + + Libraries included + + + + + Email + + + + + English + + + + + French + + + + + German + + + + + Hungarian + + + + + + Github + + + + + Norwegian + + + + + Translations included + + + + + Improve + + + + + 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. + + + + + error + + + + 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). + + + + + Unknown character "%1". + + + + + + Illegal escape sequence: %1. + + + + + + Parse error [%1:%2]: %3 + + + + + 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. + + + + + + First argument to map is not a function. + + + + + + Second argument to map is not an array. + + + + + + First argument to fold is not a function. + + + + + + Second argument to fold is not an array. + + + + + + + First argument to filter is not a function. + + + + + + + Second argument to filter is not an array. + + + + + + Second argument to indexOf is not a string or array. + + + + + + Second argument to join is not an array. + + + + + EOF + + + + + 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. + + + + + expression + + + + LogarithmPlotter - Parsing error + + + + + Error while parsing expression for property %1: +%2 + +Evaluated expression: %3 + + + + + LogarithmPlotter - Drawing error + + + + + 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 + + + + position + + + Position of %1 %2 set from "%3" to "%4". + + + + + Position of %1 set from %2 to %3. + + + + + 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: {}. + + + + usage + + + + Usage: %1 + + + + + + + Usage: %1 or +%2 + + + + + integral(<from: number>, <to: number>, <f: ExecutableObject>) + + + + + 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/LogarithmPlotter/i18n/lp_template.ts b/LogarithmPlotter/i18n/lp_template.ts new file mode 100644 index 0000000..2cbbff2 --- /dev/null +++ b/LogarithmPlotter/i18n/lp_template.ts @@ -0,0 +1,1578 @@ + + + + + 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 + + + + + Expression editor + + + + + Automatically close parenthesises and brackets + + + + + Enable syntax highlighting + + + + + Enable autocompletion + + + + + Color Scheme + + + + + &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 + + + + + Changelog + + + Fetching changelog... + + + + + Done + + + + + 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 + + + + + Label content + + + + + 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 + + + + + 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 + + + + + Automatically close parenthesises and brackets in expressions + + + + + Enable syntax highlighting for expressions + + + + + Enable autocompletion interface in expression editor + + + + + Color scheme: + + + + + 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 + + + + + ObjectRow + + + 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: + + + + + Pick X + + + + + Pick Y + + + + + Open picker settings + + + + + Hide picker settings + + + + + (no pick selected) + + + + + 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 + + + + + Translations included + + + + + Improve + + + + + 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. + + + + + error + + + + 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). + + + + + Unknown character "%1". + + + + + + Illegal escape sequence: %1. + + + + + + Parse error [%1:%2]: %3 + + + + + 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. + + + + + + First argument to map is not a function. + + + + + + Second argument to map is not an array. + + + + + + First argument to fold is not a function. + + + + + + Second argument to fold is not an array. + + + + + + + First argument to filter is not a function. + + + + + + + Second argument to filter is not an array. + + + + + + Second argument to indexOf is not a string or array. + + + + + + Second argument to join is not an array. + + + + + EOF + + + + + 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. + + + + + expression + + + + LogarithmPlotter - Parsing error + + + + + Error while parsing expression for property %1: +%2 + +Evaluated expression: %3 + + + + + LogarithmPlotter - Drawing error + + + + + 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 + + + + + position + + + Position of %1 %2 set from "%3" to "%4". + + + + + Position of %1 set from %2 to %3. + + + + + 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: {}. + + + + + usage + + + + Usage: %1 + + + + + + + Usage: %1 or +%2 + + + + + integral(<from: number>, <to: number>, <f: ExecutableObject>) + + + + + 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/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..f6f2cb3 --- /dev/null +++ b/LogarithmPlotter/logarithmplotter.py @@ -0,0 +1,155 @@ +""" + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +""" + +from time import time + +from PySide6.QtWidgets import QApplication +from PySide6.QtQml import QQmlApplicationEngine +from PySide6.QtCore import Qt, QTranslator, QLocale +from PySide6.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": "Fusion", + "gnome": "Basic", + "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(): + + if not 'QT_QUICK_CONTROLS_STYLE' in environ: + environ["QT_QUICK_CONTROLS_STYLE"] = { + "linux": get_linux_theme(), + "freebsd": get_linux_theme(), + "win32": "Universal" if os_release == "10" else "Fusion", + "cygwin": "Fusion", + "darwin": "macOS" + }[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]) + + # Check for LaTeX installation if LaTeX support is enabled + if config.getSetting("enable_latex"): + 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/runtime-pyside6/LogarithmPlotter/logarithmplotter.svg b/LogarithmPlotter/logarithmplotter.svg similarity index 100% rename from runtime-pyside6/LogarithmPlotter/logarithmplotter.svg rename to LogarithmPlotter/logarithmplotter.svg diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml similarity index 51% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/AppMenuBar.qml index d271cb6..7a33bce 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,7 +20,9 @@ import QtQuick import Qt.labs.platform as Native //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 LatexJS /*! @@ -33,6 +35,7 @@ import eu.ad5001.LogarithmPlotter.Common \sa LogarithmPlotter */ MenuBar { + property var settingsMenu: settingsSubMenu Menu { title: qsTr("&File") @@ -41,7 +44,6 @@ MenuBar { shortcut: StandardKey.Open onTriggered: settings.load() icon.name: 'document-open' - icon.color: sysPalette.windowText } Action { @@ -49,14 +51,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 +71,6 @@ MenuBar { } icon.name: 'application-exit' - icon.color: sysPalette.windowText } } @@ -79,31 +79,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,68 +105,160 @@ 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 { + id: settingsSubMenu + title: qsTr("&Settings") + Action { + id: checkForUpdatesMenuSetting + text: qsTr("Check for updates on startup") + checkable: true + checked: Helper.getSettingBool("check_for_updates") + onTriggered: Helper.setSettingBool("check_for_updates", checked) + icon.name: 'update' + icon.color: sysPalette.buttonText + } + + Action { + id: resetRedoStackMenuSetting + text: qsTr("Reset redo stack automaticly") + checkable: true + checked: Helper.getSettingBool("reset_redo_stack") + onTriggered: Helper.setSettingBool("reset_redo_stack", checked) + icon.name: 'timeline' + icon.color: sysPalette.buttonText + } + + Action { + id: enableLatexJSSetting + text: qsTr("Enable LaTeX rendering") + checkable: true + checked: Helper.getSettingBool("enable_latex") + onTriggered: { + Helper.setSettingBool("enable_latex", checked) + LatexJS.enabled = checked + drawCanvas.requestPaint() + } + icon.name: 'Expression' + icon.color: sysPalette.buttonText + } + + Menu { + title: qsTr("Expression editor") + + Action { + id: autocloseFormulaSetting + text: qsTr("Automatically close parenthesises and brackets") + checkable: true + checked: Helper.getSettingBool("expression_editor.autoclose") + onTriggered: { + Helper.setSettingBool("expression_editor.autoclose", checked) + } + icon.name: 'Text' + icon.color: sysPalette.buttonText + } + + Action { + id: colorizeFormulaSetting + text: qsTr("Enable syntax highlighting") + checkable: true + checked: Helper.getSettingBool("expression_editor.colorize") + onTriggered: { + Helper.setSettingBool("expression_editor.colorize", checked) + } + icon.name: 'appearance' + icon.color: sysPalette.buttonText + } + + Action { + id: autocompleteFormulaSetting + text: qsTr("Enable autocompletion") + checkable: true + checked: Helper.getSettingBool("autocompletion.enabled") + onTriggered: { + Helper.setSettingBool("autocompletion.enabled", checked) + } + icon.name: 'label' + icon.color: sysPalette.buttonText + } + + Menu { + id: colorSchemeSetting + title: qsTr("Color Scheme") + property var schemes: ["Breeze Light", "Breeze Dark", "Solarized", "Github Light", "Github Dark", "Nord", "Monokai"] + + Repeater { + model: colorSchemeSetting.schemes + + MenuItem { + text: modelData + checkable: true + checked: Helper.getSettingInt("expression_editor.color_scheme") == index + onTriggered: { + parent.children[Helper.getSettingInt("expression_editor.color_scheme")].checked = false + checked = true + Helper.setSettingInt("expression_editor.color_scheme", index) + } + } + } + } + } + } + Menu { 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 + icon.name: 'about' onTriggered: thanksTo.open() } Action { text: qsTr("&About") shortcut: StandardKey.HelpContents - icon.name: 'help-about' - icon.color: sysPalette.windowText + icon.name: 'about' onTriggered: about.open() } } 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..df9503d --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/History.qml @@ -0,0 +1,221 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick +import QtQml +import QtQuick.Window +import "../js/objects.js" as Objects +import "../js/historylib.js" as HistoryLib +import "../js/history/common.js" as HistoryCommon + +/*! + \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 + HistoryCommon.imageDepth = Screen.devicePixelRatio + } +} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/Browser.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/HistoryBrowser.qml similarity index 66% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/Browser.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/HistoryBrowser.qml index 09b6feb..69ddfdb 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,15 +16,14 @@ * along with this program. If not, see . */ -pragma ComponentBehavior: Bound - import QtQuick.Controls import QtQuick 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,25 +47,12 @@ 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" } @@ -90,22 +76,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 +101,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 +124,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 +150,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 +163,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 79% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/SingleItem.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/History/HistoryItem.qml index fac19e3..041ddfa 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,13 +16,15 @@ * along with this program. If not, see . */ -import QtQuick import QtQuick.Controls +import QtQuick +import Qt5Compat.GraphicalEffects +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 { @@ -145,6 +135,13 @@ Button { 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..64bef06 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml @@ -0,0 +1,493 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick +import Qt.labs.platform as Native +import "js/objects.js" as Objects +import "js/utils.js" as Utils +import "js/mathlib.js" as MathLib + +/*! + \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 = {} + + Native.MessageDialog { + id: drawingErrorDialog + title: qsTranslate("expression", "LogarithmPlotter - Drawing error") + text: "" + function showDialog(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 + 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) + try { + obj.draw(canvas, ctx) + } catch(e) { + // Drawing throws an error. Generally, it's due to a new modification (or the opening of a file) + drawingErrorDialog.showDialog(objType, obj.name, e.message) + history.undo() + } + } + } + ctx.lineWidth = 1 + } + } + + 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*(xpow==1))) + } + } else { + for(var x = 1; x < drawMaxX; x += 1) { + var drawX = x*xaxisstep1 + var txtX = xaxisstepExpr.simplify(x).replace(/^\((.+)\)$/, '$1') + 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).replace(/^\((.+)\)$/, '$1') + var textSize = ctx.measureText(txtY).width + drawVisibleText(ctx, txtY, axisypx-6-textSize, y2px(drawY)+4+(10*(y==0))) + if(y != 0) + drawVisibleText(ctx, '-'+txtY, axisypx-6-textSize-txtMinus, y2px(-drawY)+4) + } + } + ctx.fillStyle = "#FFFFFF" + } + + /*! + \qmlmethod void LogGraphCanvas::drawXLine(var ctx, double x) + Draws an horizontal line at \c x plot coordinate using 2D \c ctx. + */ + function drawXLine(ctx, x) { + if(isVisible(x, ymax)) { + drawLine(ctx, x2px(x), 0, x2px(x), canvasSize.height) + } + } + + /*! + \qmlmethod void LogGraphCanvas::drawXLine(var ctx, double x) + Draws an vertical line at \c y plot coordinate using 2D \c ctx. + */ + function drawYLine(ctx, y) { + if(isVisible(xmin, y)) { + drawLine(ctx, 0, y2px(y), canvasSize.width, y2px(y)) + } + } + + /*! + \qmlmethod void LogGraphCanvas::drawVisibleText(var ctx, string text, double x, double y) + Writes multline \c text onto the canvas using 2D \c ctx. + \note The \c x and \c y properties here are relative to the canvas, not the plot. + */ + function drawVisibleText(ctx, text, x, y) { + if(x > 0 && x < canvasSize.width && y > 0 && y < canvasSize.height) { + text.toString().split("\n").forEach(function(txt, i){ + ctx.fillText(txt, x, y+(canvas.textsize*i)) + }) + } + } + + /*! + \qmlmethod void LogGraphCanvas::drawVisibleImage(var ctx, var image, double x, double y) + Draws an \c image onto the canvas using 2D \c ctx. + \note The \c x, \c y \c width and \c height properties here are relative to the canvas, not the plot. + */ + function drawVisibleImage(ctx, image, x, y, width, height) { + //console.log("Drawing image", isImageLoaded(image), isImageError(image)) + markDirty(Qt.rect(x, y, width, height)); + ctx.drawImage(image, x, y, width, height) + /*if(true || (x > 0 && x < canvasSize.width && y > 0 && y < canvasSize.height)) { + }*/ + } + + /*! + \qmlmethod var LogGraphCanvas::measureText(var ctx, string text) + Measures the wicth and height of a multiline \c text that would be drawn onto the canvas using 2D \c ctx. + Return format: dictionary {"width": width, "height": height} + */ + function measureText(ctx, text) { + let theight = 0 + let twidth = 0 + let defaultHeight = ctx.measureText("M").width // Approximate but good enough! + text.split("\n").forEach(function(txt, i){ + theight += defaultHeight + if(ctx.measureText(txt).width > twidth) twidth = ctx.measureText(txt).width + }) + return {'width': twidth, 'height': theight} + } + + /*! + \qmlmethod double LogGraphCanvas::x2px(double x) + 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::isVisible(double x, double y) + Checks whether a plot point (\c x, \c y) is visible or not on the canvas. + */ + function isVisible(x, y) { + return (x2px(x) >= 0 && x2px(x) <= canvasSize.width) && (y2px(y) >= 0 && y2px(y) <= canvasSize.height) + } + + /*! + \qmlmethod bool LogGraphCanvas::drawLine(var ctx, double x1, double y1, double x2, double y2) + Draws a line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx. + */ + function drawLine(ctx, x1, y1, x2, y2) { + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + } + + /*! + \qmlmethod bool LogGraphCanvas::drawDashedLine2(var ctx, double x1, double y1, double x2, double y2) + Draws a dashed line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx. + */ + function drawDashedLine2(ctx, x1, y1, x2, y2, dashPxSize = 5) { + ctx.setLineDash([dashPxSize, dashPxSize]); + drawLine(ctx, x1, y1, x2, y2) + ctx.setLineDash([]); + } + + /*! + \qmlmethod bool LogGraphCanvas::drawDashedLine(var ctx, double x1, double y1, double x2, double y2) + Draws a dashed line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx. + (Legacy slower method) + */ + function drawDashedLine(ctx, x1, y1, x2, y2, dashPxSize = 10) { + var distance = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)) + var progPerc = dashPxSize/distance + ctx.beginPath(); + ctx.moveTo(x1, y1); + for(var i = 0; i < 1; i += progPerc) { + ctx.lineTo(x1-(x1-x2)*i, y1-(y1-y2)*i) + ctx.moveTo(x1-(x1-x2)*(i+progPerc/2), y1-(y1-y2)*(i+progPerc/2)) + } + ctx.stroke(); + } + + /*! + \qmlmethod var LogGraphCanvas::renderLatexImage(string ltxText, color) + Renders latex markup \c ltxText to an image and loads it. Returns a dictionary with three values: source, width and height. + */ + function renderLatexImage(ltxText, color, callback) { + let [ltxSrc, ltxWidth, ltxHeight] = Latex.render(ltxText, textsize, color).split(",") + let imgData = { + "source": ltxSrc, + "width": parseFloat(ltxWidth), + "height": parseFloat(ltxHeight) + }; + if(!isImageLoaded(ltxSrc) && !isImageLoading(ltxSrc)){ + // Wait until the image is loaded to callback. + loadImage(ltxSrc) + imageLoaders[ltxSrc] = [callback, imgData] + } else { + // Callback directly + callback(canvas, ctx, imgData) + } + } +} diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml new file mode 100644 index 0000000..5b77730 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml @@ -0,0 +1,378 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQml +import QtQuick.Controls +import eu.ad5001.MixedMenu 1.1 +import QtQuick.Layouts 1.12 +import QtQuick +// Auto loading all objects. +import "js/objs/autoload.js" as ALObjects + +import "js/objects.js" as Objects +import "js/math/latex.js" as LatexJS +import eu.ad5001.LogarithmPlotter.History 1.0 +import eu.ad5001.LogarithmPlotter.ObjectLists 1.0 +import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup + +/*! + \qmltype LogarithmPlotter + \inqmlmodule eu.ad5001.LogarithmPlotter + \brief Main window of LogarithmPlotter + + \sa AppMenuBar, History, GreetScreen, Changelog, Alert, ObjectLists, Settings, HistoryBrowser, LogGraphCanvas, PickLocationOverlay. +*/ +ApplicationWindow { + id: root + visible: true + width: 1000 + height: 500 + color: sysPalette.window + title: "LogarithmPlotter " + (settings.saveFilename != "" ? " - " + settings.saveFilename.split('/').pop() : "") + (history.saved ? "" : "*") + + SystemPalette { + id: sysPalette; colorGroup: SystemPalette.Active + + Component.onCompleted: { + // LatexJS initialization. + LatexJS.enabled = Helper.getSettingBool("enable_latex") + LatexJS.Renderer = Latex + LatexJS.defaultColor = sysPalette.windowText + } + } + SystemPalette { id: sysPaletteIn; colorGroup: SystemPalette.Disabled } + + menuBar: appMenu.trueItem + + AppMenuBar {id: appMenu} + + 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 + anchors.bottomMargin: 5 + z: 3 + } + + Item { + id: sidebar + width: 300 + height: parent.height + //y: root.menuBar.height + readonly property bool inPortrait: root.width < root.height + /*modal: true// inPortrait + interactive: inPortrait + position: inPortrait ? 0 : 1 + */ + visible: !inPortrait + + + TabBar { + id: sidebarSelector + width: parent.width + anchors.top: parent.top + TabButton { + text: qsTr("Objects") + icon.name: 'polygon-add-nodes' + icon.color: sysPalette.windowText + //height: 24 + } + TabButton { + text: qsTr("Settings") + icon.name: 'preferences-system-symbolic' + icon.color: sysPalette.windowText + //height: 24 + } + TabButton { + text: qsTr("History") + icon.name: 'view-history' + icon.color: sysPalette.windowText + //height: 24 + } + } + + StackLayout { + id: sidebarContents + anchors.top: sidebarSelector.bottom + anchors.left: parent.left + anchors.topMargin: 5 + anchors.leftMargin: 5 + anchors.bottom: parent.bottom + //anchors.bottomMargin: sidebarSelector.height + width: parent.width - 5 + currentIndex: sidebarSelector.currentIndex + z: -1 + clip: true + + ObjectLists { + id: objectLists + onChanged: drawCanvas.requestPaint() + } + + Settings { + id: settings + canvas: drawCanvas + onChanged: drawCanvas.requestPaint() + } + + HistoryBrowser { + id: historyBrowser + } + } + } + + LogGraphCanvas { + id: drawCanvas + anchors.top: parent.top + anchors.left: sidebar.inPortrait ? parent.left : sidebar.right + height: parent.height + 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) { + firstDrawDone = true; + console.info("First paint done in " + (new Date().getTime()-(StartTime*1000)) + "ms") + if(TestBuild == true) { + console.log("Plot drawn in canvas, terminating test of build in 100ms.") + testBuildTimer.start() + } + } + + ViewPositionChangeOverlay { + id: viewPositionChanger + anchors.fill: parent + canvas: parent + settingsInstance: settings + } + + PickLocationOverlay { + id: positionPicker + anchors.fill: parent + canvas: 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 = {} + Object.keys(Objects.currentObjectsByName).forEach(key => { + delete Objects.currentObjectsByName[key]; + // Required to keep the same reference for the copy of the object used in expression variable detection. + // Another way would be to change the reference as well, but I feel like the code would be less clean. + }) + for(let objType in data['objects']) { + if(Object.keys(Objects.types).indexOf(objType) > -1) { + Objects.currentObjects[objType] = [] + for(let objData of data['objects'][objType]) { + let obj = new Objects.types[objType](...objData) + Objects.currentObjects[objType].push(obj) + Objects.currentObjectsByName[obj.name] = obj + } + } else { + error += qsTr("Unknown object type: %1.").arg(objType) + "\n"; + } + } + + // Updating object dependencies. + for(let objName in Objects.currentObjectsByName) + Objects.currentObjectsByName[objName].update() + + // Importing history + if("history" in data) + history.unserialize(...data["history"]) + + // Refreshing sidebar + if(sidebarSelector.currentIndex == 0) { + // For some reason, if we load a file while the tab is on object, + // we get stuck in a Qt-side loop? Qt bug or side-effect here, I don't know. + sidebarSelector.currentIndex = 1 + objectLists.update() + delayRefreshTimer.start() + } else { + objectLists.update() + } + } else { + error = qsTr("Invalid file provided.") + } + if(error != "") { + console.log(error) + alert.show(qsTr("Could not save file: ") + error) + // TODO: Error handling + return + } + drawCanvas.requestPaint() + alert.show(qsTr("Loaded file '%1'.").arg(basename)) + history.saved = true + } + + Timer { + id: delayRefreshTimer + repeat: false + interval: 1 + onTriggered: sidebarSelector.currentIndex = 0 + } + + Timer { + id: testBuildTimer + repeat: false + interval: 100 + onTriggered: Qt.quit() // Quit after paint on test build + } + + onClosing: function(close) { + if(!history.saved) { + close.accepted = false + appMenu.openSaveUnsavedChangesDialog() + } + } + + /*! + \qmlmethod void LogarithmPlotter::copyDiagramToClipboard() + Copies the current diagram image to the clipboard. + */ + function copyDiagramToClipboard() { + var file = Helper.gettmpfile() + drawCanvas.save(file) + Helper.copyImageToClipboard() + alert.show(qsTr("Copied plot screenshot to clipboard!")) + } + + /*! + \qmlmethod void LogarithmPlotter::showAlert(string alertText) + Shows an alert on the diagram. + */ + function showAlert(alertText) { + // This function is called from the backend and is used to show alerts from there. + alert.show(alertText) + } + + + Menu { + id: updateMenu + title: qsTr("&Update") + Action { + text: qsTr("&Update LogarithmPlotter") + icon.name: 'update' + onTriggered: Qt.openUrlExternally("https://dev.apps.ad5001.eu/logarithmplotter") + } + } + + /*! + \qmlmethod void LogarithmPlotter::showUpdateMenu() + Shows the update menu in the AppMenuBar. + */ + function showUpdateMenu() { + appMenu.addMenu(updateMenu) + } +} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/CustomPropertyList.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/CustomPropertyList.qml similarity index 82% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/CustomPropertyList.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/CustomPropertyList.qml index 6e33b71..3f2f5ab 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/CustomPropertyList.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/CustomPropertyList.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,7 +20,10 @@ 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 +import "../../js/objects.js" as Objects +import "../../js/historylib.js" as HistoryLib +import "../../js/utils.js" as Utils +import "../../js/mathlib.js" as MathLib /*! \qmltype CustomPropertyList @@ -47,7 +50,7 @@ Repeater { */ property var positionPicker - readonly property var textTypes: ['Domain', 'string', 'number', 'int'] + readonly property var textTypes: ['Domain', 'string', 'number'] readonly property var comboBoxTypes: ['ObjectType', 'Enum'] readonly property var listTypes: ['List', 'Dict'] @@ -74,13 +77,13 @@ Repeater { Setting.ExpressionEditor { height: 30 label: propertyLabel - icon: `properties/${propertyIcon}.svg` - defValue: JS.Utils.simplifyExpression(obj[propertyName].toEditableString()) + icon: `settings/custom/${propertyIcon}.svg` + defValue: 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( + history.addToHistory(new HistoryLib.EditedProperty( obj.name, objType, propertyName, obj[propertyName], newExpr )) @@ -88,7 +91,7 @@ Repeater { root.changed() } } - } + } } @@ -99,31 +102,27 @@ Repeater { Setting.TextSetting { height: 30 label: propertyLabel - icon: `properties/${propertyIcon}.svg` - min: propertyType == "int" ? 0 : -Infinity - isInt: propertyType == "int" + icon: `settings/custom/${propertyIcon}.svg` isDouble: propertyType == "number" defValue: obj[propertyName] == null ? '' : obj[propertyName].toString() category: { return { "Domain": "domain", "string": "all", - "number": "all", - "int": "all", + "number": "all" }[propertyType] } onChanged: function(newValue) { try { var newValueParsed = { - "Domain": () => JS.MathLib.parseDomain(newValue), + "Domain": () => MathLib.parseDomain(newValue), "string": () => newValue, - "number": () => newValue, - "int": () => newValue + "number": () => parseFloat(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( + history.addToHistory(new HistoryLib.EditedProperty( obj.name, objType, propertyName, obj[propertyName], newValueParsed )) @@ -132,7 +131,6 @@ Repeater { } } catch(e) { // Error in expression or domain - console.trace() parsingErrorDialog.showDialog(propertyName, newValue, e.message) } } @@ -143,9 +141,7 @@ Repeater { 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) + text = qsTranslate("error", "Error while parsing expression for property %1:\n%2\n\nEvaluated expression: %3").arg(propName).arg(error).arg(propValue) open() } } @@ -159,7 +155,7 @@ Repeater { CheckBox { height: 20 text: propertyLabel - //icon: `properties/${propertyIcon}.svg` + //icon: `settings/custom/${propertyIcon}.svg` checked: { //if(obj[propertyName] == null) { @@ -168,7 +164,7 @@ Repeater { return obj[propertyName] } onClicked: { - Modules.History.addToHistory(new JS.HistoryLib.EditedProperty( + history.addToHistory(new HistoryLib.EditedProperty( obj.name, objType, propertyName, obj[propertyName], this.checked )) @@ -185,15 +181,15 @@ Repeater { Setting.ComboBoxSetting { height: 30 label: propertyLabel - icon: `properties/${propertyIcon}.svg` + icon: `settings/custom/${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())] : []) + Objects.getObjectsName(propertyType.objType).concat( + isRealObject ? [qsTr("+ Create new %1").arg(Objects.types[propertyType.objType].displayType())] : []) : propertyType.values // Translated version of the model. model: selectObjMode ? baseModel : propertyType.translatedValues @@ -203,32 +199,30 @@ Repeater { if(selectObjMode) { // This is only done when what we're selecting are Objects. // Setting object property. - var selectedObj = Modules.Objects.currentObjectsByName[baseModel[newIndex]] + var selectedObj = 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())] : + selectedObj = Objects.createNewRegisteredObject(propertyType.objType) + history.addToHistory(new HistoryLib.CreateNewObject(selectedObj.name, propertyType.objType, selectedObj.export())) + baseModel = Objects.getObjectsName(propertyType.objType).concat( + isRealObject ? [qsTr("+ Create new %1").arg(Objects.types[propertyType.objType].displayType())] : []) 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) + selectedObj.requiredBy.push(Objects.currentObjects[objType][objIndex]) + //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( + history.addToHistory(new 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( + history.addToHistory(new HistoryLib.EditedProperty( obj.name, objType, propertyName, obj[propertyName], baseModel[newIndex] )) @@ -246,7 +240,7 @@ Repeater { Setting.ListSetting { label: propertyLabel - //icon: `properties/${propertyIcon}.svg` + //icon: `settings/custom/${propertyIcon}.svg` dictionaryMode: paramTypeIn(propertyType, ['Dict']) keyType: dictionaryMode ? propertyType.keyType : 'string' valueType: propertyType.valueType @@ -258,10 +252,11 @@ Repeater { onChanged: { var exported = exportModel() - Modules.History.addToHistory(new JS.HistoryLib.EditedProperty( + history.addToHistory(new HistoryLib.EditedProperty( obj.name, objType, propertyName, obj[propertyName], exported )) + //Objects.currentObjects[objType][objIndex][propertyName] = exported obj[propertyName] = exported root.changed() } @@ -283,7 +278,7 @@ Repeater { property string propertyName: modelData[0] property var propertyType: modelData[1] property string propertyLabel: qsTranslate('prop',propertyName) - property string propertyIcon: propertyName + property string propertyIcon: Utils.camelCase2readable(propertyName) sourceComponent: { if(propertyName.startsWith('comment')) diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/Dialog.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/Dialog.qml similarity index 83% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/Dialog.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/Dialog.qml index d762da2..78efdae 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/Dialog.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/Dialog.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,10 +18,15 @@ import QtQuick import QtQuick.Controls +import QtQuick.Dialogs as D import Qt.labs.platform as Native import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup -import eu.ad5001.LogarithmPlotter.Common +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 Dialog @@ -49,7 +54,7 @@ Popup.BaseDialog { \qmlproperty var EditorDialog::obj Instance of the object being edited. */ - property var obj: Modules.Objects.currentObjects[objType][objIndex] + property var obj: Objects.currentObjects[objType][objIndex] /*! \qmlproperty var EditorDialog::posPicker Reference to the global PositionPicker QML object. @@ -82,7 +87,7 @@ Popup.BaseDialog { Label { id: dlgTitle verticalAlignment: TextInput.AlignVCenter - text: qsTr("Edit properties of %1 %2").arg(Modules.Objects.types[objEditor.objType].displayType()).arg(objEditor.obj.name) + text: qsTr("Edit properties of %1 %2").arg(Objects.types[objEditor.objType].displayType()).arg(objEditor.obj.name) font.pixelSize: 20 color: sysPalette.windowText } @@ -106,16 +111,16 @@ Popup.BaseDialog { width: dlgProperties.width value: objEditor.obj.name onChanged: function(newValue) { - let newName = JS.Utils.parseName(newValue) + let newName = Utils.parseName(newValue) if(newName != '' && objEditor.obj.name != newName) { - if(newName in Modules.Objects.currentObjectsByName) { + if(newName in Objects.currentObjectsByName) { invalidNameDialog.showDialog(newName) } else { - Modules.History.addToHistory(new JS.HistoryLib.NameChanged( + history.addToHistory(new HistoryLib.NameChanged( objEditor.obj.name, objEditor.objType, newName )) - Modules.Objects.renameObject(obj.name, newName) - objEditor.obj = Modules.Objects.currentObjects[objEditor.objType][objEditor.objIndex] + Objects.renameObject(obj.name, newName) + objEditor.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex] objectListList.update() } } @@ -126,17 +131,13 @@ Popup.BaseDialog { id: labelContentProperty height: 30 width: dlgProperties.width - label: qsTranslate("prop", "labelContent") + 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) { - 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() @@ -164,7 +165,7 @@ Popup.BaseDialog { */ function open() { dlgCustomProperties.model = [] // Reset - let objProps = Modules.Objects.types[objEditor.objType].properties() + let objProps = Objects.types[objEditor.objType].properties() dlgCustomProperties.model = Object.keys(objProps).map(prop => [prop, objProps[prop]]) // Converted to 2-dimentional array. objEditor.show() } diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/qmldir b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/qmldir similarity index 100% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/qmldir rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/Editor/qmldir diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml similarity index 87% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectCreationGrid.qml index 21559a9..1fd7364 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,8 +18,9 @@ import QtQuick import QtQuick.Controls +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 /*! @@ -43,7 +44,7 @@ Column { // Open editor objectEditor.obj = obj objectEditor.objType = obj.type - objectEditor.objIndex = Modules.Objects.currentObjects[obj.type].indexOf(obj) + objectEditor.objIndex = Objects.currentObjects[obj.type].indexOf(obj) objectEditor.open() // Disconnect potential link posPicker.picked.disconnect(openEditorDialog) @@ -60,12 +61,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 +94,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,10 +104,8 @@ Column { ToolTip.text: label.text onClicked: { - let newObj = Modules.Objects.createNewRegisteredObject(modelData) - Modules.History.addToHistory(new JS.HistoryLib.CreateNewObject( - newObj.name, modelData, newObj.export() - )) + let newObj = Objects.createNewRegisteredObject(modelData) + history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export())) objectLists.update() let hasXProp = newObj.constructor.properties().hasOwnProperty('x') diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml similarity index 79% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml index d37a015..e7d7a6f 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectLists.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ import QtQuick import QtQuick.Controls import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.ObjectLists.Editor 1.0 as Editor +import "../js/objects.js" as Objects /*! \qmltype ObjectLists @@ -46,7 +47,7 @@ ScrollView { ListView { id: objectsListView - model: Object.keys(Modules.Objects.types) + model: Object.keys(Objects.types) //width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0) implicitHeight: contentItem.childrenRect.height + footerItem.height + 10 @@ -54,9 +55,9 @@ ScrollView { id: objTypeList property string objType: objectsListView.model[index] property var editingRows: [] - model: Modules.Objects.currentObjects[objType] + model: Objects.currentObjects[objType] width: objectsListView.width - height: contentItem.childrenRect.height + (visible ? 10 : 0) + implicitHeight: contentItem.childrenRect.height visible: model != undefined && model.length > 0 interactive: false @@ -69,23 +70,21 @@ ScrollView { CheckBox { id: typeVisibilityCheckBox - checked: Modules.Objects.currentObjects[objType] != undefined ? Modules.Objects.currentObjects[objType].every(obj => obj.visible) : true + checked: Objects.currentObjects[objType] != undefined ? 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 + 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(Modules.Objects.types[objType].displayTypeMultiple()) : - qsTr("Show all %1").arg(Modules.Objects.types[objType].displayTypeMultiple()) + 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(Modules.Objects.types[objType].displayTypeMultiple()) + text: qsTranslate("control", "%1: ").arg(Objects.types[objType].displayTypeMultiple()) font.pixelSize: 20 } } @@ -93,11 +92,11 @@ ScrollView { delegate: ObjectRow { id: controlRow width: objTypeList.width - obj: Modules.Objects.currentObjects[objType][index] + obj: Objects.currentObjects[objType][index] posPicker: positionPicker onChanged: { - obj = Modules.Objects.currentObjects[objType][index] + obj = Objects.currentObjects[objType][index] objectListList.update() } @@ -129,7 +128,7 @@ ScrollView { function update() { objectListList.changed() for(var objType in objectListList.listViews) { - objectListList.listViews[objType].model = Modules.Objects.currentObjects[objType] + objectListList.listViews[objType].model = Objects.currentObjects[objType] } } diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml similarity index 76% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml index 0f7003f..ab3f2f7 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/ObjectRow.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,9 @@ import QtQuick.Dialogs import QtQuick.Controls import QtQuick.Window import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting -import eu.ad5001.LogarithmPlotter.Common +import "../js/objects.js" as Objects +import "../js/historylib.js" as HistoryLib +import "../js/math/latex.js" as LatexJS /*! @@ -72,7 +74,7 @@ Item { anchors.left: parent.left anchors.leftMargin: 5 onClicked: { - Modules.History.addToHistory(new JS.HistoryLib.EditedVisibility( + history.addToHistory(new HistoryLib.EditedVisibility( obj.name, obj.type, this.checked )) obj.visible = this.checked @@ -89,43 +91,27 @@ Item { id: objDescription anchors.left: objVisibilityCheckBox.right anchors.right: deleteButton.left - height: Modules.Latex.enabled ? Math.max(parent.minHeight, latexDescription.height+4) : parent.minHeight + height: LatexJS.enabled ? Math.max(parent.minHeight, latexDescription.height+4) : parent.minHeight verticalAlignment: TextInput.AlignVCenter - text: Modules.Latex.enabled ? "" : obj.getReadableString() + text: LatexJS.enabled ? "" : obj.getReadableString() font.pixelSize: 14 Image { id: latexDescription anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left - visible: Modules.Latex.enabled + visible: LatexJS.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 - }) - } - } + property var ltxInfo: visible ? Latex.render(obj.getLatexString(), depth*(parent.font.pixelSize+2), parent.color).split(",") : ["","0","0"] + source: visible ? ltxInfo[0] : "" + width: parseInt(ltxInfo[1])/depth + height: parseInt(ltxInfo[2])/depth } MouseArea { anchors.fill: parent onClicked: { - objEditor.obj = Modules.Objects.currentObjects[obj.type][index] + objEditor.obj = Objects.currentObjects[obj.type][index] objEditor.objType = obj.type objEditor.objIndex = index //objEditor.editingRow = objectRow @@ -212,7 +198,7 @@ Item { 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( + history.addToHistory(new HistoryLib.ColorChanged( obj.name, obj.type, obj.color, selectedColor.toString() )) obj.color = selectedColor.toString() @@ -227,14 +213,10 @@ Item { 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) - } + object.requiredBy = [] + history.addToHistory(new HistoryLib.DeleteObject( + object.name, object.type, object.export() + )) + Objects.deleteObject(object.name) } } diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/qmldir b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/qmldir similarity index 100% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/qmldir rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ObjectLists/qmldir diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/PickLocation.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml similarity index 89% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/PickLocation.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml index 6ba8bb6..afc8d61 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/PickLocation.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,12 +18,14 @@ import QtQuick import QtQuick.Controls -import eu.ad5001.LogarithmPlotter.Setting as Setting -import eu.ad5001.LogarithmPlotter.Common +import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting +import "js/objects.js" as Objects +import "js/mathlib.js" as MathLib +import "js/historylib.js" as HistoryLib /*! - \qmltype PickLocation - \inqmlmodule eu.ad5001.LogarithmPlotter.Overlay + \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 @@ -36,7 +38,7 @@ Item { id: pickerRoot visible: false clip: true - + /*! \qmlsignal PickLocationOverlay::picked(var obj) @@ -97,9 +99,9 @@ Item { readonly property bool userPickY: pickY && pickYCheckbox.checked Rectangle { - anchors.fill: parent color: sysPalette.window opacity: 0.35 + anchors.fill: parent } MouseArea { @@ -112,10 +114,10 @@ Item { if(mouse.button == Qt.LeftButton) { // Validate let newValueX = !parent.userPickX ? null : parseValue(picked.mouseX.toString(), objType, propertyX) let newValueY = !parent.userPickY ? null : parseValue(picked.mouseY.toString(), objType, propertyY) - let obj = Modules.Objects.currentObjectsByName[objName] + let obj = Objects.currentObjectsByName[objName] // Set values if(parent.userPickX && parent.userPickY) { - Modules.History.addToHistory(new JS.HistoryLib.EditedPosition( + history.addToHistory(new HistoryLib.EditedPosition( objName, objType, obj[propertyX], newValueX, obj[propertyY], newValueY )) obj[propertyX] = newValueX @@ -124,7 +126,7 @@ Item { objectLists.update() pickerRoot.picked(obj) } else if(parent.userPickX) { - Modules.History.addToHistory(new JS.HistoryLib.EditedProperty( + history.addToHistory(new HistoryLib.EditedProperty( objName, objType, propertyX, obj[propertyX], newValueX )) obj[propertyX] = newValueX @@ -132,7 +134,7 @@ Item { objectLists.update() pickerRoot.picked(obj) } else if(parent.userPickY) { - Modules.History.addToHistory(new JS.HistoryLib.EditedProperty( + history.addToHistory(new HistoryLib.EditedProperty( objName, objType, propertyY, obj[propertyY], newValueY )) obj[propertyY] = newValueY @@ -261,7 +263,7 @@ Item { color: 'black' anchors.top: parent.top anchors.left: parent.left - anchors.leftMargin: Modules.Canvas.x2px(picked.mouseX) + anchors.leftMargin: canvas.x2px(picked.mouseX) visible: parent.userPickX } @@ -272,7 +274,7 @@ Item { color: 'black' anchors.top: parent.top anchors.left: parent.left - anchors.topMargin: Modules.Canvas.y2px(picked.mouseY) + anchors.topMargin: canvas.y2px(picked.mouseY) visible: parent.userPickY } @@ -281,26 +283,25 @@ Item { x: picker.mouseX - width - 5 y: picker.mouseY - height - 5 color: 'black' + property double axisX: canvas.xaxisstep1 property double mouseX: { - const axisX = Modules.Canvas.axesSteps.x.value - const xpos = Modules.Canvas.px2x(picker.mouseX) + let xpos = canvas.px2x(picker.mouseX) if(snapToGridCheckbox.checked) { - if(Modules.Settings.logscalex) { + 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 axisX*Math.round(xpos/axisX) + return canvas.xaxisstep1*Math.round(xpos/canvas.xaxisstep1) } } else { return xpos.toFixed(parent.precision) } } property double mouseY: { - const axisY = Modules.Canvas.axesSteps.y.value - const ypos = Modules.Canvas.px2y(picker.mouseY) + let ypos = canvas.px2y(picker.mouseY) if(snapToGridCheckbox.checked) { - return axisY*Math.round(ypos/axisY) + return canvas.yaxisstep1*Math.round(ypos/canvas.yaxisstep1) } else { return ypos.toFixed(parent.precision) } @@ -323,9 +324,9 @@ Item { Parses a given \c value as an expression or a number depending on the type of \c propertyName of all \c objType. */ function parseValue(value, objType, propertyName) { - if(Modules.Objects.types[objType].properties()[propertyName] == 'number') + if(Objects.types[objType].properties()[propertyName] == 'number') return parseFloat(value) else - return new JS.MathLib.Expression(value) + return new MathLib.Expression(value) } } diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/About.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/About.qml similarity index 97% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/About.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/About.qml index e82f39c..d74c101 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/About.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/About.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -102,7 +102,7 @@ BaseDialog { wrapMode: Text.WordWrap textFormat: Text.RichText font.pixelSize: 13 - text: "Copyright © 2021-2025 Ad5001 <mail@ad5001.eu>
+ text: "Copyright © 2021-2024 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.

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..0220956 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/BaseDialog.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/BaseDialog.qml similarity index 97% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/BaseDialog.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/BaseDialog.qml index a9c73a6..10fe87e 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/BaseDialog.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/BaseDialog.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Changelog.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Changelog.qml similarity index 85% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Changelog.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/Changelog.qml index 1797b9d..f86af07 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,17 +45,17 @@ Popup { property bool changelogNeedsFetching: true onAboutToShow: if(changelogNeedsFetching) { - Helper.fetchChangelog().then((fetchedText) => { - changelogNeedsFetching = false - changelog.text = fetchedText + Helper.fetchChangelog() + } + + Connections { + target: Helper + function onChangelogFetched(chl) { + changelogNeedsFetching = false; + changelog.text = chl 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 - }) + // console.log(changelog.height, changelogView.contentItem.implicitHeight) + } } ScrollView { @@ -96,7 +96,7 @@ Popup { Button { id: doneBtn - text: qsTr("Close") + text: qsTr("Done") font.pixelSize: 18 anchors.bottom: parent.bottom anchors.bottomMargin: 7 diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/FileDialog.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/FileDialog.qml similarity index 97% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/FileDialog.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/FileDialog.qml index 00dbdae..4b7ff8a 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by 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..dddafab --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/GreetScreen.qml @@ -0,0 +1,236 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick +import QtQuick.Controls +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, 700) + modal: true + focus: true + clip: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + + ScrollView { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.bottomMargin: bottomButtons.height + 20 + clip: true + + Column { + width: greetingPopup.width - 25 + spacing: 10 + clip: true + topPadding: 35 + + Row { + id: welcome + height: logo.height + spacing: 10 + anchors.horizontalCenter: parent.horizontalCenter + + Image { + id: logo + source: "../icons/logarithmplotter.svg" + sourceSize.width: 48 + sourceSize.height: 48 + width: 48 + height: 48 + } + + Label { + id: welcomeText + anchors.verticalCenter: parent.verticalCenter + wrapMode: Text.WordWrap + font.pixelSize: 32 + text: qsTr("Welcome to LogarithmPlotter") + } + } + + Label { + id: versionText + anchors.horizontalCenter: parent.horizontalCenter + wrapMode: Text.WordWrap + width: implicitWidth + font.pixelSize: 18 + font.italic: true + text: qsTr("Version %1").arg(Helper.getVersion()) + } + + Label { + id: helpText + anchors.horizontalCenter: parent.horizontalCenter + wrapMode: Text.WordWrap + font.pixelSize: 14 + width: parent.width - 50 + text: qsTr("Take a few seconds to configure LogarithmPlotter.\nThese settings can be changed at any time from the \"Settings\" menu.") + } + + CheckBox { + id: checkForUpdatesSetting + anchors.left: parent.left + checked: Helper.getSettingBool("check_for_updates") + text: qsTr('Check for updates on startup (requires online connectivity)') + onClicked: { + Helper.setSettingBool("check_for_updates", checked) + // Set in the menu bar + appMenu.settingsMenu.children[0].checked = checked + } + } + + CheckBox { + id: resetRedoStackSetting + anchors.left: parent.left + checked: Helper.getSettingBool("reset_redo_stack") + text: qsTr('Reset redo stack when a new action is added to history') + onClicked: { + Helper.setSettingBool("reset_redo_stack", checked) + appMenu.settingsMenu.children[1].checked = checked + } + } + + CheckBox { + id: enableLatexSetting + anchors.left: parent.left + checked: Helper.getSettingBool("enable_latex") + text: qsTr('Enable LaTeX rendering') + onClicked: { + Helper.setSettingBool("enable_latex", checked) + appMenu.settingsMenu.children[2].checked = checked + } + } + + CheckBox { + id: autocloseFormulaSetting + anchors.left: parent.left + checked: Helper.getSettingBool("expression_editor.autoclose") + text: qsTr('Automatically close parenthesises and brackets in expressions') + onClicked: { + Helper.setSettingBool("expression_editor.autoclose", checked) + appMenu.settingsMenu.children[3].children[0].checked = checked + } + } + + CheckBox { + id: colorizeFormulaSetting + anchors.left: parent.left + checked: Helper.getSettingBool("expression_editor.colorize") + text: qsTr('Enable syntax highlighting for expressions') + onClicked: { + Helper.setSettingBool("expression_editor.colorize", checked) + appMenu.settingsMenu.children[3].children[1].checked = checked + } + } + + CheckBox { + id: autocompleteFormulaSetting + anchors.left: parent.left + checked: Helper.getSettingBool("autocompletion.enabled") + text: qsTr('Enable autocompletion interface in expression editor') + onClicked: { + Helper.setSettingBool("autocompletion.enabled", checked) + appMenu.settingsMenu.children[3].children[2].checked = checked + } + } + + Row { + anchors.left: parent.left + anchors.leftMargin: 10 + spacing: 10 + + Label { + id: colorSchemeLabel + anchors.verticalCenter: parent.verticalCenter + wrapMode: Text.WordWrap + text: qsTr("Color scheme:") + } + + ComboBox { + model: ["Breeze Light", "Breeze Dark", "Solarized", "Github Light", "Github Dark", "Nord", "Monokai"] + currentIndex: Helper.getSettingInt("expression_editor.color_scheme") + + onActivated: function(index) { + Helper.setSettingInt("expression_editor.color_scheme", index) + } + } + } + } + } + + Rectangle { + id: bottomSeparator + opacity: 0.3 + color: sysPalette.windowText + width: parent.width * 2 / 3 + height: 1 + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: bottomButtons.top + anchors.bottomMargin: 9 + } + + Row { + id: bottomButtons + anchors.bottom: parent.bottom + anchors.bottomMargin: 7 + spacing: 10 + 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/InsertCharacter.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/InsertCharacter.qml similarity index 72% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/InsertCharacter.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/InsertCharacter.qml index 8111f85..7002539 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/InsertCharacter.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/InsertCharacter.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,9 +16,8 @@ * along with this program. If not, see . */ -import QtQuick import QtQuick.Controls -import QtQml.Models +import QtQuick /*! \qmltype InsertCharacter @@ -43,18 +42,15 @@ Popup { */ property string category: 'all' - width: insertGrid.width + 10 - height: insertGrid.height + 10 + width: 280 + height: Math.ceil(insertGrid.insertChars.length/insertGrid.columns)*(width/insertGrid.columns)+5 modal: true closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent - GridView { + Grid { id: insertGrid - width: 280 - height: Math.ceil(model.count/columns)*cellHeight - property int columns: 7 - cellWidth: width/columns - cellHeight: cellWidth + width: parent.width + columns: 7 property var insertCharsExpression: [ "∞","π","¹","²","³","⁴","⁵", @@ -90,34 +86,21 @@ Popup { }[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() + 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: { + selected(text) + } } } - - 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/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/ThanksTo.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/ThanksTo.qml new file mode 100644 index 0000000..4c6d29c --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/ThanksTo.qml @@ -0,0 +1,334 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +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: about + title: qsTr("Thanks and Contributions - LogarithmPlotter") + width: 450 + minimumHeight: 710 + + Column { + anchors { + top: parent.top; + left: parent.left; + bottom: parent.bottom; + right: parent.right; + topMargin: margin; + leftMargin: margin; + bottomMargin: margin; + rightMargin: 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: { + append({ + tranName: '🇬🇧 ' + qsTr('English'), + link: 'https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/en/', + authors: [{ + authorLine: 'Ad5001', + email: 'mail@ad5001.eu', + website: 'https://ad5001.eu', + websiteName: qsTr('Website') + }] + }) + append({ + tranName: '🇫🇷 ' + qsTr('French'), + link: 'https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/fr/', + authors: [{ + authorLine: 'Ad5001', + website: 'https://ad5001.eu', + websiteName: qsTr('Website') + }] + }) + append({ + tranName: '🇩🇪 ' + qsTr('German'), + link: 'https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/de/', + authors: [{ + authorLine: 'Ad5001', + website: 'https://ad5001.eu', + websiteName: qsTr('Website') + }] + }) + append({ + tranName: '🇭🇺 ' + qsTr('Hungarian'), + link: 'https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/hu/', + authors: [{ + authorLine: 'Óvári', + website: 'https://github.com/ovari', + websiteName: qsTr('Github') + }] + }) + append({ + tranName: '🇳🇴 ' + qsTr('Norwegian'), + link: 'https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/no/', + authors: [{ + authorLine: 'Allan Nordhøy', + website: 'https://github.com/comradekingu', + websiteName: qsTr('Github') + }] + }) + } + } + + 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.right: parent.right + anchors.verticalCenter: parent.verticalCenter + height: 30 + spacing: 10 + + Button { + text: websiteName + icon.name: 'web-browser' + height: parent.height + onClicked: Qt.openUrlExternally(website) + } + } + } + } + } + } + } +} diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/qmldir b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/qmldir similarity index 89% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/qmldir rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/qmldir index 3cf03fa..9306fae 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/qmldir +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Popup/qmldir @@ -1,11 +1,10 @@ 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 +About 1.0 About.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 +Changelog 1.0 Changelog.qml ThanksTo 1.0 ThanksTo.qml +InsertCharacter 1.0 InsertCharacter.qml diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/AutocompletionCategory.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/AutocompletionCategory.qml similarity index 99% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/AutocompletionCategory.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/AutocompletionCategory.qml index 897af70..304b704 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/AutocompletionCategory.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/AutocompletionCategory.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ComboBoxSetting.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ComboBoxSetting.qml similarity index 98% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ComboBoxSetting.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ComboBoxSetting.qml index a4dc93b..d701661 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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/ExpressionEditor.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml similarity index 82% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml index 35bba0a..fa3dece 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,7 +20,10 @@ 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 +import "../js/mathlib.js" as MathLib +import "../js/utils.js" as Utils +import "../js/objects.js" as Objects +import "../js/parsing/parsing.js" as Parsing /*! @@ -78,17 +81,6 @@ Item { 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 @@ -175,20 +167,19 @@ Item { Icon { id: iconLabel anchors.top: parent.top - anchors.topMargin: parent.icon == "" ? 0 : 3 - source: control.visible && parent.icon != "" ? "../icons/" + control.icon : "" + anchors.topMargin: icon == "" ? 0 : 3 + source: control.visible && icon != "" ? "../icons/" + control.icon : "" width: height - height: parent.icon == "" || !visible ? 0 : 24 + height: icon == "" || !visible ? 0 : 24 color: sysPalette.windowText } Label { id: labelItem anchors.left: iconLabel.right - anchors.leftMargin: parent.icon == "" ? 0 : 5 - anchors.top: parent.top + anchors.leftMargin: icon == "" ? 0 : 5 height: parent.height - width: Math.max(85, implicitWidth) + anchors.top: parent.top verticalAlignment: TextInput.AlignVCenter //color: sysPalette.windowText text: visible ? qsTranslate("control", "%1: ").arg(control.label) : "" @@ -200,9 +191,7 @@ Item { 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) + text = qsTranslate("expression", "Error while parsing expression for property %1:\n%2\n\nEvaluated expression: %3").arg(propName).arg(error).arg(propValue) open() } } @@ -221,9 +210,9 @@ Item { 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 bool autocompleteEnabled: Helper.getSettingBool("autocompletion.enabled") + property bool syntaxHighlightingEnabled: Helper.getSettingBool("expression_editor.colorize") + property bool autoClosing: Helper.getSettingBool("expression_editor.autoclose") property var tokens: autocompleteEnabled || syntaxHighlightingEnabled ? parent.tokens(text) : [] Keys.priority: Keys.BeforeItem // Required for knowing which key the user presses. @@ -231,8 +220,8 @@ Item { onEditingFinished: { if(insertButton.focus || insertPopup.focus) return let value = text - if(value != "" && value.toString() != parent.defValue) { - let expr = parent.parse(value) + if(value != "" && value.toString() != defValue) { + let expr = parse(value) if(expr != null) { control.changed(expr) defValue = expr.toEditableString() @@ -280,10 +269,10 @@ Item { acPopupContent.itemSelected = 0 - if(event.text in parent.openAndCloseMatches && autoClosing) { + if(event.text in openAndCloseMatches && autoClosing) { let start = selectionStart insert(selectionStart, event.text) - insert(selectionEnd, parent.openAndCloseMatches[event.text]) + insert(selectionEnd, openAndCloseMatches[event.text]) cursorPosition = start+1 event.accepted = true } @@ -317,9 +306,9 @@ Item { width: parent.width readonly property var identifierTokenTypes: [ - JS.Parsing.TokenType.VARIABLE, - JS.Parsing.TokenType.FUNCTION, - JS.Parsing.TokenType.CONSTANT + Parsing.TokenType.VARIABLE, + Parsing.TokenType.FUNCTION, + Parsing.TokenType.CONSTANT ] property var currentToken: generateTokenInformation(getTokenAt(editor.tokens, editor.cursorPosition)) property var previousToken: generateTokenInformation(getPreviousToken(currentToken.token)) @@ -344,7 +333,7 @@ Item { '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, + 'dot': exists ? (token.type == Parsing.TokenType.PUNCT && token.value == ".") : false, 'identifier': exists ? identifierTokenTypes.includes(token.type) : false } } @@ -383,7 +372,7 @@ Item { */ function getPreviousToken(token) { let newToken = getTokenAt(editor.tokens, token.startPosition) - if(newToken != null && newToken.type == JS.Parsing.TokenType.WHITESPACE) + if(newToken != null && newToken.type == Parsing.TokenType.WHITESPACE) return getPreviousToken(newToken) return newToken } @@ -392,7 +381,7 @@ Item { id: objectPropertiesList category: qsTr("Object Properties") - visbilityCondition: control.allowGraphObjects && doesObjectExist + visbilityCondition: doesObjectExist itemStartIndex: 0 itemSelected: parent.itemSelected property bool isEnteringProperty: ( @@ -403,9 +392,9 @@ Item { property string objectName: isEnteringProperty ? (parent.currentToken.dot ? parent.previousToken.value : parent.previousToken2.value) : "" - property bool doesObjectExist: isEnteringProperty && (objectName in Modules.Objects.currentObjectsByName) + property bool doesObjectExist: isEnteringProperty && (objectName in Objects.currentObjectsByName) property var objectProperties: doesObjectExist ? - Modules.Objects.currentObjectsByName[objectName].constructor.properties() : + Objects.currentObjectsByName[objectName].constructor.properties() : {} categoryItems: Object.keys(objectProperties) autocompleteGenerator: (item) => { @@ -442,9 +431,9 @@ Item { visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot itemStartIndex: variablesList.itemStartIndex + variablesList.model.length itemSelected: parent.itemSelected - categoryItems: JS.Parsing.CONSTANTS_LIST + categoryItems: Parsing.CONSTANTS_LIST autocompleteGenerator: (item) => {return { - 'text': item, 'annotation': JS.Parsing.CONSTANTS[item], + 'text': item, 'annotation': Parsing.CONSTANTS[item], 'autocomplete': item + " ", 'cursorFinalOffset': 0 }} baseText: parent.visible ? parent.currentToken.value : "" @@ -457,9 +446,9 @@ Item { visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot itemStartIndex: constantsList.itemStartIndex + constantsList.model.length itemSelected: parent.itemSelected - categoryItems: JS.Parsing.FUNCTIONS_LIST + categoryItems: Parsing.FUNCTIONS_LIST autocompleteGenerator: (item) => {return { - 'text': item, 'annotation': JS.Parsing.FUNCTIONS_USAGE[item].join(', '), + 'text': item, 'annotation': Parsing.FUNCTIONS_USAGE[item].join(', '), 'autocomplete': item+'()', 'cursorFinalOffset': -1 }} baseText: parent.visible ? parent.currentToken.value : "" @@ -469,12 +458,12 @@ Item { id: executableObjectsList category: qsTr("Executable Objects") - visbilityCondition: control.allowGraphObjects && parent.currentToken.identifier && !parent.previousToken.dot + visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot itemStartIndex: functionsList.itemStartIndex + functionsList.model.length itemSelected: parent.itemSelected - categoryItems: Modules.Objects.getObjectsName("ExecutableObject").filter(obj => obj != self) + categoryItems: Objects.getObjectsName("ExecutableObject").filter(obj => obj != self) autocompleteGenerator: (item) => {return { - 'text': item, 'annotation': Modules.Objects.currentObjectsByName[item] == null ? '' : Modules.Objects.currentObjectsByName[item].constructor.displayType(), + 'text': item, 'annotation': Objects.currentObjectsByName[item] == null ? '' : Objects.currentObjectsByName[item].constructor.displayType(), 'autocomplete': item+'()', 'cursorFinalOffset': -1 }} baseText: parent.visible ? parent.currentToken.value : "" @@ -484,12 +473,12 @@ Item { id: objectsList category: qsTr("Objects") - visbilityCondition: control.allowGraphObjects && parent.currentToken.identifier && !parent.previousToken.dot + visbilityCondition: 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) + categoryItems: Object.keys(Objects.currentObjectsByName).filter(obj => obj != self) autocompleteGenerator: (item) => {return { - 'text': item, 'annotation': `${Modules.Objects.currentObjectsByName[item].constructor.displayType()}`, + 'text': item, 'annotation': `${Objects.currentObjectsByName[item].constructor.displayType()}`, 'autocomplete': item+'.', 'cursorFinalOffset': 0 }} baseText: parent.visible ? parent.currentToken.value : "" @@ -500,41 +489,29 @@ Item { Button { id: insertButton + text: "α" 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() + insertPopup.focus = true } } P.InsertCharacter { id: insertPopup - x: parent.width - width - y: parent.height + x: Math.round((parent.width - width) / 2) + y: Math.round((parent.height - height) / 2) category: "expression" onSelected: function(c) { editor.insert(editor.cursorPosition, c) - } - - onClosed: function() { + insertPopup.close() focus = false editor.focus = true } @@ -548,9 +525,9 @@ Item { function parse(newExpression) { let expr = null try { - expr = new JS.MathLib.Expression(value.toString()) + expr = new MathLib.Expression(value.toString()) // Check if the expression is valid, throws error otherwise. - if(!expr.allRequirementsFulfilled()) { + if(!expr.allRequirementsFullfilled()) { let undefVars = expr.undefinedVariables() if(undefVars.length > 1) throw new Error(qsTranslate('error', 'No object found with names %1.').arg(undefVars.join(', '))) @@ -561,8 +538,8 @@ Item { 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]) + (obj) => Objects.currentObjectsByName[obj].getDependenciesList() + .includes(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)) @@ -582,7 +559,7 @@ Item { 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 tokenizer = new Parsing.Tokenizer(new Parsing.Input(text), true, false) let tokenList = [] let token while((token = tokenizer.next()) != null) @@ -612,31 +589,31 @@ Item { */ function colorize(tokenList) { let parsedText = "" - let scheme = colorSchemes[Helper.getSetting("expression_editor.color_scheme")] + let scheme = colorSchemes[Helper.getSettingInt("expression_editor.color_scheme")] for(let token of tokenList) { switch(token.type) { - case JS.Parsing.TokenType.VARIABLE: + case Parsing.TokenType.VARIABLE: parsedText += `${token.value}` break; - case JS.Parsing.TokenType.CONSTANT: + case Parsing.TokenType.CONSTANT: parsedText += `${token.value}` break; - case JS.Parsing.TokenType.FUNCTION: - parsedText += `${JS.Utils.escapeHTML(token.value)}` + case Parsing.TokenType.FUNCTION: + parsedText += `${Utils.escapeHTML(token.value)}` break; - case JS.Parsing.TokenType.OPERATOR: - parsedText += `${JS.Utils.escapeHTML(token.value)}` + case Parsing.TokenType.OPERATOR: + parsedText += `${Utils.escapeHTML(token.value)}` break; - case JS.Parsing.TokenType.NUMBER: - parsedText += `${JS.Utils.escapeHTML(token.value)}` + case Parsing.TokenType.NUMBER: + parsedText += `${Utils.escapeHTML(token.value)}` break; - case JS.Parsing.TokenType.STRING: - parsedText += `${token.limitator}${JS.Utils.escapeHTML(token.value)}${token.limitator}` + case Parsing.TokenType.STRING: + parsedText += `${token.limitator}${Utils.escapeHTML(token.value)}${token.limitator}` break; - case JS.Parsing.TokenType.WHITESPACE: - case JS.Parsing.TokenType.PUNCT: + case Parsing.TokenType.WHITESPACE: + case Parsing.TokenType.PUNCT: default: - parsedText += JS.Utils.escapeHTML(token.value).replace(/ /g, ' ') + parsedText += Utils.escapeHTML(token.value).replace(/ /g, ' ') break; } } diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/Icon.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/Icon.qml similarity index 80% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/Icon.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/Icon.qml index 6ba76aa..f845eb9 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ */ import QtQuick import QtQuick.Window -import QtQuick.Controls.impl +import Qt5Compat.GraphicalEffects /*! \qmltype Icon @@ -41,16 +41,20 @@ 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 + visible: false + sourceSize.width: width*Screen.devicePixelRatio + sourceSize.height: width*Screen.devicePixelRatio + } + + ColorOverlay { + anchors.fill: img + source: img color: parent.color } } diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ListSetting.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ListSetting.qml similarity index 99% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ListSetting.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ListSetting.qml index daad433..221b3a3 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml similarity index 83% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml index bc105aa..d1c4c9d 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,7 +37,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 @@ -100,14 +100,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 @@ -127,15 +127,10 @@ Item { 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) { + 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() } @@ -144,40 +139,28 @@ 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() + insertPopup.focus = true } } Popup.InsertCharacter { id: insertPopup - x: parent.width - width - y: parent.height + x: Math.round((parent.width - width) / 2) + y: Math.round((parent.height - height) / 2) onSelected: function(c) { input.insert(input.cursorPosition, c) - } - - onClosed: function() { + insertPopup.close() focus = false 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 100% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/qmldir rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/qmldir index bb78bbe..8106e26 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/qmldir +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/qmldir @@ -1,8 +1,8 @@ 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 +ExpressionEditor 1.0 ExpressionEditor.qml +AutocompletionCategory 1.0 AutocompletionCategory.qml diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml similarity index 60% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml index 58db0e9..208e3e9 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,11 +16,11 @@ * along with this program. If not, see . */ -import QtQuick import QtQuick.Controls +import QtQuick import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup -import 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 @@ -131,18 +136,15 @@ ScrollView { id: fdiag onAccepted: { var filePath = fdiag.currentFile.toString().substr(7) - Modules.Settings.set("saveFilename", filePath) + 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 } } } @@ -153,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 @@ -197,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.") + } } } @@ -220,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 { @@ -240,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 { @@ -268,21 +241,15 @@ 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 { @@ -292,16 +259,12 @@ ScrollView { 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 { @@ -311,13 +274,11 @@ ScrollView { 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 { @@ -328,13 +289,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 { @@ -345,13 +304,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 { @@ -360,31 +317,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 { @@ -393,7 +343,6 @@ ScrollView { width: settings.settingWidth label: qsTr('Y Label') icon: "settings/ylabel.svg" - editable: true model: ListModel { ListElement { text: "" } ListElement { text: "y" } @@ -402,52 +351,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 { @@ -455,10 +391,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 { @@ -504,10 +439,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) } } @@ -528,30 +463,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/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/ViewPositionChange.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ViewPositionChangeOverlay.qml similarity index 64% rename from runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/ViewPositionChange.qml rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ViewPositionChangeOverlay.qml index 78329a7..0d80da4 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Overlay/ViewPositionChange.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ViewPositionChangeOverlay.qml @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,9 +17,14 @@ */ import QtQuick +import QtQuick.Controls +import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting +import "js/objects.js" as Objects +import "js/mathlib.js" as MathLib +import "js/historylib.js" as HistoryLib /*! - \qmltype ViewPositionChange.Overlay + \qmltype ViewPositionChangeOverlay \inqmlmodule eu.ad5001.LogarithmPlotter \brief Overlay used allow the user to drag the canvas' position and change the zoom level. @@ -57,6 +62,16 @@ Item { */ signal endPositionChange(int deltaX, int deltaY) + /*! + \qmlproperty var ViewPositionChangeOverlay::canvas + LogGraphCanvas instance. + */ + property var canvas + /*! + \qmlproperty var ViewPositionChangeOverlay::settingsInstance + Settings instance. + */ + property var settingsInstance /*! \qmlproperty int ViewPositionChangeOverlay::prevX The x coordinate (on the mousearea) at the last change of the canvas position. @@ -69,7 +84,7 @@ Item { 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). + How much should the zoom be mutliplied/scrolled by for one scroll step (120° on the mouse wheel). */ property double baseZoomMultiplier: 0.1 @@ -79,15 +94,11 @@ Item { 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() + function updatePosition(deltaX, deltaY) { + settingsInstance.xmin = (canvas.px2x(canvas.x2px(settingsInstance.xmin)-deltaX)) + settingsInstance.ymax += deltaY/canvas.yzoom + settingsInstance.ymax = settingsInstance.ymax.toFixed(4) + settingsInstance.changed() parent.positionChanged(deltaX, deltaY) } @@ -97,49 +108,43 @@ Item { 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) + let deltaX = mouse.x - prevX + let deltaY = mouse.y - prevY + updatePosition(deltaX, deltaY) prevX = mouse.x prevY = mouse.y positionChangeTimer = 0 } } - onReleased: function(mouse) { - let deltaX = mouse.x - parent.prevX - let deltaY = mouse.y - parent.prevY - updatePosition(deltaX, deltaY, true) + let deltaX = mouse.x - prevX + let deltaY = mouse.y - prevY + updatePosition(deltaX, deltaY) parent.endPositionChange(deltaX, deltaY) } - onWheel: function(wheel) { // Scrolling let scrollSteps = Math.round(wheel.angleDelta.y / 120) - let zoomMultiplier = Math.pow(1+parent.baseZoomMultiplier, Math.abs(scrollSteps)) + let zoomMultiplier = Math.pow(1+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) + let xZoomDelta = (settingsInstance.xzoom*zoomMultiplier - settingsInstance.xzoom) + let yZoomDelta = (settingsInstance.yzoom*zoomMultiplier - settingsInstance.yzoom) if(scrollSteps < 0) { // Negative scroll xZoomDelta *= -1 yZoomDelta *= -1 } - let newXZoom = (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() + let newXZoom = (settingsInstance.xzoom+xZoomDelta).toFixed(0) + let newYZoom = (settingsInstance.yzoom+yZoomDelta).toFixed(0) + if(newXZoom == settingsInstance.xzoom) // No change, allow more precision. + newXZoom = (settingsInstance.xzoom+xZoomDelta).toFixed(4) + if(newYZoom == settingsInstance.yzoom) // No change, allow more precision. + newYZoom = (settingsInstance.yzoom+yZoomDelta).toFixed(4) + settingsInstance.xzoom = newXZoom + settingsInstance.yzoom = newYZoom + settingsInstance.changed() } } } 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/settings.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/settings.svg similarity index 100% rename from assets/icons/common/settings.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/common/settings.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/assets/icons/logarithmplotter.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/logarithmplotter.svg similarity index 100% rename from assets/icons/logarithmplotter.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/logarithmplotter.svg diff --git a/assets/icons/objects/Function.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Function.svg similarity index 100% rename from assets/icons/objects/Function.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Function.svg diff --git a/assets/icons/objects/Gain Bode.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Gain Bode.svg similarity index 100% rename from assets/icons/objects/Gain Bode.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Gain Bode.svg diff --git a/assets/icons/objects/Phase Bode.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Phase Bode.svg similarity index 100% rename from assets/icons/objects/Phase Bode.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Phase Bode.svg diff --git a/assets/icons/objects/Point.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Point.svg similarity index 100% rename from assets/icons/objects/Point.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Point.svg 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/assets/icons/objects/Sequence.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Sequence.svg similarity index 100% rename from assets/icons/objects/Sequence.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Sequence.svg diff --git a/assets/icons/objects/Text.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Text.svg similarity index 100% rename from assets/icons/objects/Text.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/Text.svg diff --git a/assets/icons/objects/X Cursor.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/X Cursor.svg similarity index 100% rename from assets/icons/objects/X Cursor.svg rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/objects/X Cursor.svg 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/ω_0.svg b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/ω_0.svg new file mode 120000 index 0000000..21699e9 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/icons/settings/custom/ω_0.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..1afd66b --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js @@ -0,0 +1,1852 @@ +// 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'; + +// Additional variable characters. +var ADDITIONAL_VARCHARS = [ + "α","β","γ","δ","ε","ζ","η", + "π","θ","κ","λ","μ","ξ","ρ", + "ς","σ","τ","φ","χ","ψ","ω", + "Γ","Δ","Θ","Λ","Ξ","Π","Σ", + "Φ","Ψ","Ω","ₐ","ₑ","ₒ","ₓ", + "ₕ","ₖ","ₗ","ₘ","ₙ","ₚ","ₛ", + "ₜ","¹","²","³","⁴","⁵","⁶", + "⁷","⁸","⁹","⁰","₁","₂","₃", + "₄","₅","₆","₇","₈","₉","₀", + "∞","π" +] + +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(); + //console.log("Getting property ", item.value, "of", n1) + 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 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) { + // 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 { + var 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 === 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(); + //console.log("Getting property", item.value, "of", n1,":",n1[item.value]) + 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, 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(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 === 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(qsTranslate('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 = Object.assign({}, values, this.parser.consts) + 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; + var consts = this.parser.consts + return vars.filter(function (name) { + return !(name in functions) && !(name in consts); + }); +}; + +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.builtinConsts = parser.builtinConsts; + 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(qsTranslate('error', 'Unknown character "%1".').arg(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() && !ADDITIONAL_VARCHARS.includes(c)) { + 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.builtinConsts) { + this.current = this.newToken(TNUMBER, this.builtinConsts[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() && !ADDITIONAL_VARCHARS.includes(c)) { + 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(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; + 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(qsTranslate('error', 'Parse error [%1:%2]: %3').arg(coords.line).arg(coords.column).arg(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(qsTranslate('error', 'Parse error [%1:%2]: %3') + .arg(coords.line).arg(coords.column) + .arg(qsTranslate('error', 'Expected %1').arg(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(qsTranslate('error', 'Unexpected %1').arg(this.nextToken)); + } +}; + +ParserState.prototype.parseExpression = function (instr) { + var exprInstr = []; + if (this.parseUntilEndStatement(instr, exprInstr)) { + return; + } + this.parseConditionalExpression(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.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(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)); + } + } +}; + +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 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')) + } +} + +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')) + } +} + +function arrayMap(f, a) { + if (typeof f !== 'function') { + throw new EvalError(qsTranslate('error', 'First argument to map is not a function.')); + } + if (!Array.isArray(a)) { + throw new EvalError(qsTranslate('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 EvalError(qsTranslate('error', 'First argument to fold is not a function.')); + } + if (!Array.isArray(a)) { + throw new EvalError(qsTranslate('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 EvalError(qsTranslate('error', 'First argument to filter is not a function.')); + } + if (!Array.isArray(a)) { + throw new EvalError(qsTranslate('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(qsTranslate('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(qsTranslate('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, + 'Γ': gamma, + roundTo: roundTo, + map: arrayMap, + fold: arrayFold, + filter: arrayFilter, + indexOf: stringOrArrayIndexOf, + join: arrayJoin + }; + + // 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) { + var instr = []; + var 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 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', // Disable assignment + '[': 'array', + //'()=': 'fndef' // Diable function definition +}; + +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 + http://www.undefined.ch/mparser/index.html + + Ported to JavaScript and modified by Matthew Crumley (http://silentmatt.com/) + + Ported to QMLJS with modifications done accordingly done by Ad5001 (https://ad5001.eu) + + 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..9f921b5 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) 2021-2024 Ad5001 + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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/common/src/history/common.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.js similarity index 61% rename from common/src/history/common.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.js index 8c46757..2baf532 100644 --- a/common/src/history/common.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.js @@ -1,116 +1,111 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 - * + * Copyright (C) 2021-2024 Ad5001 + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -import History from "../module/history.mjs" -import Latex from "../module/latex.mjs" +.pragma library -export class Action { +.import "../math/latex.js" as Latex + +var themeTextColor; +var imageDepth = 2; +var fontSize = 14; + + +class Action { /** * Type of the action. - * + * * @returns {string} */ - type() { - return "Unknown" - } - + type(){return 'Unknown'} + /** * Icon associated with the action. - * + * * @returns {string} */ - icon() { - return "position" - } - + 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. + * + * @returns {string} */ - undo() { - } - + undo() {} + /** * Redoes the action. + * + * @returns {string} */ - redo() { - } - + redo() {} + /** * Export the action to a serializable format. * NOTE: These arguments will be reinputed in the constructor in this order. - * - * @returns {string[]} + * + * @returns {string} */ export() { return [this.targetName, this.targetType] } - + /** - * Returns a string with the human-readable description of the action. - * + * Returns a string with the human readable description of the action. + * * @returns {string} */ getReadableString() { - return "Unknown action" + 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 `` + 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} + * @returns {string} */ - async renderLatexAsHtml(latexString) { + 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 `` + let latexInfo = Latex.Renderer.render(latexString, imageDepth*(fontSize+2), themeTextColor).split(",") + return `` } - + /** - * Returns a string with the HTML-formatted description of the action. - * - * @returns {string|Promise} + * Returns a string with the HTML-formated description of the action. + * + * @returns {string} */ getHTMLString() { return this.getReadableString() diff --git a/common/src/history/create.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/create.js similarity index 57% rename from common/src/history/create.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/create.js index fb27f62..8eca385 100644 --- a/common/src/history/create.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/create.js @@ -1,69 +1,65 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 - * + * Copyright (C) 2021-2024 Ad5001 + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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) + //let targetIndex = Objects.getObjectsName(this.targetType).indexOf(this.targetName) + //delete Objects.currentObjectsByName[this.targetName] + //Objects.currentObjects[this.targetType][targetIndex].delete() + //Objects.currentObjects[this.targetType].splice(targetIndex, 1) } - + redo() { 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..c70d2d0 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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/common/src/history/editproperty.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/editproperty.js similarity index 57% rename from common/src/history/editproperty.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/editproperty.js index 2a81e7a..9b712f2 100644 --- a/common/src/history/editproperty.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/editproperty.js @@ -1,52 +1,39 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 - * + * Copyright (C) 2021-2024 Ad5001 + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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" +.pragma library -/** - * Action used everytime an object's property has been changed. - */ -export default class EditedProperty extends Action { - type() { - return "EditedProperty" +.import "../objects.js" as Objects +.import "../math/latex.js" as Latex +.import "../mathlib.js" as MathLib +.import "../objs/common.js" as Common +.import "common.js" as C + +class EditedProperty extends C.Action { + // Action used everytime an object's property has been changed + type(){return 'EditedProperty'} + + icon(){return 'modify'} + + color(darkVer=false){ + return darkVer ? 'darkslateblue' : 'cyan'; } - - 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 @@ -55,12 +42,12 @@ export default class EditedProperty extends Action { 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) + 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); @@ -69,91 +56,78 @@ export default class EditedProperty extends Action { } 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) { + } 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.prevString = "" - this.nextString = "" - this._renderPromises = [] + this.prevString = ""; + this.nextString = ""; 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 + break; case "ObjectType": this.prevString = this.previousValue == null ? "null" : this.previousValue.name this.nextString = this.newValue == null ? "null" : this.newValue.name - break + break; case "List": this.prevString = this.previousValue.join(",") this.nextString = this.newValue.name.join(",") - break + break; case "Dict": this.prevString = JSON.stringify(this.previousValue) this.nextString = JSON.stringify(this.newValue) - break + break; case "Expression": this.prevString = this.previousValue == null ? "null" : this.previousValue.toString() this.nextString = this.newValue == null ? "null" : this.newValue.toString() - break + 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) - ] + this.prevHTML = ' '+this.prevString+' ' + this.nextHTML = ' '+this.nextString+' ' + if(Latex.enabled && typeof this.propertyType == 'object' && this.propertyType.type == "Expression") { + this.prevHTML= this.renderLatexAsHtml(this.previousValue.latexMarkup) + this.nextHTML= this.renderLatexAsHtml(this.newValue.latexMarkup) } } - + 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 qsTr('%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) + + getHTMLString() { + return qsTr('%1 of %2 changed from %3 to %4.') + .arg(this.targetPropertyReadable) + .arg(' ' + this.targetName + ' ') + .arg(this.prevHTML) + .arg(this.nextHTML) } } 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..89f97c3 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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 = "") { @@ -43,16 +45,20 @@ export default class NameChanged extends EditedProperty { redo() { Objects.renameObject(this.previousValue, this.newValue) + //let obj = Objects.currentObjectsByName[this.previousValue] + //obj.name = this.newValue + //Objects.currentObjectsByName[this.newValue] = obj + //delete Objects.currentObjectsByName[this.previousValue] } getReadableString() { - 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/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/position.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/position.js new file mode 100644 index 0000000..c171124 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/position.js @@ -0,0 +1,95 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.pragma library + +.import "../objects.js" as Objects +.import "../mathlib.js" as MathLib +.import "../math/latex.js" as Latex +.import "../utils.js" as Utils +.import "../objs/common.js" as Common +.import "common.js" as C + +class EditedPosition extends C.Action { + // Action used for objects that have a X and Y expression properties (points, texts...) + 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()})` + // Render as LaTeX + if(Latex.enabled) { + this.prevHTML = this.renderLatexAsHtml(`\\left(${this.previousXValue.latexMarkup},${this.previousYValue.latexMarkup}\\right)`) + this.nextHTML = this.renderLatexAsHtml(`\\left(${this.newXValue.latexMarkup},${this.newYValue.latexMarkup}\\right)`) + } else { + this.prevHTML = ' '+Utils.escapeHTML(this.prevString)+' ' + this.nextHTML = ' '+Utils.escapeHTML(this.nextString)+' ' + } + + } + + export() { + return [this.targetName, this.targetType, + this.previousXValue.toEditableString(), this.newXValue.toEditableString(), + this.previousYValue.toEditableString(), this.newYValue.toEditableString()] + } + + getReadableString() { + return qsTr('Position of %1 %2 set from "%3" to "%4".') + .arg(Objects.types[this.targetType].displayType()) + .arg(this.targetName).arg(this.prevString).arg(this.nextString) + } + + getHTMLString() { + return qsTr('Position of %1 set from %2 to %3.') + .arg(' ' + this.targetName + ' ') + .arg(this.prevHTML) + .arg(this.nextHTML) + } +} 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..78981eb 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) 2021-2024 Ad5001 + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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 64% rename from common/src/history/index.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/historylib.js index a2ef1ea..d9fa812 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,27 +18,30 @@ // 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/position.js" as Pos +.import "history/visibility.js" as V +.import "history/name.js" as Name +.import "history/color.js" as Color + +var history = null; -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 EditedPosition = Pos.EditedPosition +var EditedVisibility = V.EditedVisibility +var NameChanged = Name.NameChanged +var ColorChanged = Color.ColorChanged -export const Actions = { +var Actions = { "Action": Action, "CreateNewObject": CreateNewObject, "DeleteObject": DeleteObject, 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..0858522 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js @@ -0,0 +1,99 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.pragma library + +.import "../expr-eval.js" as ExprEval +.import "../utils.js" as Utils +.import "latex.js" as Latex + +var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manually + "pi": Math.PI, + "PI": Math.PI, + "π": Math.PI, + "inf": Infinity, + "infinity": Infinity, + "Infinity": Infinity, + "∞": Infinity, + "e": Math.E, + "E": Math.E, + "true": true, + "false": false + +} + +var currentVars = {} +var currentObjectsByName = {} // Mirror of currentObjectsByName in objects.js + +const parser = new ExprEval.Parser() + +parser.consts = Object.assign({}, parser.consts, evalVariables) + +/** + * Parses arguments for a function, returns the corresponding JS function if it exists. + * Throws either usage error otherwise. + * @param {array} args - Arguments of the function, either [ ExecutableObject ] or [ string, variable ]. + * @param {string} usage1 - Usage for executable object. + * @param {string} usage2 - Usage for string function. + * @return {callable} JS function to call.. + */ +function parseArgumentsForFunction(args, usage1, usage2) { + let f, target, variable + if(args.length == 1) { + // Parse object + f = args[0] + if(typeof f != 'object' || !f.execute) + throw EvalError(qsTranslate('usage', 'Usage: %1').arg(usage1)) + let target = f + f = (x) => target.execute(x) + } else if(args.length == 2) { + // Parse variable + [f,variable] = args + if(typeof f != 'string' || typeof variable != 'string') + throw EvalError(qsTranslate('usage', 'Usage: %1').arg(usage2)) + f = parser.parse(f).toJSFunction(variable, currentVars) + } else + throw EvalError(qsTranslate('usage', 'Usage: %1 or\n%2').arg(usage1).arg(usage2)) + return f +} + +// Function definition +parser.functions.integral = function(a, b, ...args) { + let usage1 = qsTranslate('usage', 'integral(, , )') + let usage2 = qsTranslate('usage', 'integral(, , , )') + let f = parseArgumentsForFunction(args, usage1, usage2) + if(a == null || b == null) + throw EvalError(qsTranslate('usage', 'Usage: %1 or\n%2').arg(usage1).arg(usage2)) + + // https://en.wikipedia.org/wiki/Simpson%27s_rule + // Simpler, faster than tokenizing the expression + return (b-a)/6*(f(a)+4*f((a+b)/2)+f(b)) +} + +parser.functions.derivative = function(...args) { + let usage1 = qsTranslate('usage', 'derivative(, )') + let usage2 = qsTranslate('usage', 'derivative(, , )') + let x = args.pop() + let f = parseArgumentsForFunction(args, usage1, usage2) + if(x == null) + throw EvalError(qsTranslate('usage', 'Usage: %1 or\n%2').arg(usage1).arg(usage2)) + + let derivative_precision = x/10 + return (f(x+derivative_precision/2)-f(x-derivative_precision/2))/derivative_precision +} + diff --git a/common/src/math/domain.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/domain.js similarity index 65% rename from common/src/math/domain.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/domain.js index b81e527..6c47a23 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) 2021-2024 Ad5001 + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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,20 +63,24 @@ 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+*": @@ -94,6 +90,7 @@ export class Domain { case "ℝ*+": case "ℝ+*": return Domain.RPE + break; case "RME": case "REM": case "R-*": @@ -103,6 +100,7 @@ export class Domain { case "ℝ-*": case "ℝ*-": return Domain.RME + break; case "ℕ": case "N": case "ZP": @@ -110,10 +108,12 @@ export class Domain { case "ℤ⁺": case "ℤ+": return Domain.N + break; case "NLOG": case "ℕˡᵒᵍ": case "ℕLOG": return Domain.NLog + break; case "NE": case "NP": case "N*": @@ -130,14 +130,17 @@ export class Domain { 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-*": @@ -147,12 +150,15 @@ export class Domain { 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 +166,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 +218,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 +227,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 +239,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 +286,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 +300,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 +423,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 +440,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 +448,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 dom2 = parseDomain(domains.pop()) + var dom1 = parseDomain(domains.join('∪')) return dom1.union(dom2) } } @@ -475,7 +461,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 +469,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 +494,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 +506,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 +514,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 +536,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..4a57746 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js @@ -0,0 +1,99 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.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 = Utils.exponentsToExpression(expr) + this.calc = C.parser.parse(this.expr).simplify() + this.cached = this.isConstant() + this.cachedValue = this.cached && this.allRequirementsFullfilled() ? this.calc.evaluate(C.currentObjectsByName) : null + this.latexMarkup = Latex.expression(this.calc.tokens) + } + + isConstant() { + let vars = this.calc.variables() + return !vars.includes("x") && !vars.includes("n") + } + + requiredObjects() { + return this.calc.variables().filter(objName => objName != "x" && objName != "n") + } + + allRequirementsFullfilled() { + return this.requiredObjects().every(objName => objName in C.currentObjectsByName) + } + + undefinedVariables() { + return this.requiredObjects().filter(objName => !(objName in C.currentObjectsByName)) + } + + recache() { + if(this.cached) + this.cachedValue = this.calc.evaluate(C.currentObjectsByName) + } + + execute(x = 1) { + if(this.cached) { + if(this.cachedValue == null) + this.cachedValue = this.calc.evaluate(C.currentObjectsByName) + return this.cachedValue + } + C.currentVars = Object.assign({'x': x}, C.currentObjectsByName) + return this.calc.evaluate(C.currentVars) + } + + simplify(x) { + var expr = this.calc.substitute('x', x).simplify() + if(expr.evaluate() == 0) return '0' + var str = Utils.makeExpressionReadable(expr.toString()); + if(str != undefined && str.match(/^\d*\.\d+$/)) { + 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) { + let 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..50883b2 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.js @@ -0,0 +1,282 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.pragma library + +.import "../expr-eval.js" as ExprEval + + +/** + * true if latex has been enabled by the user, false otherwise. + */ +var enabled = false +/** + * LaTeX python backend QObject. + */ +var Renderer = null +/** + * Default window color used to render LaTeX formulas. + */ +var defaultColor = "black" + +/** + * Puts element within parenthesis. + * + * @param {string} elem - element to put within parenthesis. + * @returns {string} + */ +function par(elem) { + return '(' + elem + ')' +} + +/** + * Checks if the element contains at least one of the elements of + * the string array contents, but not at the first position of the string, + * and returns the parenthesis version if so. + * + * @param {string} elem - element to put within parenthesis. + * @param {Array} contents - Array of elements to put within parenthesis. + * @returns {string} + */ +function parif(elem, contents) { + elem = elem.toString() + if(elem[0] != "(" && elem[elem.length-1] != ")" && contents.some(x => elem.indexOf(x) > 0)) + return par(elem) + if(elem[0] == "(" && elem[elem.length-1] == ")") + return elem.substr(1, elem.length-2) + return elem +} + + +/** + * Creates a latex expression for a function. + * + * @param {string} f - Function to convert + * @param {Array} args - Arguments of the function + * @returns {string} + */ +function functionToLatex(f, args) { + switch(f) { + case "derivative": + if(args.length == 3) + return '\\frac{d' + args[0].substr(1, args[0].length-2).replace(new RegExp(args[1].substr(1, args[1].length-2), 'g'), 'x') + '}{dx}'; + else + return '\\frac{d' + args[0] + '}{dx}(x)'; + break; + case "integral": + if(args.length == 4) + return '\\int\\limits_{' + args[0] + '}^{' + args[1] + '}' + args[2].substr(1, args[2].length-2) + ' d' + args[3].substr(1, args[3].length-2); + else + return '\\int\\limits_{' + args[0] + '}^{' + args[1] + '}' + args[2] + '(t) dt'; + break; + case "sqrt": + return '\\sqrt\\left(' + args.join(', ') + '\\right)'; + break; + case "abs": + return '\\left|' + args.join(', ') + '\\right|'; + break; + case "floor": + return '\\left\\lfloor' + args.join(', ') + '\\right\\rfloor'; + break; + case "ceil": + return '\\left\\lceil' + args.join(', ') + '\\right\\rceil'; + break; + default: + return '\\mathrm{' + f + '}\\left(' + args.join(', ') + '\\right)'; + break; + } +} + + +/** + * Creates a latex variable from a variable. + * + * @param {string} vari - variable text to convert + * @param {bool} wrapIn$ - checks whether the escaped chars should be escaped + * @returns {string} + */ +function variable(vari, wrapIn$ = false) { + let unicodechars = ["α","β","γ","δ","ε","ζ","η", + "π","θ","κ","λ","μ","ξ","ρ", + "ς","σ","τ","φ","χ","ψ","ω", + "Γ","Δ","Θ","Λ","Ξ","Π","Σ", + "Φ","Ψ","Ω","ₐ","ₑ","ₒ","ₓ", + "ₕ","ₖ","ₗ","ₘ","ₙ","ₚ","ₛ", + "ₜ","¹","²","³","⁴","⁵","⁶", + "⁷","⁸","⁹","⁰","₁","₂","₃", + "₄","₅","₆","₇","₈","₉","₀", + "pi", "∞"] + let equivalchars = ["\\alpha","\\beta","\\gamma","\\delta","\\epsilon","\\zeta","\\eta", + "\\pi","\\theta","\\kappa","\\lambda","\\mu","\\xi","\\rho", + "\\sigma","\\sigma","\\tau","\\phi","\\chi","\\psi","\\omega", + "\\Gamma","\\Delta","\\Theta","\\Lambda","\\Xi","\\Pi","\\Sigma", + "\\Phy","\\Psi","\\Omega","{}_{a}","{}_{e}","{}_{o}","{}_{x}", + "{}_{h}","{}_{k}","{}_{l}","{}_{m}","{}_{n}","{}_{p}","{}_{s}", + "{}_{t}","{}^{1}","{}^{2}","{}^{3}","{}^{4}","{}^{5}","{}^{6}", + "{}^{7}","{}^{8}","{}^{9}","{}^{0}","{}_{1}","{}_{2}","{}_{3}", + "{}_{4}","{}_{5}","{}_{6}","{}_{7}","{}_{8}","{}_{9}","{}_{0}", + "\\pi", "\\infty"] + if(wrapIn$) + for(let i = 0; i < unicodechars.length; i++) { + if(vari.includes(unicodechars[i])) + vari = vari.replace(new RegExp(unicodechars[i], 'g'), '$'+equivalchars[i]+'$') + } + else + for(let i = 0; i < unicodechars.length; i++) { + if(vari.includes(unicodechars[i])) + vari = vari.replace(new RegExp(unicodechars[i], 'g'), equivalchars[i]) + } + return vari; +} + +/** + * Converts expr-eval tokens to a latex string. + * + * @param {Array} tokens - expr-eval tokens list + * @returns {string} + */ +function expression(tokens) { + var nstack = []; + var n1, n2, n3; + var f, args, argCount; + for (var i = 0; i < tokens.length; i++) { + var item = tokens[i]; + var type = item.type; + + switch(type) { + case ExprEval.INUMBER: + if(item.value == Infinity) { + nstack.push("\\infty") + } else if(typeof item.value === 'number' && item.value < 0) { + nstack.push(par(item.value)); + } else if(Array.isArray(item.value)) { + nstack.push('[' + item.value.map(ExprEval.escapeValue).join(', ') + ']'); + } else { + nstack.push(ExprEval.escapeValue(item.value)); + } + break; + case ExprEval.IOP2: + n2 = nstack.pop(); + n1 = nstack.pop(); + f = item.value; + switch(f) { + case '-': + case '+': + nstack.push(n1 + f + n2); + break; + case '||': + case 'or': + case '&&': + case 'and': + case '==': + case '!=': + nstack.push(par(n1) + f + par(n2)); + break; + case '*': + if(n2 == "\\pi" || n2 == "e" || n2 == "x" || n2 == "n") + nstack.push(parif(n1,['+','-']) + n2) + else + nstack.push(parif(n1,['+','-']) + " \\times " + parif(n2,['+','-'])); + break; + case '/': + nstack.push("\\frac{" + n1 + "}{" + n2 + "}"); + break; + case '^': + nstack.push(parif(n1,['+','-','*','/','!']) + "^{" + n2 + "}"); + break; + case '%': + nstack.push(parif(n1,['+','-','*','/','!','^']) + " \\mathrm{mod} " + parif(n2,['+','-','*','/','!','^'])); + break; + case '[': + nstack.push(n1 + '[' + n2 + ']'); + break; + default: + throw new EvalError("Unknown operator " + ope + "."); + } + break; + case ExprEval.IOP3: // Thirdiary operator + n3 = nstack.pop(); + n2 = nstack.pop(); + n1 = nstack.pop(); + f = item.value; + if (f === '?') { + nstack.push('(' + n1 + ' ? ' + n2 + ' : ' + n3 + ')'); + } else { + throw new EvalError('Unknown operator ' + ope + '.'); + } + break; + case ExprEval.IVAR: + case ExprEval.IVARNAME: + nstack.push(variable(item.value.toString())); + break; + case ExprEval.IOP1: // Unary operator + n1 = nstack.pop(); + f = item.value; + switch(f) { + case '-': + case '+': + nstack.push(par(f + n1)); + break; + case '!': + nstack.push(parif(n1,['+','-','*','/','^']) + '!'); + break; + default: + nstack.push(f + parif(n1,['+','-','*','/','^'])); + break; + } + break; + case ExprEval.IFUNCALL: + argCount = item.value; + args = []; + while (argCount-- > 0) { + args.unshift(nstack.pop()); + } + f = nstack.pop(); + // Handling various functions + nstack.push(functionToLatex(f, args)) + break; + case ExprEval.IFUNDEF: + nstack.push(par(n1 + '(' + args.join(', ') + ') = ' + n2)); + break; + case ExprEval.IMEMBER: + n1 = nstack.pop(); + nstack.push(n1 + '.' + item.value); + break; + case ExprEval.IARRAY: + argCount = item.value; + args = []; + while (argCount-- > 0) { + args.unshift(nstack.pop()); + } + nstack.push('[' + args.join(', ') + ']'); + break; + case ExprEval.IEXPR: + nstack.push('(' + expression(item.value) + ')'); + break; + case ExprEval.IENDSTATEMENT: + break; + default: + throw new EvalError('invalid Expression'); + break; + } + } + if (nstack.length > 1) { + nstack = [ nstack.join(';') ] + } + return String(nstack[0]); +} diff --git a/common/src/math/sequence.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.js similarity index 56% rename from common/src/math/sequence.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/sequence.js index 6049e53..352a386 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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,9 +35,9 @@ 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() } @@ -43,7 +45,7 @@ export class Sequence extends Expr.Expression { } isConstant() { - return this.expr.indexOf("n") === -1 + return this.expr.indexOf("n") == -1 } execute(n = 1) { @@ -54,31 +56,28 @@ 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( + var str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString()) + var expr = C.parser.parse(str).simplify() + C.currentVars = Object.assign( {'n': n-this.valuePlus}, // Just in case, add n (for custom functions) - Objects.currentObjectsByName, - {[this.name]: this.calcValues} + C.currentObjectsByName ) - this.calcValues[n] = expr.evaluate(ExprParser.currentVars) + C.currentVars[this.name] = this.calcValues + 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 +85,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/parsing/index.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/mathlib.js similarity index 55% rename from common/src/parsing/index.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/mathlib.js index f0fde37..9ecb377 100644 --- a/common/src/parsing/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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,17 +16,26 @@ * 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 "math/expression.js" as Expr +.import "math/sequence.js" as Seq -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 +.import "math/domain.js" as Dom + +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..90dd468 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objects.js @@ -0,0 +1,96 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.pragma library + +.import "utils.js" as Utils +.import "math/common.js" as MathCommons +.import "parameters.js" as P + +var types = {} + +var currentObjects = {} +var currentObjectsByName = {} +MathCommons.currentObjectsByName = currentObjectsByName // Required for using objects in variables. + +function renameObject(oldName, newName) { + /** + * Renames an object from its old name to the new one. + * @param {string} oldName - Current name of the object. + * @param {string} newName - Name to rename the object to. + */ + let obj = currentObjectsByName[oldName] + delete currentObjectsByName[oldName] + currentObjectsByName[newName] = obj + obj.name = newName +} + +function deleteObject(objName) { + /** + * Deletes an object by its given name. + * @param {string} objName - Current name of the object. + */ + let obj = currentObjectsByName[objName] + currentObjects[obj.type].splice(currentObjects[obj.type].indexOf(obj),1) + obj.delete() + delete currentObjectsByName[objName] +} + +function getObjectsName(objType) { + /** + * Gets a list of all names of a certain object type. + * @param {string} objType - Type of the object to query. Can be ExecutableObject for all ExecutableObjects. + * @return {array} List of names of the objects. + */ + if(objType == "ExecutableObject") { + // NOTE: QMLJS does not support flatMap. + // return getExecutableTypes().flatMap(elemType => currentObjects[elemType].map(obj => obj.name)) + let types = getExecutableTypes() + let elementNames = [''] + for(let elemType of types) + elementNames = elementNames.concat(currentObjects[elemType].map(obj => obj.name)) + return elementNames + } + if(currentObjects[objType] == undefined) return [] + return currentObjects[objType].map(obj => obj.name) +} + +function getExecutableTypes() { + /** + * Returns a list of all object types which are executable objects. + * @return {array} List of all object types which are executable objects. + */ + return Object.keys(currentObjects).filter(objType => types[objType].executable()) +} + +function createNewRegisteredObject(objType, args=[]) { + /** + * Creates and register an object in the database. + * @param {string} objType - Type of the object to create. + * @param {string} args - List of arguments for the objects (can be empty). + * @return {[objType]} Newly created object. + */ + if(Object.keys(types).indexOf(objType) == -1) return null // Object type does not exist. + var newobj = new types[objType](...args) + if(Object.keys(currentObjects).indexOf(objType) == -1) { + currentObjects[objType] = [] + } + currentObjects[objType].push(newobj) + currentObjectsByName[newobj.name] = newobj + return newobj +} diff --git a/common/src/utils/other.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/autoload.js similarity index 52% rename from common/src/utils/other.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/autoload.js index 50f84f4..8ee04cf 100644 --- a/common/src/utils/other.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/autoload.js @@ -1,41 +1,42 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 - * + * Copyright (C) 2021-2024 Ad5001 + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +.pragma library +.import "common.js" as C +.import "point.js" as P +.import "text.js" as T +.import "function.js" as F +.import "gainbode.js" as GB +.import "phasebode.js" as PB +.import "sommegainsbode.js" as SGB +.import "sommephasesbode.js" as SPB +.import "xcursor.js" as X +.import "sequence.js" as S +.import "repartition.js" as R -/** - * 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, ">") -} +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/common/src/objs/common.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js similarity index 51% rename from common/src/objs/common.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js index 602856a..bc31bac 100644 --- a/common/src/objs/common.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/common.js @@ -1,120 +1,118 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 - * + * Copyright (C) 2021-2024 Ad5001 + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -import Objects from "../module/objects.mjs" -import Latex from "../module/latex.mjs" -import { getRandomColor } from "../utils/index.mjs" -import { ensureTypeSafety, serializesByPropertyType } from "../parameters.mjs" +.pragma library + +.import "../utils.js" as Utils +.import "../objects.js" as Objects +.import "../math/latex.js" as Latex +.import "../parameters.js" as P +.import "../math/common.js" as C // This file contains the default data to be imported from all other objects +/** + * Creates a new name for an object, based on the \c allowedLetters. + * If variables with each of the allowedLetters is created, a subscript + * number is added to the name. + * @param {string} prefix - Prefix to the name. + * @return {string} New unused name for a new object. + */ +function getNewName(allowedLetters, prefix='') { + // Allows to get a new name, based on the allowed letters, + // as well as adding a sub number when needs be. + var newid = 0 + var ret + do { + var letter = allowedLetters[newid % allowedLetters.length] + var num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length) + ret = prefix + letter + (num > 0 ? Utils.textsub(num-1) : '') + newid += 1 + } while(ret in Objects.currentObjectsByName) + return ret +} + /** * Class to extend for every type of object that * can be drawn on the canvas. */ -export class DrawableObject { +class DrawableObject { /** * Base name of the object. Needs to be constant over time. * @return {string} Type of the object. */ - static type() { - return "Unknown" - } - + static type(){return 'Unknown'} + /** * Translated name of the object to be shown to the user. * @return {string} */ - static displayType() { - return "Unknown" - } - + static displayType(){return 'Unknown'} + /** * Translated name of the object in plural form to be shown to the user. * @return {string} */ - static displayTypeMultiple() { - return "Unknowns" - } - + 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} + * @return {bool} */ - static createable() { - return true - } - + 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 + * 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 + * Enums that are translated should be indexed in parameters.js and * then be linked directly here. - * - * @return {Object.} + * + * @return {Dictionary} */ - static properties() { - return {} - } - + 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} + * @return {bool} */ - 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) - } - + static executable() {return false} + /** * 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 + * @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 = getRandomColor() + constructor(name, visible = true, color = null, labelContent = 'name + value') { + if(color == null) color = Utils.getRandomColor() this.type = this.constructor.type() this.name = name this.visible = visible @@ -123,20 +121,17 @@ export class DrawableObject { this.requiredBy = [] this.requires = [] } - + /** - * Serializes the object in an array that can be JSON serialized. + * 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() { - 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 + // 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. @@ -145,53 +140,53 @@ export class DrawableObject { getReadableString() { return `${this.name} = Unknown` } - + /** * Latex markuped version of the readable string. - * Every non latin character should be passed as latex symbols and formulas + * Every non latin character should be passed as latex symbols and formulas * should be in latex form. - * See ../latex.mjs for helper methods. + * See ../latex.js for helper methods. * @return {string} */ getLatexString() { return this.getReadableString() } - + /** - * Readable string content of the label depending on the value of the latexContent. + * Readable string content of the label depending on the value of the \c latexContent. * @return {string} */ getLabel() { switch(this.labelContent) { - case "name": + case 'name': return this.name - case "name + value": + case 'name + value': return this.getReadableString() - case "null": - return "" - + 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 + * 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.mjs for helper methods. + * See ../latex.js for helper methods. * @return {string} */ getLatexLabel() { switch(this.labelContent) { - case "name": + case 'name': return Latex.variable(this.name) - case "name + value": + case 'name + value': return this.getLatexString() - case "null": - return "" - + case 'null': + return '' + } } - + /** * Returns the recursive list of objects this one depends on. * @return {array} @@ -202,33 +197,32 @@ export class DrawableObject { 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) + obj.requiredBy = obj.requiredBy.filter(dep => dep != this) // Checking objects this one depends on this.requires = [] - let currentObjectsByName = Objects.currentObjectsByName let properties = this.constructor.properties() for(let property in properties) - if(typeof properties[property] == "object" && "type" in properties[property]) - if(properties[property].type === "Expression" && this[property] != null) { + if(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(objName in C.currentObjectsByName && !this.requires.includes(C.currentObjectsByName[objName])) { + this.requires.push(C.currentObjectsByName[objName]) + C.currentObjectsByName[objName].requiredBy.push(this) } } - if(this[property].canBeCached && this[property].requiredObjects().length > 0) + if(this[property].cached && this[property].requiredObjects().length > 0) // Recalculate this[property].recache() - - } else if(properties[property].type === "ObjectType" && this[property] != null) { + + } else if(properties[property].type == 'ObjectType' && this[property] != null) { // Object dependency this.requires.push(this[property]) this[property].requiredBy.push(this) @@ -237,7 +231,7 @@ export class DrawableObject { for(let req of this.requiredBy) req.update() } - + /** * Callback method when the object is about to get deleted. */ @@ -245,174 +239,175 @@ export class DrawableObject { for(let toRemove of this.requiredBy) { // Normally, there should be none here, but better leave nothing just in case. Objects.deleteObject(toRemove.name) } - 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 + * Abstract method. Draw the object onto the \c canvas with the 2D context \c ctx. + * @param {Canvas} canvas + * @param {Context2D} ctx */ - draw(canvas) { - } - + draw(canvas, ctx) {} + /** - * 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 + * 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 {{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 + * @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 + 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 + * Automatically 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 drawFunctionLatex (x,y,imageData) and - * drawFunctionText (x,y,text) depending on whether to use latex. - * - * @param {CanvasAPI} canvas + * 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 {boolean} forceText - Force the rendering of the label as text + * @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, labelPosition, posX, posY, forceText = false, - getLatexFunction = null, getTextFunction = null, drawFunctionLatex = null, drawFunctionText = null) { + 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(ltxImg.source, x, y, ltxImg.width, ltxImg.height) + 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(text, x, y + textSize.height) // Positioned from left bottom + drawFunctionText = (x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, y+textSize.height) // Positioned from left bottom // Drawing the label - if(!forceText && Latex.enabled) { + let offset + if(!forceText && Latex.enabled) { // TODO: Check for user setting with Latex. // 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) + 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() - 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)) + 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 + return this.name; } } - /** - * Class to be extended for every object on which + * Class to be extended for every object on which * an y for a x can be computed with the execute function. * 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 { +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 - } - + execute(x = 1) {return 0} /** * Returns false if the object isn't defined on the given x, true otherwise. - * + * * @param {number} x - * @returns {boolean} + * @returns {bool} */ - canExecute(x = 1) { - return true - } - + canExecute(x = 1) {return true} /** * Returns the simplified expression string for a given x. - * + * * @param {number} x - * @returns {string|Expression} + * @returns {bool} */ - simplify(x = 1) { - return "0" - } - + simplify(x = 1) {return '0'} + + /** * True if this object can be executed, so that an y value might be computed * for an x value. Only ExecutableObjects have that property set to true. - * @return {boolean} + * @return {bool} */ - static executable() { - return true - } + static executable() {return true} } - +/** + * Registers the object \c obj in the object list. + * @param {DrawableObject} obj - Object to be registered. + */ +function registerObject(obj) { + // Registers an object to be used in LogarithmPlotter. + // This function is called from autoload.js + if(obj.prototype instanceof DrawableObject) { + Objects.types[obj.type()] = obj + } else { + console.error("Could not register object " + (obj.type()) + ", as it isn't a DrawableObject.") + } +} diff --git a/common/src/objs/function.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.js similarity index 51% rename from common/src/objs/function.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.js index fec90ac..aa43a08 100644 --- a/common/src/objs/function.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/function.js @@ -1,74 +1,64 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 - * + * Copyright (C) 2021-2024 Ad5001 + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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" +.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 -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") +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')]: 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','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) - if(typeof expression == "number" || typeof expression == "string") expression = new Expression(expression.toString()) + if(typeof expression == 'number' || typeof expression == 'string') expression = new MathLib.Expression(expression.toString()) this.expression = expression - if(typeof definitionDomain == "string") definitionDomain = parseDomain(definitionDomain) + if(typeof definitionDomain == 'string') definitionDomain = MathLib.parseDomain(definitionDomain) this.definitionDomain = definitionDomain - if(typeof destinationDomain == "string") destinationDomain = parseDomain(destinationDomain) + if(typeof destinationDomain == 'string') destinationDomain = MathLib.parseDomain(destinationDomain) this.destinationDomain = destinationDomain this.displayMode = displayMode this.labelPosition = labelPosition @@ -76,74 +66,76 @@ export default class Function extends ExecutableObject { 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()}` + 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}` + return `${this.name}(x) = ${this.expression.toString()}\nD${Utils.textsub(this.name)} = ${this.definitionDomain}` } } - + getLatexString() { - if(this.displayMode === "application") { + if(this.displayMode == 'application') { return `${Latex.variable(this.name)}:\\begin{array}{llll}${this.definitionDomain.latexMarkup}\\textrm{ } & \\rightarrow & \\textrm{ }${this.destinationDomain.latexMarkup}\\\\ x\\textrm{ } & \\mapsto & \\textrm{ }${this.expression.latexMarkup}\\end{array}` } else { 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 "" + return '' } - - draw(canvas) { - Function.drawFunction(canvas, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines) + + draw(canvas, ctx) { + Function.drawFunction(canvas, ctx, 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))) + this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX))) } - - /** - * Reusable in other objects. - * Drawing small traits every few pixels - */ - static drawFunction(canvas, 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) { + + static drawFunction(canvas, ctx, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) { + // Reusable in other objects. + // Drawing small traits every 0.2px + var pxprecision = 10 + var previousX = canvas.px2x(0) + var previousY = null; + if(definitionDomain instanceof MathLib.SpecialDomain && definitionDomain.moveSupported) { // 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) { + while(previousX !== null && canvas.x2px(previousX) < canvas.canvasSize.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 + 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(canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY)) + canvas.drawDashedLine(ctx, 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) + 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 @@ -151,8 +143,8 @@ export default class Function extends ExecutableObject { } 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) + 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 { // Use max precision if function is trigonometrical on log scale. @@ -162,16 +154,16 @@ export default class Function extends ExecutableObject { // 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) { + for(let px = pxprecision; px < canvas.canvasSize.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 + let tmpPx = px-pxprecision do { - tmpPx++ + tmpPx++; previousX = canvas.px2x(tmpPx) - } while(!definitionDomain.includes(previousX) && currentX > previousX) + } while(!definitionDomain.includes(previousX)) // Recaclulate previousY previousY = expr.execute(previousX) } else if(!definitionDomain.includes(currentX)) { @@ -179,17 +171,17 @@ export default class Function extends ExecutableObject { // Augmenting the pixel precision until this condition is fulfilled. let tmpPx = px do { - tmpPx-- + tmpPx--; currentX = canvas.px2x(tmpPx) - } while(!definitionDomain.includes(currentX) && currentX > previousX) + } while(!definitionDomain.includes(currentX) && currentX != previousX) } // This max variation is needed for functions with asymptotical vertical lines (e.g. 1/x, tan x...) - let maxvariation = (canvas.px2y(0) - canvas.px2y(canvas.height)) + let maxvariation = (canvas.px2y(0)-canvas.px2y(canvas.canvasSize.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)) + if(previousY != null && destinationDomain.includes(previousY) && Math.abs(previousY-currentY) < maxvariation) { + canvas.drawLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY)) } } 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..9604656 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/gainbode.js @@ -0,0 +1,146 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.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')]: 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 = Common.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', [Common.getNewName('ω'), true, this.color, 'name']) + HistoryLib.history.addToHistory(new HistoryLib.CreateNewObject(om_0.name, 'Point', om_0.export())) + om_0.update() + 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()}*(log10(x)-log10(${this.om_0.x}))+${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..edf9d68 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/phasebode.js @@ -0,0 +1,128 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.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')]: 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 = Common.getNewName('φ') + if(name == 'φ') name = 'φ₀' // φ is reserved for sum of BODE phases (Somme phases Bode). + super(name, visible, color, labelContent) + if(typeof phase == 'number' || typeof phase == 'string') phase = new MathLib.Expression(phase.toString()) + 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', [Common.getNewName('ω'), this.color, 'name']) + om_0.labelPosition = this.phase.execute() >= 0 ? 'above' : 'below' + HistoryLib.history.addToHistory(new HistoryLib.CreateNewObject(om_0.name, 'Point', om_0.export())) + 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..effdee1 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/point.js @@ -0,0 +1,83 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.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')]: 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 = Common.getNewName('ABCDEFJKLMNOPQRSTUVW') + super(name, visible, color, labelContent) + 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..e872876 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/repartition.js @@ -0,0 +1,153 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.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 { + [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', "F_") + 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() { + 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) { + 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 `${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.isVisible(keys[0],this.probabilities[keys[0]].replace(/,/g, '.'))) { + canvas.drawLine(ctx, + 0, + canvas.y2px(0), + canvas.x2px(keys[0]), + canvas.y2px(0) + ) + if(canvas.isVisible(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.isVisible(idx,currentY) || canvas.isVisible(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.isVisible(idx,currentY)) { + ctx.beginPath(); + ctx.arc(canvas.x2px(idx),canvas.y2px(currentY), 4, 0, 2 * Math.PI); + ctx.fill(); + } + if(canvas.isVisible(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.isVisible(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..c08aac8 --- /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) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.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..f8b3fee --- /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) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.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.ExecutableObject { + 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(name => Latex.variable(name)).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..eb1c3e0 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/sommephasesbode.js @@ -0,0 +1,129 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.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 { + [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(name => Latex.variable(name)).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..b513ea2 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/text.js @@ -0,0 +1,100 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.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 { + [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 = Common.getNewName('t') + super(name, visible, color, labelContent) + 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() { + // 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()}}"` + } + + export() { + return [this.name, this.visible, this.color.toString(), this.labelContent, this.x.toEditableString(), this.y.toEditableString(), this.labelPosition, this.text, this.disableLatex] + } + + 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 65% rename from common/src/objs/xcursor.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/objs/xcursor.js index 4ad511c..cea2864 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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,11 +47,11 @@ 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.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") { @@ -60,6 +62,12 @@ export default class XCursor extends DrawableObject { 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()}` @@ -72,30 +80,27 @@ export default class XCursor extends DrawableObject { ${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.min(this.rounding, approx.toString().length - intLength - 1) - approx = approx.toPrecision(rounding + intLength) - } - return approx - } 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..2ef194a --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parameters.js @@ -0,0 +1,128 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +class Expression { + constructor(...variables) { + this.type = 'Expression' + this.variables = variables + } + + toString() { + return this.variables.length == 0 ? 'Number' : `Expression(${this.variables.join(', ')})` + } +} + +class Enum { + constructor(...values) { + this.type = 'Enum' + this.values = values + this.translatedValues = values.map(x => qsTr(x)) + } + + toString() { + return this.type + } +} + +class ObjectType { + constructor(objType) { + this.type = 'ObjectType' + this.objType = objType + } + + toString() { + return this.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 + } + + toString() { + return this.objType + } +} + +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 + } + + toString() { + return 'Dictionary' + } +} + +// 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/common/src/parsing/README.md b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/README.md similarity index 84% rename from common/src/parsing/README.md rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/README.md index 8fa8a93..a0ff7e7 100644 --- a/common/src/parsing/README.md +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/README.md @@ -2,4 +2,4 @@ Here lies the potential new, abandoned, cleaner implementation of the parsing system that was supposed to replace expr-eval.js, but never came to be because it's unfinished. If somebody has the will to finish this, you're welcome to try, as I won't. -Currently, the tokenizer is complete in use to provide tokens for the syntax highlighting, and the reference to provide usage. +Currently, the tokenizer is complete in use to provide tokens for the syntax highlighting. diff --git a/common/src/parsing/common.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/common.js similarity index 71% rename from common/src/parsing/common.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/common.js index 998f038..53a5941 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) 2021-2024 Ad5001 + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +.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(!this.atEnd() && this.peek() == char) { + this.position++; } else { - this.raise("Unexpected character " + this.peek() + ". Expected character " + char) + this.raise("Unexpected character " + this.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/common/src/index.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/parsing.js similarity index 61% rename from common/src/index.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/parsing.js index 57efb01..b2cc6bc 100644 --- a/common/src/index.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/parsing.js @@ -1,6 +1,6 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 + * Copyright (C) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,13 +16,19 @@ * along with this program. If not, see . */ -import js from "./lib/polyfills/js.mjs" +.pragma library -export * as Utils from "./utils/index.mjs" +.import "reference.js" as Reference +.import "tokenizer.js" as TK +.import "common.js" as Common -import * as ObjsAutoload from "./objs/autoload.mjs" +var Input = Common.InputExpression +var TokenType = TK.TokenType +var Token = TK.Token +var Tokenizer = TK.ExpressionTokenizer -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 FUNCTIONS_LIST = Reference.FUNCTIONS_LIST +var FUNCTIONS = Reference.FUNCTIONS +var FUNCTIONS_USAGE = Reference.FUNCTIONS_USAGE +var CONSTANTS_LIST = Reference.CONSTANTS_LIST +var CONSTANTS = Reference.CONSTANTS diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/polyfill.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/polyfill.js new file mode 100644 index 0000000..df880c5 --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/polyfill.js @@ -0,0 +1,136 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// Contains polyfill math functions used for reference. + +.pragma library + +function factorial(x) { + if (x < 0) // Integrating by less than 0 + if(isFinite(n)) + return Infinity + else + throw new EvalError("Cannot calculate the factorial of -∞.") + + return gamma(x+1) +} + +let GAMMA_G = 4.7421875 +let 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 +] + +function gamma(n) { + if(n <= 0) // Integrating by less than 0 + if(isFinite(n)) + return Infinity + else + throw new EvalError("Cannot calculate Γ(-∞).") + + if(n >= 171.35) + return Infinity // Would return more than 2^1024 - 1 (aka Number.INT_MAX) + + if(n === Math.round(n) && isFinite(n)) { + // Calculating (n-1)! + let res = n - 1 + + for(let i = n - 2; i > 1; i++) + res *= i + + if(res === 0) + res = 1 // 0! is per definition 1 + + return res + } + + // Section below adapted function adapted from math.js + if(n < 0.5) + return Math.PI / (Math.sin(Math.PI * n) * gamma(1 - n)) + + if(n > 85.0) { // Extended Stirling Approx + let twoN = n * n + let threeN = twoN * n + let fourN = threeN * n + let 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 + let x = GAMMA_P[0] + for (let i = 1; i < GAMMA_P.length; ++i) { + x += GAMMA_P[i] / (n + i) + } + + let t = n + GAMMA_G + 0.5 + return Math.sqrt(2 * Math.PI) * Math.pow(t, n + 0.5) * Math.exp(-t) * x +} + +function arrayMap(f, arr) { + if (typeof f != 'function') + throw new EvalError(qsTranslate('error', 'First argument to map is not a function.')) + if (!Array.isArray(arr)) + throw new EvalError(qsTranslate('error', 'Second argument to map is not an array.')) + return arr.map(f) +} + +function arrayFold(f, init, arr) { + if (typeof f != 'function') + throw new EvalError(qsTranslate('error', 'First argument to fold is not a function.')) + if (!Array.isArray(arr)) + throw new EvalError(qsTranslate('error', 'Second argument to fold is not an array.')) + return arr.reduce(f, init) +} + +function arrayFilter(f, arr) { + if (typeof f != 'function') + throw new EvalError(qsTranslate('error', 'First argument to filter is not a function.')) + if (!Array.isArray(arr)) + throw new EvalError(qsTranslate('error', 'Second argument to filter is not an array.')) + return arr.filter(f) +} + +function arrayFilter(f, arr) { + if (typeof f != 'function') + throw new EvalError(qsTranslate('error', 'First argument to filter is not a function.')) + if (!Array.isArray(arr)) + throw new EvalError(qsTranslate('error', 'Second argument to filter is not an array.')) + return arr.filter(f) +} + +function arrayJoin(sep, arr) { + if (!Array.isArray(arr)) + throw new Error(qsTranslate('error', 'Second argument to join is not an array.')) + return arr.join(sep) +} + +function indexOf(target, s) { + if (!(Array.isArray(s) || typeof s === 'string')) + throw new Error(qsTranslate('error', 'Second argument to indexOf is not a string or array.')) + return s.indexOf(target) +} 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..5d1cb9f --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/reference.js @@ -0,0 +1,177 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.pragma library + +.import "polyfill.js" as Polyfill + + +const CONSTANTS = { + "π": Math.PI, + "pi": Math.PI, + "inf": Infinity, + "infinity": Infinity, + "∞": Infinity, + "e": Math.E +}; +const CONSTANTS_LIST = Object.keys(CONSTANTS); + +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, +} +const FUNCTIONS_LIST = Object.keys(FUNCTIONS); + +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 + } +} + +let string = new P('string') +let bool = new P('bool') +let number = new P('number') +let array = new P('array') + +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/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/tokenizer.js similarity index 59% rename from common/src/parsing/tokenizer.mjs rename to LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/tokenizer.js index 8725bf3..9a907fa 100644 --- a/common/src/parsing/tokenizer.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/parsing/tokenizer.js @@ -1,32 +1,33 @@ /** * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. - * Copyright (C) 2021-2025 Ad5001 - * + * Copyright (C) 2021-2024 Ad5001 + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +.pragma library -import * as Reference from "./reference.mjs" +.import "reference.js" as Reference const WHITESPACES = " \t\n\r" -const STRING_LIMITERS = "\"'`" -const OPERATORS = "+-*/^%?:=!><" -const PUNCTUATION = "()[]{},." +const STRING_LIMITORS = '"\'`'; +const OPERATORS = "+-*/^%?:=!><"; +const PUNCTUTATION = "()[]{},."; const NUMBER_CHARS = "0123456789" const IDENTIFIER_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_₀₁₂₃₄₅₆₇₈₉αβγδεζηθκλμξρςστφχψωₐₑₒₓₔₕₖₗₘₙₚₛₜ" -export const TokenType = { +var TokenType = { // Expression type "WHITESPACE": "WHITESPACE", "VARIABLE": "VARIABLE", @@ -39,136 +40,130 @@ export const TokenType = { "UNKNOWN": "UNKNOWN" } -export class Token { +class Token { constructor(type, value, startPosition) { - this.type = type - this.value = value + this.type = type; + this.value = value; this.startPosition = startPosition } } -export class ExpressionTokenizer { - /** - * - * @param {InputExpression} input - * @param {boolean} tokenizeWhitespaces - * @param {boolean} errorOnUnknown - */ +class ExpressionTokenizer { constructor(input, tokenizeWhitespaces = false, errorOnUnknown = true) { - this.input = input - this.currentToken = null + 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() + this.input.next(); } - + readWhitespaces() { - let included = "" + let included = ""; while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek())) { - included += this.input.next() + included += this.input.next(); } - return new Token(TokenType.WHITESPACE, included, this.input.position - included.length) + return new Token(TokenType.WHITESPACE, included, this.input.position-included.length) } - + readString() { - let delimitation = this.input.peek() - if(STRING_LIMITERS.includes(delimitation)) { + let delimitation = this.input.peek(); + if(STRING_LIMITORS.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() === "\\" + 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() + included += this.input.next(); } this.input.skip(delimitation) - let token = new Token(TokenType.STRING, included, this.input.position - included.length) + 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() === ".") { + 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 + hasDot = true; } - included += this.input.next() + included += this.input.next(); } - return new Token(TokenType.NUMBER, included, this.input.position - included.length) + return new Token(TokenType.NUMBER, included, this.input.position-included.length) } - + readOperator() { - let included = "" + let included = ""; while(!this.input.atEnd() && OPERATORS.includes(this.input.peek())) { - included += this.input.next() + included += this.input.next(); } - return new Token(TokenType.OPERATOR, included, this.input.position - included.length) + return new Token(TokenType.OPERATOR, included, this.input.position-included.length) } - + readIdentifier() { - let identifier = "" + let identifier = ""; while(!this.input.atEnd() && IDENTIFIER_CHARS.includes(this.input.peek().toLowerCase())) { - identifier += this.input.next() + identifier += this.input.next(); } if(Reference.CONSTANTS_LIST.includes(identifier.toLowerCase())) { - return new Token(TokenType.CONSTANT, identifier.toLowerCase(), this.input.position - identifier.length) + 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) + return new Token(TokenType.FUNCTION, identifier.toLowerCase(), this.input.position-identifier.length) } else { - return new Token(TokenType.VARIABLE, identifier, this.input.position - identifier.length) + 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.input.atEnd()) return null; + let c = this.input.peek(); + if(this.tokenizeWhitespaces && WHITESPACES.includes(c)) return this.readWhitespaces(); + if(STRING_LIMITORS.includes(c)) return this.readString(); + if(NUMBER_CHARS.includes(c)) return this.readNumber(); + if(IDENTIFIER_CHARS.includes(c.toLowerCase())) return this.readIdentifier(); + if(OPERATORS.includes(c)) return this.readOperator(); + if(Reference.CONSTANTS_LIST.includes(c)) return new Token(TokenType.CONSTANT, this.input.next(), this.input.position-1); + if(PUNCTUTATION.includes(c)) return new Token(TokenType.PUNCT, this.input.next(), this.input.position-1); if(this.errorOnUnknown) - this.input.raise("Unknown token character " + c) + this.input.throw("Unknown token character " + c) else - return new Token(TokenType.UNKNOWN, this.input.next(), this.input.position - 1) + return new Token(TokenType.UNKNOWN, this.input.next(), this.input.position-1); } peek() { - if(this.currentToken == null) this.currentToken = this.readNextToken() - return this.currentToken + if(this.currentToken == null) this.currentToken = this.readNextToken(); + return this.currentToken; } next() { - let tmp + let tmp; if(this.currentToken == null) - tmp = this.readNextToken() + tmp = this.readNextToken(); else - tmp = this.currentToken - this.currentToken = null - return tmp + tmp = this.currentToken; + this.currentToken = null; + return tmp; } - + atEnd() { - return this.peek() == null + 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()) + let next = this.next(); + if(next.type != type) + input.raise("Unexpected token " + next.type.toLowerCase() + ' "' + 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..0b62f8f --- /dev/null +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/utils.js @@ -0,0 +1,369 @@ +/** + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.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 exponents = [ + "⁰","¹","²","³","⁴","⁵","⁶","⁷","⁸","⁹" +] +var exponentReg = new RegExp('(['+exponents.join('')+']+)', 'g') + +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, '^'], + [/\^\(([\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'], + //[/×(\d|\()/g, '$1'], + [/([^a-z])\(([^)(+.\/-]+)\)/g, "$1×$2"], + [/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; +} + +function escapeHTML(str) { + return str.replace(/&/g,'&').replace(//g,'>') ; +} + + + +/** + * Parses exponents and replaces them with expression values + * @param {string} expression - The expression to replace in. + * @return {string} The parsed expression + */ +function exponentsToExpression(expression) { + return expression.replace(exponentReg, (m, exp) => '^' + exp.split('').map((x) => exponents.indexOf(x)).join('')) +} 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 67% rename from runtime-pyside6/LogarithmPlotter/util/config.py rename to LogarithmPlotter/util/config.py index 2cce4dc..f19e000 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,16 +19,14 @@ from os import path, environ, makedirs from platform import system from json import load, dumps -from shutil import which - 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, + "enable_latex": False, "expression_editor": { "autoclose": True, "colorize": True, @@ -36,68 +34,45 @@ DEFAULT_SETTINGS = { }, "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 } } # 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]) + setSetting(setting_name+"."+setting_name2, cfg_data[setting_name][setting_name2]) else: setSetting(setting_name, cfg_data[setting_name]) - - -def save(file=CONFIG_FILE): + +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 +86,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 +97,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..127e587 --- /dev/null +++ b/LogarithmPlotter/util/helper.py @@ -0,0 +1,170 @@ +""" + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +""" + +from PySide6.QtWidgets import QMessageBox, QApplication +from PySide6.QtCore import QRunnable, QThreadPool, QThread, QObject, Signal, Slot, QCoreApplication +from PySide6.QtQml import QQmlApplicationEngine +from PySide6.QtGui import QImage +from PySide6 import __version__ as PySide6_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/?version=" + __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)) + 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=int) + def getSettingInt(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, int) + def setSettingInt(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, PySide6 & Python + """ + return QCoreApplication.translate('main',"Built with PySide6 (Qt) v{} and python v{}").format(PySide6_version, sys_version.split("\n")[0]) + + @Slot() + def fetchChangelog(self): + changelog_cache_path = path.join(path.dirname(path.realpath(__file__)), "CHANGELOG.md") + print(changelog_cache_path) + 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..ce22bb7 --- /dev/null +++ b/LogarithmPlotter/util/latex.py @@ -0,0 +1,195 @@ +""" + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +""" + +from PySide6.QtCore import QObject, Slot, Property, QCoreApplication +from PySide6.QtGui import QImage, QColor +from PySide6.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') + +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) -> str: + """ + Prepares and renders a latex string into a png file. + """ + markup_hash = "render"+str(hash(latex_markup)) + export_path = path.join(self.tempdir.name, f'{markup_hash}_{int(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) + # 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) + except Exception as e: # One of the processes failed. A message will be sent every time. + raise e + 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'{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 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. + """ + 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 77% rename from runtime-pyside6/LogarithmPlotter/util/native.py rename to LogarithmPlotter/util/native.py index 3adf153..01d9022 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,36 +20,31 @@ from PySide6.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 75% rename from runtime-pyside6/LogarithmPlotter/util/update.py rename to LogarithmPlotter/util/update.py index e18b93e..25400ce 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,11 +21,9 @@ 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..026528f 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 [PySide6](https://pypi.org/project/PySide6/) installable with `pip install PySide6`. +- 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) 2021-2024 Ad5001 This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -95,19 +71,12 @@ 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) +- 🇭🇺 Hungarian translation by [Óvári](https://github.com/ovari) ### 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. +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). +The specific file (LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js) is licensed under the [MIT License](https://raw.githubusercontent.com/silentmatt/expr-eval/master/LICENSE.txt). 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/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/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/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/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..3bcf9f2 100644 --- a/ci/drone.yml +++ b/ci/drone.yml @@ -6,43 +6,43 @@ 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-pyside6-xvfb:jammy-6.6.1 + 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 + 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-pyside6-xvfb-wine:win7-6.5.0-rev1 +# 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: Linux packaging +# image: ad5001/ubuntu-pyside6-xvfb:jammy-6.5.0 +# commands: +# - bash scripts/package-linux.sh +# 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: Windows building + image: ad5001/ubuntu-pyside6-xvfb-wine:win10-6.6.1 + 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 0e01b59..0000000 --- a/common/package-lock.json +++ /dev/null @@ -1,4449 +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.25.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", - "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.25.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.7.tgz", - "integrity": "sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.7.tgz", - "integrity": "sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==", - "license": "MIT", - "peer": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helpers": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7", - "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.25.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", - "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.7", - "@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.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.7.tgz", - "integrity": "sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.7.tgz", - "integrity": "sha512-12xfNeKNH7jubQNm7PAkzlLwEmCs1tfuX3UjIw6vP6QXi+leKh6+LyC/+Ed4EIQermwd58wsyh070yjDHFlNGg==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", - "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", - "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.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.7.tgz", - "integrity": "sha512-bD4WQhbkx80mAyj/WCm4ZHcF4rDxkoLFO6ph8/5/mQ3z4vAzltQXAmbc7GvVJx5H+lk5Mi5EmbTeox5nMGCsbw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-member-expression-to-functions": "^7.25.7", - "@babel/helper-optimise-call-expression": "^7.25.7", - "@babel/helper-replace-supers": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", - "@babel/traverse": "^7.25.7", - "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.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.7.tgz", - "integrity": "sha512-byHhumTj/X47wJ6C6eLpK7wW/WBEcnUeb7D0FNc/jFQnQVw7DOso3Zz5u9x/zLrFVkHa89ZGDbkAa1D54NdrCQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "regexpu-core": "^6.1.1", - "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.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", - "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.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.7.tgz", - "integrity": "sha512-O31Ssjd5K6lPbTX9AAYpSKrZmLeagt9uwschJd+Ixo6QiRyfpvgtVQp8qrDR9UNFjZ8+DO34ZkdrN+BnPXemeA==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", - "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", - "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.7.tgz", - "integrity": "sha512-VAwcwuYhv/AT+Vfr28c9y6SHzTan1ryqrydSTFGjU0uDJHw3uZ+PduI8plCLkRsDnqK2DMEDmwrOQRsK/Ykjng==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", - "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.7.tgz", - "integrity": "sha512-kRGE89hLnPfcz6fTrlNU+uhgcwv0mBE4Gv3P9Ke9kLVJYpi4AMVVEElXvB5CabrPZW4nCM8P8UyyjrzCM0O2sw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-wrap-function": "^7.25.7", - "@babel/traverse": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.7.tgz", - "integrity": "sha512-iy8JhqlUW9PtZkd4pHM96v6BdJ66Ba9yWSE4z0W4TvSZwLBPkyDsiIU3ENe4SmrzRBs76F7rQXTy1lYC49n6Lw==", - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.25.7", - "@babel/helper-optimise-call-expression": "^7.25.7", - "@babel/traverse": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", - "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.7.tgz", - "integrity": "sha512-pPbNbchZBkPMD50K0p3JGcFMNLVUCuU/ABybm/PGNj4JiHrpmNyqqCphBk4i19xXtNV0JhldQJJtbSW5aUvbyA==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", - "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", - "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", - "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.7.tgz", - "integrity": "sha512-MA0roW3JF2bD1ptAaJnvcabsVlNQShUaThyJbCDD4bCp8NEgiFvpoqRI2YS22hHlc2thjO/fTg2ShLMC3jygAg==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", - "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", - "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", - "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.7" - }, - "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.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.7.tgz", - "integrity": "sha512-UV9Lg53zyebzD1DwQoT9mzkEKa922LNUp5YkTJ6Uta0RbyXaQNUgcvSt7qIu1PpPzVb6rd10OVNTzkyBGeVmxQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/traverse": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.7.tgz", - "integrity": "sha512-GDDWeVLNxRIkQTnJn2pDOM1pkCgYdSqPeT1a9vh9yIqu2uzzgw1zcqEb+IJOhy+dTBMlNdThrDIksr2o09qrrQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "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.7", - "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.7.tgz", - "integrity": "sha512-wxyWg2RYaSUYgmd9MR0FyRGyeOMQE/Uzr1wzd/g5cf5bwi9A4v6HFdDm7y1MgDtod/fLOSTZY6jDgV0xU9d5bA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "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.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.7.tgz", - "integrity": "sha512-Xwg6tZpLxc4iQjorYsyGMyfJE7nP5MV8t/Ka58BgiA7Jw0fRqQNcANlLfdJ/yvBt9z9LD2We+BEkT7vLqZRWng==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", - "@babel/plugin-transform-optional-chaining": "^7.25.7" - }, - "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.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.7.tgz", - "integrity": "sha512-UVATLMidXrnH+GMUIuxq55nejlj02HP7F5ETyBONzP6G87fPBogG4CH6kxrSrdIuAjdwNO9VzyaYsrZPscWUrw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/traverse": "^7.25.7" - }, - "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-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.7.tgz", - "integrity": "sha512-ZvZQRmME0zfJnDQnVBKYzHxXT7lYBB3Revz1GuS7oLXWMgqUPX4G+DDbT30ICClht9WKV34QVrZhSw6WdklwZQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", - "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "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.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.7.tgz", - "integrity": "sha512-EJN2mKxDwfOUCPxMO6MUI58RN3ganiRAG/MS/S3HfB6QFNjroAMelQo/gybyYq97WerCBAZoyrAoW8Tzdq2jWg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.7.tgz", - "integrity": "sha512-4B6OhTrwYKHYYgcwErvZjbmH9X5TxQBsaBHdzEIB4l71gR5jh/tuHGlb9in47udL2+wVUcOz5XXhhfhVJwEpEg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-remap-async-to-generator": "^7.25.7", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/traverse": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.7.tgz", - "integrity": "sha512-ZUCjAavsh5CESCmi/xCpX1qcCaAglzs/7tmuvoFnJgA1dM7gQplsguljoTg+Ru8WENpX89cQyAtWoaE0I3X3Pg==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-remap-async-to-generator": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.7.tgz", - "integrity": "sha512-xHttvIM9fvqW+0a3tZlYcZYSBpSWzGBFIt/sYG3tcdSzBB8ZeVgz2gBP7Df+sM0N1850jrviYSSeUuc+135dmQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.7.tgz", - "integrity": "sha512-ZEPJSkVZaeTFG/m2PARwLZQ+OG0vFIhPlKHK/JdIMy8DbRJ/htz6LRrTFtdzxi9EHmcwbNPAKDnadpNSIW+Aow==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.7.tgz", - "integrity": "sha512-mhyfEW4gufjIqYFo9krXHJ3ElbFLIze5IDp+wQTxoPd+mwFb1NxatNAwmv8Q8Iuxv7Zc+q8EkiMQwc9IhyGf4g==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.25.7.tgz", - "integrity": "sha512-rvUUtoVlkDWtDWxGAiiQj0aNktTPn3eFynBcMC2IhsXweehwgdI9ODe+XjWw515kEmv22sSOTp/rxIRuTiB7zg==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.7.tgz", - "integrity": "sha512-9j9rnl+YCQY0IGoeipXvnk3niWicIB6kCsWRGLwX241qSXpbA4MKxtp/EdvFxsc4zI5vqfLxzOd0twIJ7I99zg==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-replace-supers": "^7.25.7", - "@babel/traverse": "^7.25.7", - "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.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.7.tgz", - "integrity": "sha512-QIv+imtM+EtNxg/XBKL3hiWjgdLjMOmZ+XzQwSgmBfKbfxUjBzGgVPklUuE55eq5/uVoh8gg3dqlrwR/jw3ZeA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/template": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.7.tgz", - "integrity": "sha512-xKcfLTlJYUczdaM1+epcdh1UGewJqr9zATgrNHcLBcV2QmfvPPEixo/sK/syql9cEmbr7ulu5HMFG5vbbt/sEA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.7.tgz", - "integrity": "sha512-kXzXMMRzAtJdDEgQBLF4oaiT6ZCU3oWHgpARnTKDAqPkDJ+bs3NrZb310YYevR5QlRo3Kn7dzzIdHbZm1VzJdQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.7.tgz", - "integrity": "sha512-by+v2CjoL3aMnWDOyCIg+yxU9KXSRa9tN6MbqggH5xvymmr9p4AMjYkNlQy4brMceBnUyHZ9G8RnpvT8wP7Cfg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "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.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.7.tgz", - "integrity": "sha512-HvS6JF66xSS5rNKXLqkk7L9c/jZ/cdIVIcoPVrnl8IsVpLggTjXs8OWekbLHs/VtYDDh5WXnQyeE3PPUGm22MA==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.7.tgz", - "integrity": "sha512-UvcLuual4h7/GfylKm2IAA3aph9rwvAM2XBA0uPKU3lca+Maai4jBjjEVUS568ld6kJcgbouuumCBhMd/Yz17w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.7.tgz", - "integrity": "sha512-yjqtpstPfZ0h/y40fAXRv2snciYr0OAoMXY/0ClC7tm4C/nG5NJKmIItlaYlLbIVAWNfrYuy9dq1bE0SbX0PEg==", - "license": "MIT", - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.7.tgz", - "integrity": "sha512-h3MDAP5l34NQkkNulsTNyjdaR+OiB0Im67VU//sFupouP8Q6m9Spy7l66DcaAQxtmCqGdanPByLsnwFttxKISQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.7.tgz", - "integrity": "sha512-n/TaiBGJxYFWvpJDfsxSj9lEEE44BFM1EPGz4KEiTipTgkoFVVcCmzAL3qA7fdQU96dpo4gGf5HBx/KnDvqiHw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.7.tgz", - "integrity": "sha512-5MCTNcjCMxQ63Tdu9rxyN6cAWurqfrDZ76qvVPrGYdBxIj+EawuuxTu/+dgJlhK5eRz3v1gLwp6XwS8XaX2NiQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/traverse": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.7.tgz", - "integrity": "sha512-Ot43PrL9TEAiCe8C/2erAjXMeVSnE/BLEx6eyrKLNFCCw5jvhTHKyHxdI1pA0kz5njZRYAnMO2KObGqOCRDYSA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.7.tgz", - "integrity": "sha512-fwzkLrSu2fESR/cm4t6vqd7ebNIopz2QHGtjoU+dswQo/P6lwAG04Q98lliE3jkz/XqnbGFLnUcE0q0CVUf92w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.7.tgz", - "integrity": "sha512-iImzbA55BjiovLyG2bggWS+V+OLkaBorNvc/yJoeeDQGztknRnDdYfp2d/UPmunZYEnZi6Lg8QcTmNMHOB0lGA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.7.tgz", - "integrity": "sha512-Std3kXwpXfRV0QtQy5JJcRpkqP8/wG4XL7hSKZmGlxPlDqmpXtEPRmhF7ztnlTCtUN3eXRUJp+sBEZjaIBVYaw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.7.tgz", - "integrity": "sha512-CgselSGCGzjQvKzghCvDTxKHP3iooenLpJDO842ehn5D2G5fJB222ptnDwQho0WjEvg7zyoxb9P+wiYxiJX5yA==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.7.tgz", - "integrity": "sha512-L9Gcahi0kKFYXvweO6n0wc3ZG1ChpSFdgG+eV1WYZ3/dGbJK7vvk91FgGgak8YwRgrCuihF8tE/Xg07EkL5COg==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.7.tgz", - "integrity": "sha512-t9jZIvBmOXJsiuyOwhrIGs8dVcD6jDyg2icw1VL4A/g+FnWyJKwUfSSU2nwJuMV2Zqui856El9u+ElB+j9fV1g==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.7.tgz", - "integrity": "sha512-p88Jg6QqsaPh+EB7I9GJrIqi1Zt4ZBHUQtjw3z1bzEXcLh6GfPqzZJ6G+G1HBGKUNukT58MnKG7EN7zXQBCODw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.7.tgz", - "integrity": "sha512-BtAT9LzCISKG3Dsdw5uso4oV1+v2NlVXIIomKJgQybotJY3OwCwJmkongjHgwGKoZXd0qG5UZ12JUlDQ07W6Ow==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.7.tgz", - "integrity": "sha512-CfCS2jDsbcZaVYxRFo2qtavW8SpdzmBXC2LOI4oO0rP+JSRDxxF3inF4GcPsLgfb5FjkhXG5/yR/lxuRs2pySA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.7.tgz", - "integrity": "sha512-FbuJ63/4LEL32mIxrxwYaqjJxpbzxPVQj5a+Ebrc8JICV6YX8nE53jY+K0RZT3um56GoNWgkS2BQ/uLGTjtwfw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.7.tgz", - "integrity": "sha512-8CbutzSSh4hmD+jJHIA8vdTNk15kAzOnFLVVgBSMGr28rt85ouT01/rezMecks9pkU939wDInImwCKv4ahU4IA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.7.tgz", - "integrity": "sha512-1JdVKPhD7Y5PvgfFy0Mv2brdrolzpzSoUq2pr6xsR+m+3viGGeHEokFKsCgOkbeFOQxfB1Vt2F0cPJLRpFI4Zg==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.7.tgz", - "integrity": "sha512-pWT6UXCEW3u1t2tcAGtE15ornCBvopHj9Bps9D2DsH15APgNVOTwwczGckX+WkAvBmuoYKRCFa4DK+jM8vh5AA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-replace-supers": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.7.tgz", - "integrity": "sha512-m9obYBA39mDPN7lJzD5WkGGb0GO54PPLXsbcnj1Hyeu8mSRz7Gb4b1A6zxNX32ZuUySDK4G6it8SDFWD1nCnqg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.7.tgz", - "integrity": "sha512-h39agClImgPWg4H8mYVAbD1qP9vClFbEjqoJmt87Zen8pjqK8FTPUwrOXAvqu5soytwxrLMd2fx2KSCp2CHcNg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.7.tgz", - "integrity": "sha512-FYiTvku63me9+1Nz7TOx4YMtW3tWXzfANZtrzHhUZrz4d47EEtMQhzFoZWESfXuAMMT5mwzD4+y1N8ONAX6lMQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.7.tgz", - "integrity": "sha512-KY0hh2FluNxMLwOCHbxVOKfdB5sjWG4M183885FmaqWWiGMhRZq4DQRKH6mHdEucbJnyDyYiZNwNG424RymJjA==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.7.tgz", - "integrity": "sha512-LzA5ESzBy7tqj00Yjey9yWfs3FKy4EmJyKOSWld144OxkTji81WWnUT8nkLUn+imN/zHL8ZQlOu/MTUAhHaX3g==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.7.tgz", - "integrity": "sha512-lQEeetGKfFi0wHbt8ClQrUSUMfEeI3MMm74Z73T9/kuz990yYVtfofjf3NuA42Jy3auFOpbjDyCSiIkTs1VIYw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.7.tgz", - "integrity": "sha512-mgDoQCRjrY3XK95UuV60tZlFCQGXEtMg8H+IsW72ldw1ih1jZhzYXbJvghmAEpg5UVhhnCeia1CkGttUvCkiMQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "regenerator-transform": "^0.15.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.7.tgz", - "integrity": "sha512-3OfyfRRqiGeOvIWSagcwUTVk2hXBsr/ww7bLn6TRTuXnexA+Udov2icFOxFX9abaj4l96ooYkcNN1qi2Zvqwng==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.7.tgz", - "integrity": "sha512-uBbxNwimHi5Bv3hUccmOFlUy3ATO6WagTApenHz9KzoIdn0XeACdB12ZJ4cjhuB2WSi80Ez2FWzJnarccriJeA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.7.tgz", - "integrity": "sha512-Mm6aeymI0PBh44xNIv/qvo8nmbkpZze1KvR8MkEqbIREDxoiWTi18Zr2jryfRMwDfVZF9foKh060fWgni44luw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.7.tgz", - "integrity": "sha512-ZFAeNkpGuLnAQ/NCsXJ6xik7Id+tHuS+NT+ue/2+rn/31zcdnupCdmunOizEaP0JsUmTFSTOPoQY7PkK2pttXw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.7.tgz", - "integrity": "sha512-SI274k0nUsFFmyQupiO7+wKATAmMFf8iFgq2O+vVFXZ0SV9lNfT1NGzBEhjquFmD8I9sqHLguH+gZVN3vww2AA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.7.tgz", - "integrity": "sha512-OmWmQtTHnO8RSUbL0NTdtpbZHeNTnm68Gj5pA4Y2blFNh+V4iZR68V1qL9cI37J21ZN7AaCnkfdHtLExQPf2uA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.7.tgz", - "integrity": "sha512-BN87D7KpbdiABA+t3HbVqHzKWUDN3dymLaTnPFAMyc8lV+KN3+YzNhVRNdinaCPA4AUqx7ubXbQ9shRjYBl3SQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.7.tgz", - "integrity": "sha512-IWfR89zcEPQGB/iB408uGtSPlQd3Jpq11Im86vUgcmSTcoWAiQMCTOa2K2yNNqFJEBVICKhayctee65Ka8OB0w==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.7.tgz", - "integrity": "sha512-8JKfg/hiuA3qXnlLx8qtv5HWRbgyFx2hMMtpDDuU2rTckpKkGu4ycK5yYHwuEa16/quXfoxHBIApEsNyMWnt0g==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.7.tgz", - "integrity": "sha512-YRW8o9vzImwmh4Q3Rffd09bH5/hvY0pxg+1H1i0f7APoUeg12G7+HhLj9ZFNIrYkgBXhIijPJ+IXypN0hLTIbw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.7.tgz", - "integrity": "sha512-Gibz4OUdyNqqLj+7OAvBZxOD7CklCtMA5/j0JgUEwOnaRULsPDXmic2iKxL2DX2vQduPR5wH2hjZas/Vr/Oc0g==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.7", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.7", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.25.7", - "@babel/plugin-syntax-import-attributes": "^7.25.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.25.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.7", - "@babel/plugin-transform-async-to-generator": "^7.25.7", - "@babel/plugin-transform-block-scoped-functions": "^7.25.7", - "@babel/plugin-transform-block-scoping": "^7.25.7", - "@babel/plugin-transform-class-properties": "^7.25.7", - "@babel/plugin-transform-class-static-block": "^7.25.7", - "@babel/plugin-transform-classes": "^7.25.7", - "@babel/plugin-transform-computed-properties": "^7.25.7", - "@babel/plugin-transform-destructuring": "^7.25.7", - "@babel/plugin-transform-dotall-regex": "^7.25.7", - "@babel/plugin-transform-duplicate-keys": "^7.25.7", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.7", - "@babel/plugin-transform-dynamic-import": "^7.25.7", - "@babel/plugin-transform-exponentiation-operator": "^7.25.7", - "@babel/plugin-transform-export-namespace-from": "^7.25.7", - "@babel/plugin-transform-for-of": "^7.25.7", - "@babel/plugin-transform-function-name": "^7.25.7", - "@babel/plugin-transform-json-strings": "^7.25.7", - "@babel/plugin-transform-literals": "^7.25.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.7", - "@babel/plugin-transform-member-expression-literals": "^7.25.7", - "@babel/plugin-transform-modules-amd": "^7.25.7", - "@babel/plugin-transform-modules-commonjs": "^7.25.7", - "@babel/plugin-transform-modules-systemjs": "^7.25.7", - "@babel/plugin-transform-modules-umd": "^7.25.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.7", - "@babel/plugin-transform-new-target": "^7.25.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.7", - "@babel/plugin-transform-numeric-separator": "^7.25.7", - "@babel/plugin-transform-object-rest-spread": "^7.25.7", - "@babel/plugin-transform-object-super": "^7.25.7", - "@babel/plugin-transform-optional-catch-binding": "^7.25.7", - "@babel/plugin-transform-optional-chaining": "^7.25.7", - "@babel/plugin-transform-parameters": "^7.25.7", - "@babel/plugin-transform-private-methods": "^7.25.7", - "@babel/plugin-transform-private-property-in-object": "^7.25.7", - "@babel/plugin-transform-property-literals": "^7.25.7", - "@babel/plugin-transform-regenerator": "^7.25.7", - "@babel/plugin-transform-reserved-words": "^7.25.7", - "@babel/plugin-transform-shorthand-properties": "^7.25.7", - "@babel/plugin-transform-spread": "^7.25.7", - "@babel/plugin-transform-sticky-regex": "^7.25.7", - "@babel/plugin-transform-template-literals": "^7.25.7", - "@babel/plugin-transform-typeof-symbol": "^7.25.7", - "@babel/plugin-transform-unicode-escapes": "^7.25.7", - "@babel/plugin-transform-unicode-property-regex": "^7.25.7", - "@babel/plugin-transform-unicode-regex": "^7.25.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.7", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.38.1", - "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.25.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", - "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", - "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", - "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", - "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "license": "MIT" - }, - "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.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "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.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.0.tgz", - "integrity": "sha512-BJcu+a+Mpq476DMXG+hevgPSl56bkUoi88dKT8t3RyUp8kGuOh+2bU8Gs7zXDlu+fyZggnJ+iOBGrb/O1SorYg==", - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "commondir": "^1.0.1", - "estree-walker": "^2.0.2", - "fdir": "^6.1.1", - "is-reference": "1.2.1", - "magic-string": "^0.30.3", - "picomatch": "^2.3.1" - }, - "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-commonjs/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz", - "integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==", - "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.2", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", - "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - }, - "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/pluginutils/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", - "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", - "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", - "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", - "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", - "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", - "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", - "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", - "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", - "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", - "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", - "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", - "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", - "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", - "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", - "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", - "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/chai": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.0.0.tgz", - "integrity": "sha512-+DwhEHAaFPPdJ2ral3kNHFQXnTfscEEFsUxzD+d7nlcLrFK23JtNjH71RGasTcHb88b4vVi4mTyfpf8u2L8bdA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/chai-as-promised": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-8.0.1.tgz", - "integrity": "sha512-dAlDhLjJlABwAVYObo9TPWYTRg9NaQM5CXeaeJYcYAkvzUf0JRLIiog88ao2Wqy/20WUnhbbUZcgvngEbJ3YXQ==", - "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/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "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.9", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.9.tgz", - "integrity": "sha512-sicdRoWtYevwxjOHNMPTl3vSfJM6oyW8o1wXeI7uww6b6xHg8eBznQDNSGBCDJmsE8UMxP05JgZRtsKbTqt//Q==", - "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": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "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.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", - "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.10.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", - "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2", - "core-js-compat": "^3.38.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" - }, - "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.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", - "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.30001663", - "electron-to-chromium": "^1.5.28", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/c8": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.2.tgz", - "integrity": "sha512-Qr6rj76eSshu5CgRYvktW0uM0CFY0yi4Fd5D0duDXO6sYinyopmftUiJVuzBQxQcwQLor7JWDVRP+dUfCmzgJw==", - "license": "ISC", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@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.30001667", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz", - "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==", - "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.1.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", - "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", - "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.0", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-8.0.0.tgz", - "integrity": "sha512-sMsGXTrS3FunP/wbqh/KxM8Kj/aLPXQGkNtvE5wPfSToq8wkkvBpTZo1LIiEVmC4BwkKpag+l5h/20lBMk6nUg==", - "dev": true, - "license": "WTFPL", - "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": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "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": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "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.38.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", - "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "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.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "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.34", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.34.tgz", - "integrity": "sha512-/TZAiChbAflBNjCg+VvstbcwAtIL/VdMFO3NgRFIzBjpvPzWOTIbbO8kNb6RwU4bt9TP7K+3KqBKw/lOU+Y+GA==", - "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": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "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.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.0.tgz", - "integrity": "sha512-3oB133prH1o4j/L5lLW7uOCF1PlD+/It2L0eL/iAqWMB91RBbqTewABqxhj0ibBd90EEmWZq7ntIWzVaWcXTGQ==", - "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.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "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": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "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.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "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-lib-report/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/istanbul-lib-report/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/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.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/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/log-symbols/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-symbols/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/log-symbols/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-symbols/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==", - "dev": true, - "license": "MIT" - }, - "node_modules/log-symbols/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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/loupe": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", - "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", - "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.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", - "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.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "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.7.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", - "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", - "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/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/mocha/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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "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.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "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.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", - "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", - "optional": true, - "peer": true, - "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.1.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", - "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.11.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.11.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.1.tgz", - "integrity": "sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ==", - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.0.2" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "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.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/rollup": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", - "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.6" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.0", - "@rollup/rollup-android-arm64": "4.24.0", - "@rollup/rollup-darwin-arm64": "4.24.0", - "@rollup/rollup-darwin-x64": "4.24.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", - "@rollup/rollup-linux-arm-musleabihf": "4.24.0", - "@rollup/rollup-linux-arm64-gnu": "4.24.0", - "@rollup/rollup-linux-arm64-musl": "4.24.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", - "@rollup/rollup-linux-riscv64-gnu": "4.24.0", - "@rollup/rollup-linux-s390x-gnu": "4.24.0", - "@rollup/rollup-linux-x64-gnu": "4.24.0", - "@rollup/rollup-linux-x64-musl": "4.24.0", - "@rollup/rollup-win32-arm64-msvc": "4.24.0", - "@rollup/rollup-win32-ia32-msvc": "4.24.0", - "@rollup/rollup-win32-x64-msvc": "4.24.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": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "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-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "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.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", - "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.0" - }, - "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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi/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/wrap-ansi/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/wrap-ansi/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/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/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/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/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 0da11a8..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 -} \ No newline at end of file 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/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/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/assets/native/linux/debian/changelog b/linux/debian/changelog similarity index 100% rename from assets/native/linux/debian/changelog rename to linux/debian/changelog diff --git a/assets/native/linux/debian/control.bkp b/linux/debian/control similarity index 64% rename from assets/native/linux/debian/control.bkp rename to linux/debian/control index 1df7fd8..15e0846 100644 --- a/assets/native/linux/debian/control.bkp +++ b/linux/debian/control @@ -1,11 +1,11 @@ Package: logarithmplotter Source: logarithmplotter -Version: 0.6.0 +Version: 0.5.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 +Depends: python3, python3-pip, python3-pyside6-essentials (>= 6.4.0), texlive-latex-base, dvipng -Build-Depends: debhelper (>=11~), dh-python, dpkg-dev (>= 1.16.1~), python-setuptools +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/ 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..6d05e77 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: 2023, Ad5001 License: GPL-3.0+ diff --git a/linux/debian/depends b/linux/debian/depends new file mode 100644 index 0000000..fb79d69 --- /dev/null +++ b/linux/debian/depends @@ -0,0 +1 @@ +python3, python3-pip, python3-pyside6-essentials (>= 6.4.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/assets/native/linux/eu.ad5001.LogarithmPlotter.metainfo.xml b/linux/eu.ad5001.LogarithmPlotter.metainfo.xml similarity index 70% rename from assets/native/linux/eu.ad5001.LogarithmPlotter.metainfo.xml rename to linux/eu.ad5001.LogarithmPlotter.metainfo.xml index f7e4c9d..259edb1 100644 --- a/assets/native/linux/eu.ad5001.LogarithmPlotter.metainfo.xml +++ b/linux/eu.ad5001.LogarithmPlotter.metainfo.xml @@ -1,71 +1,83 @@ - - + + eu.ad5001.LogarithmPlotter + eu.ad5001.LogarithmPlotter.desktop logarithmplotter.desktop CC0-1.0 GPL-3.0+ LogarithmPlotter - https://apps.ad5001.eu/icons/apps/svg/logarithmplotter.svg + http://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. + 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.

- 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. + 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:

-

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
+

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/ @@ -73,66 +85,39 @@ 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. - + https://apps.ad5001.eu/img/en/gain.png?v=0.5 + https://apps.ad5001.eu/img/en/logarithmplotter/phase.png?v=0.5 + https://apps.ad5001.eu/img/en/logarithmplotter/welcome.png?v=0.5 + https://apps.ad5001.eu/img/de/gain.png?v=0.5 + https://apps.ad5001.eu/img/de/logarithmplotter/phase.png?v=0.5 + https://apps.ad5001.eu/img/de/logarithmplotter/welcome.png?v=0.5 + https://apps.ad5001.eu/img/fr/gain.png?v=0.5 + https://apps.ad5001.eu/img/fr/logarithmplotter/phase.png?v=0.5 + https://apps.ad5001.eu/img/fr/logarithmplotter/welcome.png?v=0.5 + https://apps.ad5001.eu/img/hu/gain.png?v=0.5 + https://apps.ad5001.eu/img/hu/logarithmplotter/phase.png?v=0.5 + https://apps.ad5001.eu/img/hu/logarithmplotter/welcome.png?v=0.5 + https://apps.ad5001.eu/img/no/gain.png?v=0.5 + https://apps.ad5001.eu/img/no/logarithmplotter/phase.png?v=0.5 + https://apps.ad5001.eu/img/no/logarithmplotter/welcome.png?v=0.5 - + - 768 + medium - 3840 - 360 + xlarge + xsmall - + -

Changes for v0.5.0:

+

Changes for v0.5.0:

New

  • New, reworked application icon.
  • @@ -164,19 +149,19 @@ - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.5.0/logarithmplotter-v0.5.0-setup.exe + https://artifacts.accountfree.org/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.accountfree.org/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 + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.5.0/logarithmplotter-0.5.0.tar.gz -

    Changes for v0.4.0:

    +

    Changes for v0.4.0:

    Changes

    • Fully ported to PySide6 (Qt6).
    • @@ -213,19 +198,19 @@ - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.4.0/logarithmplotter-v0.4.0-setup.exe + https://artifacts.accountfree.org/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.accountfree.org/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 + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.4.0/logarithmplotter-0.4.0.tar.gz -

      Changes for v0.3.0:

      +

      Changes for v0.3.0:

      New

      • New completely revamped expression editor:
      • @@ -279,19 +264,19 @@ - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.3.0/logarithmplotter-v0.3.0-setup.exe + https://artifacts.accountfree.org/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.accountfree.org/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 + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.3.0/logarithmplotter-0.3.0.tar.gz -

        Changes for v0.2.0:

        +

        Changes for v0.2.0:

        New

        • (EXPERIMENTAL) LogarithmPlotter now has an optional LaTeX integration.
        • @@ -332,19 +317,19 @@ - https://artifacts.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.2.0/logarithmplotter-v0.2.0-setup.exe + https://artifacts.accountfree.org/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.accountfree.org/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 + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.2.0/logarithmplotter-0.2.0.tar.gz -

          Changes for v0.1.8:

          +

          Changes for v0.1.8:

          New

          • There is now a user manual for LogarithmPlotter! Contributions apprecriated.
          • @@ -377,19 +362,19 @@ - https://artifacts.ad5001.eu/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.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.8/LogarithmPlotter-v0.1.8-setup.dmg + https://artifacts.accountfree.org/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 + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.8/logarithmplotter-0.1.8.tar.gz -

            Changes for v0.1.7:

            +

            Changes for v0.1.7:

            New

            • The history browser has been completly redesigned, improving UX.
            • @@ -433,19 +418,19 @@ - https://artifacts.ad5001.eu/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.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.7/LogarithmPlotter-v0.1.7-setup.dmg + https://artifacts.accountfree.org/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 + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.7/logarithmplotter-0.1.7.tar.gz -

              Changes for v0.1.6:

              +

              Changes for v0.1.6:

              New

              • A new changelog popup is available at startup and in the help menu.
              • @@ -474,19 +459,19 @@ - https://artifacts.ad5001.eu/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.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.6/LogarithmPlotter-v0.1.6-setup.dmg + https://artifacts.accountfree.org/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 + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.6/LogarithmPlotter-v0.1.6.tar.gz -

                Changes for v0.1.5:

                +

                Changes for v0.1.5:

                New

                • LogarithmPlotter has now better handling of very high values in logarithmic scale.
                • @@ -504,19 +489,19 @@ - https://artifacts.ad5001.eu/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.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.5/LogarithmPlotter-v0.1.5-setup.dmg + https://artifacts.accountfree.org/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 + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.5/LogarithmPlotter-v0.1.5.tar.gz -

                  Changes for v0.1.4:

                  +

                  Changes for v0.1.4:

                  New

                  • LogarithmPlotter detects unsaved changes.
                  • @@ -535,19 +520,19 @@ - https://artifacts.ad5001.eu/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.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.4/LogarithmPlotter-v0.1.4-setup.dmg + https://artifacts.accountfree.org/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 + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.4/LogarithmPlotter-v0.1.4.tar.gz -

                    Changes for v0.1.3:

                    +

                    Changes for v0.1.3:

                    Fixed bugs

                    • Sandboxed packages (snapcraft and flatpak) won't show error messages related to update checks.
                    • @@ -556,19 +541,19 @@ - https://artifacts.ad5001.eu/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.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.3/LogarithmPlotter-v0.1.3-setup.dmg + https://artifacts.accountfree.org/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 + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.3/LogarithmPlotter-v0.1.3.tar.gz -

                      Changes for v0.1.2:

                      +

                      Changes for v0.1.2:

                      Fixed bugs

                      • Unable to move Bode diagrams elements when having deleted the sum element.
                      • @@ -580,19 +565,19 @@ - https://artifacts.ad5001.eu/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.ad5001.eu/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.2/LogarithmPlotter-v0.1.2-setup.dmg + https://artifacts.accountfree.org/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 + https://artifacts.accountfree.org/repository/apps.ad5001.eu-apps/logarithmplotter/v0.1.2/LogarithmPlotter-v0.1.2.tar.gz -

                        Changes for v0.1:

                        +

                        Changes for v0.1:

                        • Initial release.
                        @@ -601,10 +586,8 @@ - - Ad5001 - https://ad5001.eu - + Ad5001 + mail@ad5001.eu Plot @@ -619,8 +602,7 @@ Phase Sequence Distribution - Qt - + 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..331239f 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.dmg" 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) { @@ -58,13 +58,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.dmg" 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 +74,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.dmg" 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/logplotter.svg b/logplotter.svg new file mode 100644 index 0000000..77a2817 --- /dev/null +++ b/logplotter.svg @@ -0,0 +1,64 @@ + +LogarithmPlotter Icon v1.0image/svg+xmlLogarithmPlotter Icon v1.02021Ad5001(c) Ad5001 2021 - All rights reserved 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..d7c0c50 100644 --- a/assets/native/mac/Info.plist +++ b/mac/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.6.0 + 0.5.0 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/assets/native/mac/logarithmplotter.icns b/mac/logarithmplotter.icns similarity index 100% rename from assets/native/mac/logarithmplotter.icns rename to mac/logarithmplotter.icns diff --git a/assets/native/mac/logarithmplotter.iconset/icon_128x128.png b/mac/logarithmplotter.iconset/icon_128x128.png similarity index 100% rename from assets/native/mac/logarithmplotter.iconset/icon_128x128.png rename to mac/logarithmplotter.iconset/icon_128x128.png diff --git a/assets/native/mac/logarithmplotter.iconset/icon_16x16.png b/mac/logarithmplotter.iconset/icon_16x16.png similarity index 100% rename from assets/native/mac/logarithmplotter.iconset/icon_16x16.png rename to mac/logarithmplotter.iconset/icon_16x16.png diff --git a/assets/native/mac/logarithmplotter.iconset/icon_256x256.png b/mac/logarithmplotter.iconset/icon_256x256.png similarity index 100% rename from assets/native/mac/logarithmplotter.iconset/icon_256x256.png rename to mac/logarithmplotter.iconset/icon_256x256.png diff --git a/assets/native/mac/logarithmplotter.iconset/icon_32x32.png b/mac/logarithmplotter.iconset/icon_32x32.png similarity index 100% rename from assets/native/mac/logarithmplotter.iconset/icon_32x32.png rename to mac/logarithmplotter.iconset/icon_32x32.png diff --git a/assets/native/mac/logarithmplotter.iconset/icon_512x512.png b/mac/logarithmplotter.iconset/icon_512x512.png similarity index 100% rename from assets/native/mac/logarithmplotter.iconset/icon_512x512.png rename to mac/logarithmplotter.iconset/icon_512x512.png 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..1481dba 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) 2021-2024 Ad5001 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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/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/LogarithmPlotter.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml deleted file mode 100644 index ddbd2bd..0000000 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml +++ /dev/null @@ -1,272 +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 QtQml -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts 1.12 -import eu.ad5001.MixedMenu 1.1 - -// Auto loading all modules. -import eu.ad5001.LogarithmPlotter.Common - -import eu.ad5001.LogarithmPlotter.History 1.0 as History -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 - -/*! - \qmltype LogarithmPlotter - \inqmlmodule eu.ad5001.LogarithmPlotter - \brief Main window of LogarithmPlotter - - \sa AppMenuBar, History, GreetScreen, Changelog, Alert, ObjectLists, Settings, HistoryBrowser, LogGraphCanvas, PickLocationOverlay. -*/ -ApplicationWindow { - id: root - visible: true - width: 1000 - height: 500 - color: sysPalette.window - title: qsTr("untitled") - - SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } - SystemPalette { id: sysPaletteIn; colorGroup: SystemPalette.Disabled } - - menuBar: appMenu.trueItem - - AppMenuBar {id: appMenu} - - Popup.GreetScreen {} - - Popup.Preferences {id: preferences} - - Popup.Changelog {id: changelog} - - Popup.About {id: about} - - Popup.ThanksTo {id: thanksTo} - - Popup.Alert { - id: alert - anchors.bottom: parent.bottom - anchors.bottomMargin: 5 - z: 3 - } - - Item { - id: sidebar - width: 300 - height: parent.height - //y: root.menuBar.height - readonly property bool inPortrait: root.width < root.height - /*modal: true// inPortrait - interactive: inPortrait - position: inPortrait ? 0 : 1 - */ - visible: !inPortrait - - - TabBar { - id: sidebarSelector - width: parent.width - anchors.top: parent.top - TabButton { - text: qsTr("Objects") - icon.name: 'polygon-add-nodes' - icon.color: sysPalette.windowText - //height: 24 - } - TabButton { - text: qsTr("Settings") - icon.name: 'preferences-system-symbolic' - icon.color: sysPalette.windowText - //height: 24 - } - TabButton { - text: qsTr("History") - icon.name: 'view-history' - icon.color: sysPalette.windowText - //height: 24 - } - } - - StackLayout { - id: sidebarContents - anchors.top: sidebarSelector.bottom - anchors.left: parent.left - anchors.topMargin: 5 - anchors.leftMargin: 5 - anchors.bottom: parent.bottom - //anchors.bottomMargin: sidebarSelector.height - width: parent.width - 5 - currentIndex: sidebarSelector.currentIndex - z: -1 - clip: true - - ObjectLists { - id: objectLists - onChanged: Modules.Canvas.requestPaint() - } - - Settings { - id: settings - canvas: drawCanvas - onChanged: Modules.Canvas.requestPaint() - } - - History.Browser { - id: historyBrowser - } - } - } - - LogGraphCanvas { - id: drawCanvas - anchors.top: parent.top - anchors.left: sidebar.inPortrait ? parent.left : sidebar.right - height: parent.height - width: sidebar.inPortrait ? parent.width : parent.width - sidebar.width//*sidebar.position - x: sidebar.width//*sidebar.position - - property bool firstDrawDone: false - - onPainted: if(!firstDrawDone) { - firstDrawDone = true; - console.info("First paint done in " + (new Date().getTime()-(StartTime*1000)) + "ms") - if(TestBuild == true) { - console.log("Plot drawn in canvas, terminating test of build in 100ms.") - testBuildTimer.start() - } - } - - Overlay.ViewPositionChange { - id: viewPositionChanger - anchors.fill: parent - } - - Overlay.PickLocation { - id: positionPicker - anchors.fill: parent - } - } - - Overlay.Loading { - id: loadingOverlay - anchors.fill: parent - } - - Timer { - id: delayRefreshTimer - repeat: false - interval: 1 - onTriggered: sidebarSelector.currentIndex = 0 - } - - Timer { - id: testBuildTimer - repeat: false - interval: 100 - onTriggered: Qt.quit() // Quit after paint on test build - } - - onClosing: function(close) { - if(!Modules.IO.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() - } - } - - /*! - \qmlmethod void LogarithmPlotter::copyDiagramToClipboard() - Copies the current diagram image to the clipboard. - */ - function copyDiagramToClipboard() { - var file = Helper.gettmpfile() - drawCanvas.save(file) - Helper.copyImageToClipboard() - alert.show(qsTr("Copied plot screenshot to clipboard!")) - } - - /*! - \qmlmethod void LogarithmPlotter::showAlert(string alertText) - Shows an alert on the diagram. - */ - function showAlert(alertText) { - // This function is called from the backend and is used to show alerts from there. - alert.show(alertText) - } - - - Menu { - id: updateMenu - title: qsTr("&Update") - Action { - text: qsTr("&Update LogarithmPlotter") - icon.name: 'update' - onTriggered: Qt.openUrlExternally("https://apps.ad5001.eu/logarithmplotter/") - } - } - - /*! - \qmlmethod void LogarithmPlotter::showUpdateMenu() - Shows the update menu in the AppMenuBar. - */ - 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/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/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/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/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/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 b446295..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'{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'{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 250fc64..0000000 --- a/runtime-pyside6/poetry.lock +++ /dev/null @@ -1,442 +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.6.4" -description = "Code coverage measurement for Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07"}, - {file = "coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a"}, - {file = "coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa"}, - {file = "coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522"}, - {file = "coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf"}, - {file = "coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5"}, - {file = "coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17"}, - {file = "coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a"}, - {file = "coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e"}, - {file = "coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef"}, - {file = "coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e"}, - {file = "coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901"}, - {file = "coverage-7.6.4-cp39-cp39-win32.whl", hash = "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09"}, - {file = "coverage-7.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f"}, - {file = "coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e"}, - {file = "coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73"}, -] - -[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.5.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, - {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, -] - -[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.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[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 = "24.1" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, -] - -[[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.11.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.11.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:6fd68a3c1207635c49326c54881b89d5c3bd9ba061bbc9daa58c0902db1be39e"}, - {file = "pyinstaller-6.11.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:eddd53f231e51adc65088eac4f40057ca803a990239828d4a9229407fb866239"}, - {file = "pyinstaller-6.11.0-py3-none-manylinux2014_i686.whl", hash = "sha256:e6d229009e815542833fe00332b589aa6984a06f794dc16f2ce1acab1c567590"}, - {file = "pyinstaller-6.11.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:7d2cd2ebdcd6860f8a4abe2977264a7b6d260a7147047008971c7cfc66a656a4"}, - {file = "pyinstaller-6.11.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:d9ec6d4398b4eebc1d4c00437716264ba8406bc2746f594e253070a82378a584"}, - {file = "pyinstaller-6.11.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:04f71828aa9531ab18c9656985c1f09b83d10332c73a1f4a113a48b491906955"}, - {file = "pyinstaller-6.11.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:a843d470768d68b05684ccf4860c45b2eb13727f41667c0b2cd8f57ae231bd18"}, - {file = "pyinstaller-6.11.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:963dedc1f37144a4385f58f7f65f1c69c004a67faae522a2085b5ddb230c908b"}, - {file = "pyinstaller-6.11.0-py3-none-win32.whl", hash = "sha256:c71024c8a19c7b221b9152b2baff4c3ba849cada68dcdd34382ba09f0107451f"}, - {file = "pyinstaller-6.11.0-py3-none-win_amd64.whl", hash = "sha256:0e229610c22b96d741d905706f9496af472c1a9216a118988f393c98ecc3f51f"}, - {file = "pyinstaller-6.11.0-py3-none-win_arm64.whl", hash = "sha256:a5f716bb507517912fda39d109dead91fc0dd2e7b2859562522b63c61aa21676"}, - {file = "pyinstaller-6.11.0.tar.gz", hash = "sha256:cb4d433a3db30d9d17cf5f2cf7bb4df80a788d493c1d67dd822dc5791d9864af"}, -] - -[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 = ">=2024.8" -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 = "2024.9" -description = "Community maintained hooks for PyInstaller" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyinstaller_hooks_contrib-2024.9-py3-none-any.whl", hash = "sha256:1ddf9ba21d586afa84e505bb5c65fca10b22500bf3fdb89ee2965b99da53b891"}, - {file = "pyinstaller_hooks_contrib-2024.9.tar.gz", hash = "sha256:4793869f370d1dc4806c101efd2890e3c3e703467d8d27bb5a3db005ebfb008d"}, -] - -[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.8.0.2" -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.8.0.2-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:30c9ca570dd18ffbfd34ee95e0a319c34313a80425c4011d6ccc9f4cca0dc4c8"}, - {file = "PySide6_Addons-6.8.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:754a9822ab2dc313f9998edef69d8a12bc9fd61727543f8d30806ed272ae1e52"}, - {file = "PySide6_Addons-6.8.0.2-cp39-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:553f3fa412f423929b5cd8b7d43fd5f02161851f10a438174a198b0f1a044df7"}, - {file = "PySide6_Addons-6.8.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:ae4377a3e10fe720a9119677b31d8de13e2a5221c06b332df045af002f5f4c3d"}, -] - -[package.dependencies] -PySide6-Essentials = "6.8.0.2" -shiboken6 = "6.8.0.2" - -[[package]] -name = "pyside6-essentials" -version = "6.8.0.2" -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.8.0.2-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:3df4ed75bbb74d74ac338b330819b1a272e7f5cec206765c7176a197c8bc9c79"}, - {file = "PySide6_Essentials-6.8.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:7df6d6c1da4858dbdea77c74d7270d9c68e8d1bbe3362892abd1a5ade3815a50"}, - {file = "PySide6_Essentials-6.8.0.2-cp39-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:cf490145d18812a6cff48b0b0afb0bfaf7066744bfbd09eb071c3323f1d6d00d"}, - {file = "PySide6_Essentials-6.8.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:d2f029b8c9f0106f57b26aa8c435435d7f509c80525075343e07177b283f862e"}, -] - -[package.dependencies] -shiboken6 = "6.8.0.2" - -[[package]] -name = "pytest" -version = "8.3.3" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, -] - -[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.2.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"}, - {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.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.2.0)", "jaraco.test", "packaging (>=23.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.11.*)", "pytest-mypy"] - -[[package]] -name = "shiboken6" -version = "6.8.0.2" -description = "Python/C++ bindings helper module" -optional = false -python-versions = "<3.14,>=3.9" -files = [ - {file = "shiboken6-6.8.0.2-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:9019e1fcfeed8bb350222e981748ef05a2fec11e31ddf616657be702f0b7a468"}, - {file = "shiboken6-6.8.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:fa7d411c3c67b4296847b3f5f572268e219d947d029ff9d8bce72fe6982d92bc"}, - {file = "shiboken6-6.8.0.2-cp39-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:1aaa8b7f9138818322ef029b2c487d1c6e00dc3f53084e62e1d11bdea47e47c2"}, - {file = "shiboken6-6.8.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:b11e750e696bb565d897e0f5836710edfb86bd355f87b09988bd31b2aad404d3"}, -] - -[[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.0.2" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, -] - -[[package]] -name = "zipp" -version = "3.20.2" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, - {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, -] - -[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 = "fad810a5ba9b4cb5ab759c9b5641ccba2b735e12064e510c0bfe0f4766c576f1" diff --git a/runtime-pyside6/pyproject.toml b/runtime-pyside6/pyproject.toml deleted file mode 100644 index cafc0cb..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.8" -PySide6-Addons = "^6.8" - -[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/globals.py b/runtime-pyside6/tests/globals.py deleted file mode 100644 index 67a1356..0000000 --- a/runtime-pyside6/tests/globals.py +++ /dev/null @@ -1,20 +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 LogarithmPlotter.logarithmplotter import create_qapp - -app = create_qapp() \ No newline at end of file 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 4ffbc6c..0000000 --- a/runtime-pyside6/tests/test_latex.py +++ /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 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 exists(path) - 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 - [path, width, height] = prerendered.split(",") - assert exists(path) - assert match(r"\d+", width) - assert match(r"\d+", height) - 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..03da330 100755 --- a/scripts/build-macosx.sh +++ b/scripts/build-macosx.sh @@ -1,35 +1,22 @@ #!/usr/bin/env bash DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd "$DIR/.." || exit 1 +cd "$DIR/.." -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<6.0 + +# 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 +25,21 @@ 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 +cp 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 +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} +rm -rf dist/LogarithmPlotter.app/Contents/MacOS/PySide6/Qt/qml/QtQuick3D +rm -rf dist/LogarithmPlotter.app/Contents/MacOS/PySide6/Qt/qml/Qt3D +rm -rf dist/LogarithmPlotter.app/Contents/MacOS/PySide6/Qt/qml/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} diff --git a/scripts/build-windows.bat b/scripts/build-windows.bat new file mode 100644 index 0000000..ef92aee --- /dev/null +++ b/scripts/build-windows.bat @@ -0,0 +1,17 @@ +rem Make sure pyinstaller is installed +python -m pip install -U pyinstaller + +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 + +rem Remove QtWebEngine +del dist\logarithmplotter\PySide6\Qt6WebEngineCore.dll +rem Remove the QtQuick styles that are unused +rmdir dist\logarithmplotter\PySide6\qml\QtQuick\Controls\Imagine /s /q +rmdir dist\logarithmplotter\PySide6\qml\QtQuick\Controls\Material /s /q +rmdir dist\logarithmplotter\PySide6\qml\QtQuick\Controls\designer /s /q +rem Remove unused translations diff --git a/scripts/build-wine.sh b/scripts/build-wine.sh index fa78bb1..638ba1d 100644 --- a/scripts/build-wine.sh +++ b/scripts/build-wine.sh @@ -1,36 +1,18 @@ #!/bin/bash -cd "$(dirname "$(readlink -f "$0" || realpath "$0")")/.." || exit +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 -rf dist +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 ../../ + +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 # Copy Qt6ShaderTools, a required library for for Qt5Compat PYSIDE6PATH="$(wine python -c "import PySide6; from os import path; print(path.dirname(PySide6.__file__));")" diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index 257b2c4..0000000 --- a/scripts/build.sh +++ /dev/null @@ -1,55 +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_DIR/LogarithmPlotter/" 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..af79276 --- /dev/null +++ b/scripts/package-linux.sh @@ -0,0 +1,32 @@ +#!/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 jammy --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 + git clone https://github.com/Ad5001/eu.ad5001.LogarithmPlotter + cd eu.ad5001.LogarithmPlotter + 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..f7e9cbe 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.5.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..8a7408c --- /dev/null +++ b/setup.py @@ -0,0 +1,156 @@ +""" + * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions. + * Copyright (C) 2021-2024 Ad5001 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +""" + +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/', ['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') + 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.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..0fbd1c7 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -1,16 +1,17 @@ name: logarithmplotter title: LogarithmPlotter -version: '0.6.0' +version: '0.5.0' summary: Create and edit 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: gtk-3-themes: @@ -57,26 +58,21 @@ 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 @@ -150,7 +146,7 @@ parts: source: . plugin: dump organize: - CHANGELOG.md: lib/python3.12/site-packages/LogarithmPlotter/util/ + CHANGELOG.md: lib/python3.8/site-packages/LogarithmPlotter/util/ apps: logarithmplotter: 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 98% rename from assets/native/win/installer.nsi rename to win/installer.nsi index bf38199..7c5b248 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.5.0" !define APP_VERSION "${VERSION_SHORT}.0" -!define COPYRIGHT "Ad5001 (c) 2021-2025" -!define DESCRIPTION "Create graphs with logarithmic scales." +!define COPYRIGHT "Ad5001 (c) 2021-2024" +!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" diff --git a/assets/native/win/logarithmplotter.ico b/win/logarithmplotter.ico similarity index 100% rename from assets/native/win/logarithmplotter.ico rename to win/logarithmplotter.ico