Compare commits

..

No commits in common. "master" and "rollup-js" have entirely different histories.

218 changed files with 5478 additions and 11822 deletions

6
.gitignore vendored
View file

@ -37,10 +37,8 @@ docs/html
*.lpf
*.lgg
# Tests
common/coverage/
**/.coverage
# npm
common/node_modules
common/coverage/
common/.coverage
runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/index.mjs*

View file

@ -1,4 +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/)
@ -24,7 +24,7 @@ First, you'll need to install all the required dependencies:
- [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.
`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`.
@ -68,19 +68,13 @@ 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 `poetry install --with test`
- Run `scripts/run-tests.sh`
## Legal notice
LogarithmPlotter - 2D plotter software to make Bode plots, sequences and repartition functions.
Copyright (C) 2021-2025 Ad5001 <mail@ad5001.eu>
Copyright (C) 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
@ -95,8 +89,8 @@ 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 <https://www.gnu.org/licenses/>.
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:
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:
- 🇭🇺 Hungarian translation by [Óvári](https://github.com/ovari)
- 🇳🇴 Norwegian translation by [Allan Nordhøy](https://github.com/comradekingu)
@ -109,5 +103,5 @@ of [ndef.parser](https://web.archive.org/web/20111023001618/http://www.undefined
&lt;r@undefined.ch&gt;, ported to javascript by Matthew Crumley
&lt;email@matthewcrumley.com&gt; (http://silentmatt.com/), and then to QMLJS by Ad5001.
All files in (common/src/lib/expr-eval/) except integration.mjs are licensed
All files in (LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/expr-eval/) except integration.mjs are licensed
under the [MIT License](https://raw.githubusercontent.com/silentmatt/expr-eval/master/LICENSE.txt).

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,2 +1,2 @@
#!/bin/bash
pyside6-lrelease *.ts
lrelease *.ts

View file

@ -21,7 +21,7 @@ replace() {
rm ../qml/eu/ad5001/LogarithmPlotter/js/index.mjs # Remove index which should not be scanned
files=$(find ../../common/src -name '*.mjs')
files=$(find .. -name *.mjs)
for file in $files; do
echo "Moving '$file' to '${file%.*}.js'..."
mv "$file" "${file%.*}.js"
@ -33,14 +33,12 @@ for file in $files; do
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
lupdate -extensions js,qs,qml,py -recursive .. -ts lp_*.ts
# Updating locations in files
for lp in *.ts; do
echo "Replacing locations in $lp..."
@ -57,9 +55,7 @@ for file in $files; do
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.#"
replace "$file" '.mjs"*/$' '.mjs"'
done

View file

@ -1 +0,0 @@
../common/appearance.svg

View file

@ -1 +0,0 @@
../common/appearance.svg

View file

@ -1 +0,0 @@
../common/arrow.svg

View file

@ -1 +0,0 @@
../common/position.svg

View file

@ -1 +0,0 @@
../common/angle.svg

View file

@ -1 +0,0 @@
../common/angle.svg

View file

@ -1 +0,0 @@
../common/appearance.svg

View file

@ -1 +0,0 @@
../common/target.svg

View file

@ -1 +0,0 @@
../common/position.svg

View file

@ -1 +0,0 @@
../common/label.svg

View file

@ -1 +0,0 @@
../common/angle.svg

View file

@ -1 +0,0 @@
../common/position.svg

View file

@ -1 +0,0 @@
../common/position.svg

View file

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1 @@
../../common/appearance.svg

View file

@ -0,0 +1 @@
../../common/appearance.svg

View file

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1 @@
../../common/arrow.svg

View file

@ -0,0 +1 @@
../../common/position.svg

View file

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1 @@
../../common/angle.svg

View file

@ -0,0 +1 @@
../../common/appearance.svg

View file

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1 @@
../../common/target.svg

View file

@ -0,0 +1 @@
../../common/position.svg

View file

@ -0,0 +1 @@
../../common/label.svg

View file

@ -0,0 +1 @@
../../common/angle.svg

View file

@ -0,0 +1 @@
../../common/position.svg

View file

@ -0,0 +1 @@
../../common/position.svg

View file

@ -0,0 +1 @@
../../common/angle.svg

View file

@ -1,171 +1,64 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48.0px"
height="48.0px"
viewBox="0 0 48.0 48.0"
width="24.0px"
height="24.0px"
viewBox="0 0 24.0 24.0"
version="1.1"
id="SVGRoot"
xmlns:xlink="http://www.w3.org/1999/xlink"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title
id="title38896">LogarithmPlotter Icon</title>
<defs
id="defs2254">
<linearGradient
id="linearGradient27593">
<stop
style="stop-color:#000000;stop-opacity:0.15000001;"
offset="0"
id="stop27589" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop27591" />
</linearGradient>
<linearGradient
id="linearGradient13467">
<stop
style="stop-color:#808080;stop-opacity:1;"
offset="0"
id="stop13463" />
<stop
style="stop-color:#666666;stop-opacity:1;"
offset="1"
id="stop13465" />
</linearGradient>
<linearGradient
id="linearGradient8377">
<stop
style="stop-color:#ebebeb;stop-opacity:1;"
offset="0"
id="stop8373" />
<stop
style="stop-color:#bfbfbf;stop-opacity:1;"
offset="1"
id="stop8375" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient8377"
id="linearGradient8379"
x1="12"
y1="4.8570137"
x2="12"
y2="21.105883"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#linearGradient13467"
id="linearGradient13469"
x1="12"
y1="9.5647058"
x2="12"
y2="21"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#linearGradient27593"
id="linearGradient27595"
x1="28"
y1="28"
x2="42"
y2="42"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="layer1">
<rect
style="fill:url(#linearGradient13469);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
xmlns:dc="http://purl.org/dc/elements/1.1/"><title
id="title836">LogarithmPlotter Icon v1.0</title><defs
id="defs833" /><metadata
id="metadata836"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title>LogarithmPlotter Icon v1.0</dc:title><cc:license
rdf:resource="http://creativecommons.org/licenses/by-nc-sa/4.0/" /><dc:date>2021</dc:date><dc:creator><cc:Agent><dc:title>Ad5001</dc:title></cc:Agent></dc:creator><dc:rights><cc:Agent><dc:title>(c) Ad5001 2021 - All rights reserved</dc:title></cc:Agent></dc:rights></cc:Work><cc:License
rdf:about="http://creativecommons.org/licenses/by-nc-sa/4.0/"><cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" /><cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" /><cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" /><cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" /><cc:prohibits
rdf:resource="http://creativecommons.org/ns#CommercialUse" /><cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /><cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" /></cc:License></rdf:RDF></metadata><g
id="layer2"
transform="matrix(1,0,0,0.94444444,0,1.1666667)"
style="fill:#666666"><rect
style="fill:#666666;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect1546"
width="18"
height="18.105883"
x="3"
y="2.8941176"
ry="2.3823531"
rx="2.2499998"
transform="matrix(2.2222222,0,0,2.0987654,-2.6666667,-0.07407404)" />
<rect
style="fill:url(#linearGradient8379);display:inline;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect1546-7"
width="18"
height="18.105883"
height="18"
x="3"
y="3"
ry="2.3212669"
rx="2.2499998"
transform="matrix(2.2222222,0,0,2.1539961,-2.6666667,-2.4619883)" />
</g>
<g
id="layer3"
style="fill:#0000ff">
<path
id="path27475"
style="fill:url(#linearGradient27595);fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 20,8 V 36 H 10 l 7,7 h 15 7 c 2.769997,0 5,-2.230003 5,-5 v -5 -1 z" />
</g>
<g
id="layer1-6"
style="stroke-width:2;stroke-dasharray:none"
transform="matrix(2,0,0,2,0,1)">
<rect
ry="2.25" /></g><g
id="layer2-6"
transform="matrix(1,0,0,0.94444444,0,0.16666668)"
style="fill:#f9f9f9"><rect
style="fill:#f9f9f9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect1546-7"
width="18"
height="18"
x="3"
y="3"
ry="2.25" /></g><g
id="layer1"
style="stroke-width:2;stroke-dasharray:none"><rect
style="fill:#000000;fill-rule:evenodd;stroke-width:1.86898;stroke-dasharray:none;stroke-opacity:0"
id="rect1410"
width="14"
height="2"
x="5"
y="15.5" />
<rect
style="fill:#000000;fill-rule:evenodd;stroke-width:2.06559;stroke-dasharray:none;stroke-opacity:0"
y="15.5" /><rect
style="fill:#000000;fill-rule:evenodd;stroke-width:2;stroke-dasharray:none;stroke-opacity:0"
id="rect1412"
width="2"
height="16"
x="8"
y="3.5" />
<path
height="15"
x="9"
y="3.9768662" /><path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path1529"
d="m 18,3.5 c 0,7 -4,12 -13,12" />
</g>
<metadata
id="metadata38894">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>LogarithmPlotter Icon</dc:title>
<dc:date>2024-10-06</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Adsooi &lt;mail@ad5001.eu&gt;</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title>(c) Adsooi 2021-2024</dc:title>
</cc:Agent>
</dc:rights>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-nc-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-nc-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:prohibits
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
</svg>
d="M 18,4 C 18,10.017307 13.40948,15.5 5,15.5" /></g></svg>

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -3,7 +3,7 @@ Source: logarithmplotter
Version: 0.6.0
Architecture: all
Maintainer: Ad5001 <mail@ad5001.eu>
Depends: python3 (>= 3.9), python3-pip, python3-pyside6-essentials (>= 6.7.0), python3-pyside6-addons (>= 6.7), texlive-latex-base, dvipng
Depends: python3 (>= 3.9), python3-pip, python3-pyside6-essentials (>= 6.7.0), texlive-latex-base, dvipng
Build-Depends: debhelper (>=11~), dh-python, dpkg-dev (>= 1.16.1~), python-setuptools
Section: science

View file

@ -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

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2025 Ad5001 -->
<!-- Copyright 2024 Ad5001 -->
<component type="desktop-application">
<id>eu.ad5001.LogarithmPlotter</id>
<launchable type="desktop-id">logarithmplotter.desktop</launchable>
@ -66,54 +66,50 @@
<categories>
<category>Science</category>
<category>Education</category>
<category>Qt</category>
</categories>
<url type="homepage">https://apps.ad5001.eu/logarithmplotter/</url>
<url type="bugtracker">https://git.ad5001.eu/Ad5001/LogarithmPlotter/issues/</url>
<url type="help">https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/</url>
<url type="translate">https://hosted.weblate.org/engage/logarithmplotter/</url>
<screenshots>
<screenshot type="default">
<image>https://apps.ad5001.eu/img/en/logarithmplotter/gain.png?v=0.6</image>
<image xml:lang="de">https://apps.ad5001.eu/img/de/logarithmplotter/gain.png?v=0.6</image>
<image xml:lang="fr">https://apps.ad5001.eu/img/fr/logarithmplotter/gain.png?v=0.6</image>
<image xml:lang="hu">https://apps.ad5001.eu/img/hu/logarithmplotter/gain.png?v=0.6</image>
<image xml:lang="no">https://apps.ad5001.eu/img/no/logarithmplotter/gain.png?v=0.6</image>
<image xml:lang="es">https://apps.ad5001.eu/img/es/logarithmplotter/gain.png?v=0.6</image>
<image>https://apps.ad5001.eu/img/en/logarithmplotter/gain.png?v=0.5</image>
<image xml:lang="de">https://apps.ad5001.eu/img/de/logarithmplotter/gain.png?v=0.5</image>
<image xml:lang="fr">https://apps.ad5001.eu/img/fr/logarithmplotter/gain.png?v=0.5</image>
<image xml:lang="hu">https://apps.ad5001.eu/img/hu/logarithmplotter/gain.png?v=0.5</image>
<image xml:lang="no">https://apps.ad5001.eu/img/no/logarithmplotter/gain.png?v=0.5</image>
<caption>Main view of LogarithmPlotter showing an asymptotic Bode magnitude plot.</caption>
<caption xml:lang="de">Die Hauptansicht des LogarithmPlotters zeigt eine asymptotische Bode-Magnitude-Darstellung.</caption>
<caption xml:lang="fr">Vue principale de LogarithmPlotter montrant un tracé asymptotique d'une magnitude de Bode.</caption>
<caption xml:lang="hu">A LogarithmPlotter fő nézete, amely egy aszimptotikus Bode-magnitúdó ábrát mutat.</caption>
<caption xml:lang="no">Hovedvisning av LogarithmPlotter som viser et asymptotisk Bode-størrelsesplott.</caption>
<caption xml:lang="es">Vista principal de LogarithmPlotter mostrando un gráfico asintótico de una magnitud de Bode.</caption>
</screenshot>
<screenshot>
<image>https://apps.ad5001.eu/img/en/logarithmplotter/phase.png?v=0.6</image>
<image xml:lang="de">https://apps.ad5001.eu/img/de/logarithmplotter/phase.png?v=0.6</image>
<image xml:lang="fr">https://apps.ad5001.eu/img/fr/logarithmplotter/phase.png?v=0.6</image>
<image xml:lang="hu">https://apps.ad5001.eu/img/hu/logarithmplotter/phase.png?v=0.6</image>
<image xml:lang="no">https://apps.ad5001.eu/img/no/logarithmplotter/phase.png?v=0.6</image>
<image xml:lang="es">https://apps.ad5001.eu/img/es/logarithmplotter/phase.png?v=0.6</image>
<image>https://apps.ad5001.eu/img/en/logarithmplotter/phase.png?v=0.5</image>
<image xml:lang="de">https://apps.ad5001.eu/img/de/logarithmplotter/phase.png?v=0.5</image>
<image xml:lang="fr">https://apps.ad5001.eu/img/fr/logarithmplotter/phase.png?v=0.5</image>
<image xml:lang="hu">https://apps.ad5001.eu/img/hu/logarithmplotter/phase.png?v=0.5</image>
<image xml:lang="no">https://apps.ad5001.eu/img/no/logarithmplotter/phase.png?v=0.5</image>
<caption>Main view of LogarithmPlotter showing an asymptotic Bode phase plot.</caption>
<caption xml:lang="de">Hauptansicht des LogarithmPlotters mit einer asymptotischen Bode-Phasendarstellung.</caption>
<caption xml:lang="fr">Vue principale de LogarithmPlotter montrant un tracé asymptotique d'une phase de Bode.</caption>
<caption xml:lang="hu">A LogarithmPlotter fő nézete, amely egy aszimptotikus Bode-fázis ábrát mutat.</caption>
<caption xml:lang="no">Hovedvisning av LogarithmPlotter som viser et asymptotisk Bode-fasediagram.</caption>
<caption xml:lang="es">Vista principal de LogarithmPlotter mostrando un gráfico asintótico de una fase de Bode.</caption>
</screenshot>
<screenshot>
<image>https://apps.ad5001.eu/img/en/logarithmplotter/welcome.png?v=0.6</image>
<image xml:lang="de">https://apps.ad5001.eu/img/de/logarithmplotter/welcome.png?v=0.6</image>
<image xml:lang="fr">https://apps.ad5001.eu/img/fr/logarithmplotter/welcome.png?v=0.6</image>
<image xml:lang="hu">https://apps.ad5001.eu/img/hu/logarithmplotter/welcome.png?v=0.6</image>
<image xml:lang="no">https://apps.ad5001.eu/img/no/logarithmplotter/welcome.png?v=0.6</image>
<image xml:lang="es">https://apps.ad5001.eu/img/es/logarithmplotter/welcome.png?v=0.6</image>
<image>https://apps.ad5001.eu/img/en/logarithmplotter/welcome.png?v=0.5</image>
<image xml:lang="de">https://apps.ad5001.eu/img/de/logarithmplotter/welcome.png?v=0.5</image>
<image xml:lang="fr">https://apps.ad5001.eu/img/fr/logarithmplotter/welcome.png?v=0.5</image>
<image xml:lang="hu">https://apps.ad5001.eu/img/hu/logarithmplotter/welcome.png?v=0.5</image>
<image xml:lang="no">https://apps.ad5001.eu/img/no/logarithmplotter/welcome.png?v=0.5</image>
<caption>LogarithmPlotter's welcome page.</caption>
<caption xml:lang="de">LogarithmPlotter's Willkommensseite.</caption>
<caption xml:lang="fr">Page d'accueil de LogarithmPlotter.</caption>
<caption xml:lang="hu">LogarithmPlotter üdvözlő oldala.</caption>
<caption xml:lang="no">LogarithmPlotters velkomstside.</caption>
<caption xml:lang="es">Página de bienvenida de LogarithmPlotter.</caption>
</screenshot>
</screenshots>

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type xmlns="http://www.freedesktop.org/standards/shared-mime-info" type="application/x-logarithm-plot">
<comment>Logarithmic Plot File</comment>
<comment>Logarithm Plot File</comment>
<comment xml:lang="fr">Fichier Graphe Logarithmique</comment>
<icon name="application-x-logarithm-plot"/>
<glob-deleteall/>

View file

@ -13,7 +13,7 @@ Unicode True
!define WEBSITE "https://apps.ad5001.eu/logarithmplotter"
!define VERSION_SHORT "0.6.0"
!define APP_VERSION "${VERSION_SHORT}.0"
!define COPYRIGHT "Ad5001 (c) 2021-2025"
!define COPYRIGHT "Ad5001 (c) 2021-2024"
!define DESCRIPTION "Create graphs with logarithmic scales."
!define REG_UNINSTALL "Software\Microsoft\Windows\CurrentVersion\Uninstall\LogarithmPlotter"

View file

@ -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"}

View file

@ -12,29 +12,32 @@ steps:
- git submodule update --init --recursive
- name: Build
image: ad5001/ubuntu-pyside-xvfb:linux-6-latest-latex-node
image: node:18-bookworm
commands:
- cd common && npm install && cd ..
- apt update
- apt install -y qtchooser qttools5-dev-tools
# Start building
- bash scripts/build.sh
when:
event: [ push, tag ]
- name: Unit Tests
image: ad5001/ubuntu-pyside-xvfb:linux-6-latest-latex-node
image: ad5001/ubuntu-pyside-xvfb:linux-6-latest-latex
commands:
- apt update
- apt install -y npm
- cd common && npm install -D && cd ..
- xvfb-run bash scripts/run-tests.sh --no-rebuild
when:
event: [ push, tag ]
- name: File Tests
image: ad5001/ubuntu-pyside-xvfb:linux-6-latest-latex-node
image: ad5001/ubuntu-pyside-xvfb:linux-6-latest-latex
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 ]

File diff suppressed because one or more lines are too long

View file

@ -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"}

File diff suppressed because one or more lines are too long

1539
common/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
"name": "logarithmplotter",
"version": "0.6.0",
"description": "2D plotter software to make Bode plots, sequences and distribution functions.",
"main": "src/index.mjs",
"main": "LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/autoload.mjs",
"scripts": {
"build": "rollup --config rollup.config.mjs",
"test": "c8 mocha test/**/*.mjs"
@ -24,12 +24,9 @@
},
"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"
}

View file

@ -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
@ -22,7 +22,7 @@ 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"
const dest = "../build/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/index.mjs"
export default {
input: src,

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
/**
* 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<string, Set<function>>} */
#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)
}
}

View file

@ -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

View file

@ -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
@ -95,15 +95,11 @@ export class Action {
if(!Latex.enabled)
throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.")
const imgDepth = History.imageDepth
const renderArguments = [
const { source, width, height } = await Latex.requestAsyncRender(
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 `<img src="${source}" width="${width / imgDepth}" height="${height / imgDepth}" style="vertical-align: middle"/>`
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,7 +19,7 @@
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 { escapeHTML } from "../utils.mjs"
import { Action } from "./common.mjs"
/**

View file

@ -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

View file

@ -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,11 +18,10 @@
import js from "./lib/polyfills/js.mjs"
export * as Utils from "./utils/index.mjs"
import * as Modules from "./module/index.mjs"
import * as ObjsAutoload from "./objs/autoload.mjs"
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"
export * as Utils from "./utils.mjs"

View file

@ -7,7 +7,7 @@
*
* Ported to QMLJS with modifications done accordingly done by Ad5001 <mail@ad5001.eu> (https://ad5001.eu)
*
* Copyright (c) 2015 Matthew Crumley, 2021-2025 Ad5001
* Copyright (c) 2015 Matthew Crumley, 2021-2024 Ad5001
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
@ -111,7 +111,7 @@ function simplify(tokens, unaryOps, binaryOps, ternaryOps, values) {
* In the given instructions, replaces variable by expr.
* @param {Instruction[]} tokens
* @param {string} variable
* @param {ExprEvalExpression} expr
* @param {number} expr
* @return {Instruction[]}
*/
function substitute(tokens, variable, expr) {
@ -171,6 +171,9 @@ function evaluate(tokens, expr, values) {
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)))
@ -487,6 +490,18 @@ export class ExprEvalExpression {
return evaluate(this.tokens, this, values)
}
/**
* Returns a list of symbols (string of characters) in the expressions.
* Can be functions, constants, or variables.
* @returns {string[]}
*/
symbols(options) {
options = options || {}
const vars = []
getSymbols(this.tokens, vars, options)
return vars
}
toString() {
return expressionToString(this.tokens, false)
}

View file

@ -7,7 +7,7 @@
*
* Ported to QMLJS with modifications done accordingly done by Ad5001 <mail@ad5001.eu> (https://ad5001.eu)
*
* Copyright (c) 2015 Matthew Crumley, 2021-2025 Ad5001
* Copyright (c) 2015 Matthew Crumley, 2021-2024 Ad5001
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

View file

@ -7,7 +7,7 @@
*
* Ported to QMLJS with modifications done accordingly done by Ad5001 <mail@ad5001.eu> (https://ad5001.eu)
*
* Copyright (c) 2015 Matthew Crumley, 2021-2025 Ad5001
* Copyright (c) 2015 Matthew Crumley, 2021-2024 Ad5001
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
@ -47,7 +47,9 @@ const optionNameMap = {
"not": "logical",
"?": "conditional",
":": "conditional",
//'=': 'assignment', // Disable assignment
"[": "array"
//'()=': 'fndef' // Diable function definition
}
export class Parser {
@ -107,6 +109,7 @@ export class Parser {
and: Polyfill.andOperator,
or: Polyfill.orOperator,
"in": Polyfill.inOperator,
"=": Polyfill.setVar,
"[": Polyfill.arrayIndex
}
@ -120,13 +123,18 @@ export class Parser {
min: Polyfill.min,
max: Polyfill.max,
hypot: Math.hypot || Polyfill.hypot,
pyt: Math.hypot || Polyfill.hypot,
pyt: Math.hypot || Polyfill.hypot, // backward compat
pow: Math.pow,
atan2: Math.atan2,
"if": Polyfill.condition,
gamma: Polyfill.gamma,
"Γ": Polyfill.gamma,
roundTo: Polyfill.roundTo,
map: Polyfill.arrayMap,
fold: Polyfill.arrayFold,
filter: Polyfill.arrayFilter,
indexOf: Polyfill.stringOrArrayIndexOf,
join: Polyfill.arrayJoin
}
// These constants will automatically be replaced the MOMENT they are parsed.
@ -151,6 +159,10 @@ export class Parser {
return new ExprEvalExpression(instr, this)
}
evaluate(expr, variables) {
return this.parse(expr).evaluate(variables)
}
isOperatorEnabled(op) {
const optionName = optionNameMap.hasOwnProperty(op) ? optionNameMap[op] : op
const operators = this.options.operators || {}

View file

@ -7,7 +7,7 @@
*
* Ported to QMLJS with modifications done accordingly done by Ad5001 <mail@ad5001.eu> (https://ad5001.eu)
*
* Copyright (c) 2015 Matthew Crumley, 2021-2025 Ad5001
* Copyright (c) 2015 Matthew Crumley, 2021-2024 Ad5001
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

View file

@ -7,7 +7,7 @@
*
* Ported to QMLJS with modifications done accordingly done by Ad5001 <mail@ad5001.eu> (https://ad5001.eu)
*
* Copyright (c) 2015 Matthew Crumley, 2021-2025 Ad5001
* Copyright (c) 2015 Matthew Crumley, 2021-2024 Ad5001
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
@ -210,8 +210,9 @@ export function gamma(n) {
}
export function stringOrArrayLength(s) {
if(Array.isArray(s))
if(Array.isArray(s)) {
return s.length
}
return String(s).length
}
@ -266,6 +267,11 @@ export function roundTo(value, exp) {
return +(value[0] + "e" + (value[1] ? (+value[1] + exp) : exp))
}
export function setVar(name, value, variables) {
if(variables) variables[name] = value
return value
}
export function arrayIndex(array, index) {
return array[index | 0]
}
@ -290,6 +296,58 @@ export function min(array) {
}
}
export 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)
})
}
export 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)
}
export 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)
})
}
export 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)
}
export 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)
}
export function sign(x) {
return ((x > 0) - (x < 0)) || +x
}

View file

@ -7,7 +7,7 @@
*
* Ported to QMLJS with modifications done accordingly done by Ad5001 <mail@ad5001.eu> (https://ad5001.eu)
*
* Copyright (c) 2015 Matthew Crumley, 2021-2025 Ad5001
* Copyright (c) 2015 Matthew Crumley, 2021-2024 Ad5001
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
@ -472,7 +472,7 @@ export class TokenStream {
this.current = this.newToken(TOP, "==")
this.pos++
} else {
return false
this.current = this.newToken(TOP, c)
}
} else if(c === "!") {
if(this.expression.charAt(this.pos + 1) === "=") {

View file

@ -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
@ -64,20 +64,10 @@ function arrayFlatMap(callbackFn, thisArg) {
* @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]
let str = this
while(str.includes(from))
str = str.replace(from, to)
return str
}
@ -108,8 +98,8 @@ const polyfills = {
[String.prototype, "replaceAll", stringReplaceAll]
],
2022: [
[Array.prototype, "at", arrayAt],
[String.prototype, "at", arrayAt],
[Array.prototype, "at", notPolyfilled("Array.prototype.at")],
[String.prototype, "at", notPolyfilled("String.prototype.at")],
[Object, "hasOwn", notPolyfilled("Object.hasOwn")]
],
2023: [
@ -119,7 +109,7 @@ const polyfills = {
[Array.prototype, "toSpliced", notPolyfilled("Array.prototype.toSpliced")],
[Array.prototype, "with", notPolyfilled("Array.prototype.with")]
],
2025: [
2024: [
[Object, "groupBy", notPolyfilled("Object.groupBy")],
[Map, "groupBy", notPolyfilled("Map.groupBy")]
]

View file

@ -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

View file

@ -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
@ -24,7 +24,6 @@ import { Expression, executeExpression } from "./expression.mjs"
*/
export class Domain {
constructor() {
this.latexMarkup = "#INVALID"
}
/**
@ -207,8 +206,8 @@ export class Range extends Domain {
}
includes(x) {
if(typeof x == "string") x = executeExpression(x)
if(x instanceof Expression) x = x.execute()
if(typeof x == "string") x = executeExpression(x)
return ((this.openBegin && x > this.begin.execute()) || (!this.openBegin && x >= this.begin.execute())) &&
((this.openEnd && x < this.end.execute()) || (!this.openEnd && x <= this.end.execute()))
}
@ -250,17 +249,15 @@ export 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.
*/
constructor(displayName, latexMarkup, isValid, next = () => true, previous = () => true,
constructor(displayName, isValid, next = () => true, previous = () => true,
moveSupported = true) {
super()
this.displayName = displayName
this.latexMarkup = latexMarkup
this.isValid = isValid
this.nextValue = next
this.prevValue = previous
@ -565,54 +562,39 @@ Domain.RPE.latexMarkup = "\\mathbb{R}^{+*}"
Domain.RME = new Range(-Infinity, 0, true, true)
Domain.RME.displayName = "ℝ⁻*"
Domain.RME.latexMarkup = "\\mathbb{R}^{+*}"
Domain.N = new SpecialDomain(
"", "\\mathbb{N}",
x => x % 1 === 0 && x >= 0,
Domain.N = new SpecialDomain("", x => x % 1 === 0 && x >= 0,
x => Math.max(Math.floor(x) + 1, 0),
x => Math.max(Math.ceil(x) - 1, 0)
)
Domain.NE = new SpecialDomain(
"*", "\\mathbb{N}^{*}",
x => x % 1 === 0 && x > 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.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.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.ZM = new SpecialDomain(
"ℤ⁻", "\\mathbb{Z}^{-}",
x => x % 1 === 0 && x <= 0,
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.ZME = new SpecialDomain(
"ℤ⁻*", "\\mathbb{Z}^{-*}",
x => x % 1 === 0 && x < 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.NLog = new SpecialDomain(
"ℕˡᵒᵍ", "\\mathbb{N}^{log}",
x => Math.min(Math.ceil(x) - 1, -1))
Domain.ZME.latexMarkup = "\\mathbb{Z}^{-*}"
Domain.NLog = new SpecialDomain("ℕˡᵒᵍ",
x => x / Math.pow(10, Math.ceil(Math.log10(x))) % 1 === 0 && x > 0,
x => {
function(x) {
let x10pow = Math.pow(10, Math.ceil(Math.log10(x)))
return Math.max(1, (Math.floor(x / x10pow) + 1) * x10pow)
},
x => {
function(x) {
let x10pow = Math.pow(10, Math.ceil(Math.log10(x)))
return Math.max(1, (Math.ceil(x / x10pow) - 1) * x10pow)
}
)
})
Domain.NLog.latexMarkup = "\\mathbb{N}^{log}"
let refedDomains = []
@ -644,7 +626,7 @@ export function parseDomainSimple(domain) {
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)

View file

@ -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,38 +17,28 @@
*/
import * as Utils from "../utils/index.mjs"
import { ExprEvalExpression } from "../lib/expr-eval/expression.mjs"
import * as Utils from "../utils.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) {
} else {
// 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.cached = this.isConstant()
this.cachedValue = null
if(this.canBeCached && this.allRequirementsFulfilled())
this.recache()
if(this.cached && this.allRequirementsFulfilled())
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
this.latexMarkup = Latex.expression(this.calc.tokens)
}
@ -87,20 +77,21 @@ export class Expression {
/**
* Returns a list of names whose corresponding objects this expression is dependant on and are missing.
* @return {string[]}
* @return {boolean}
*/
undefinedVariables() {
return this.requiredObjects().filter(objName => !(objName in Objects.currentObjectsByName))
}
recache() {
if(this.cached)
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
}
execute(x = 1) {
if(this.canBeCached) {
if(this.cached) {
if(this.cachedValue == null)
this.recache()
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
return this.cachedValue
}
ExprParser.currentVars = Object.assign({ "x": x }, Objects.currentObjectsByName)
@ -108,10 +99,9 @@ export class Expression {
}
simplify(x) {
let expr = new Expression(this.calc.substitute("x", x).simplify())
if(expr.allRequirementsFulfilled() && expr.execute() === 0)
expr = new Expression("0")
return expr
let expr = this.calc.substitute("x", x).simplify()
if(expr.evaluate() === 0) expr = "0"
return new Expression(expr)
}
toEditableString() {
@ -120,28 +110,17 @@ export class Expression {
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) {
if(str !== undefined && str.match(/^\d*\.\d+$/)) {
if(str.split(".")[1].split("0").length > 7) {
// Likely rounding error
str = parseFloat(str).toDecimalPrecision(8).toString()
str = parseFloat(str.substring(0, str.length - 1)).toString()
}
}
if(str[0] === "(" && str.at(-1) === ")")
str = str.substring(1, str.length - 1)
if(str[0] !== "-" && forceSign)
str = "+" + str
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()
}

View file

@ -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

View file

@ -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 * as Expr from "./expression.mjs"
import * as Utils from "../utils/index.mjs"
import * as Utils from "../utils.mjs"
import Latex from "../module/latex.mjs"
import Objects from "../module/objects.mjs"
import ExprParser from "../module/expreval.mjs"

View file

@ -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,33 +18,30 @@
import { Module } from "./common.mjs"
import { CanvasInterface, DialogInterface } from "./interface.mjs"
import { textsup } from "../utils/index.mjs"
import { textsup } from "../utils.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 {CanvasInterface} */
this._canvas = null
/** @type {CanvasRenderingContext2D} */
this._ctx = null
/**
* @type {{show(string, string, string)}}
* @private
*/
this._drawingErrorDialog = null
/**
*
* @type {Object.<string, {expression: Expression, value: number, maxDraw: number}>}
@ -70,18 +67,18 @@ class CanvasAPI extends Module {
*/
initialize({ canvas, drawingErrorDialog }) {
super.initialize({ canvas, drawingErrorDialog })
this.#canvas = canvas
this.#drawingErrorDialog = drawingErrorDialog
this._canvas = canvas
this._drawingErrorDialog = drawingErrorDialog
}
get width() {
if(!this.initialized) throw new Error("Attempting width before initialize!")
return this.#canvas.width
return this._canvas.width
}
get height() {
if(!this.initialized) throw new Error("Attempting height before initialize!")
return this.#canvas.height
return this._canvas.height
}
/**
@ -90,7 +87,7 @@ class CanvasAPI extends Module {
*/
get xmin() {
if(!this.initialized) throw new Error("Attempting xmin before initialize!")
return Settings.xmin
return this._canvas.xmin
}
/**
@ -99,7 +96,7 @@ class CanvasAPI extends Module {
*/
get xzoom() {
if(!this.initialized) throw new Error("Attempting xzoom before initialize!")
return Settings.xzoom
return this._canvas.xzoom
}
/**
@ -108,7 +105,7 @@ class CanvasAPI extends Module {
*/
get ymax() {
if(!this.initialized) throw new Error("Attempting ymax before initialize!")
return Settings.ymax
return this._canvas.ymax
}
/**
@ -117,7 +114,7 @@ class CanvasAPI extends Module {
*/
get yzoom() {
if(!this.initialized) throw new Error("Attempting yzoom before initialize!")
return Settings.yzoom
return this._canvas.yzoom
}
/**
@ -126,7 +123,7 @@ class CanvasAPI extends Module {
*/
get xlabel() {
if(!this.initialized) throw new Error("Attempting xlabel before initialize!")
return Settings.xlabel
return this._canvas.xlabel
}
/**
@ -135,7 +132,7 @@ class CanvasAPI extends Module {
*/
get ylabel() {
if(!this.initialized) throw new Error("Attempting ylabel before initialize!")
return Settings.ylabel
return this._canvas.ylabel
}
/**
@ -144,7 +141,7 @@ class CanvasAPI extends Module {
*/
get linewidth() {
if(!this.initialized) throw new Error("Attempting linewidth before initialize!")
return Settings.linewidth
return this._canvas.linewidth
}
/**
@ -153,7 +150,7 @@ class CanvasAPI extends Module {
*/
get textsize() {
if(!this.initialized) throw new Error("Attempting textsize before initialize!")
return Settings.textsize
return this._canvas.textsize
}
/**
@ -162,7 +159,7 @@ class CanvasAPI extends Module {
*/
get logscalex() {
if(!this.initialized) throw new Error("Attempting logscalex before initialize!")
return Settings.logscalex
return this._canvas.logscalex
}
/**
@ -171,7 +168,7 @@ class CanvasAPI extends Module {
*/
get showxgrad() {
if(!this.initialized) throw new Error("Attempting showxgrad before initialize!")
return Settings.showxgrad
return this._canvas.showxgrad
}
/**
@ -180,7 +177,7 @@ class CanvasAPI extends Module {
*/
get showygrad() {
if(!this.initialized) throw new Error("Attempting showygrad before initialize!")
return Settings.showygrad
return this._canvas.showygrad
}
/**
@ -204,7 +201,7 @@ class CanvasAPI extends Module {
requestPaint() {
if(!this.initialized) throw new Error("Attempting requestPaint before initialize!")
this.#canvas.requestPaint()
this._canvas.requestPaint()
}
/**
@ -212,19 +209,17 @@ class CanvasAPI extends Module {
*/
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._ctx = this._canvas.getContext("2d")
this._computeAxes()
this._reset()
this._drawGrid()
this._drawAxes()
this._drawLabels()
this.#ctx.lineWidth = this.linewidth
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
this._ctx.strokeStyle = obj.color
this._ctx.fillStyle = obj.color
if(obj.visible)
try {
obj.draw(this)
@ -232,12 +227,12 @@ class CanvasAPI extends Module {
// 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)
this._drawingErrorDialog.show(objType, obj.name, e.message)
History.undo()
}
}
}
this.#ctx.lineWidth = 1
this._ctx.lineWidth = 1
}
/**
@ -245,9 +240,9 @@ class CanvasAPI extends Module {
* @private
*/
_computeAxes() {
let exprY = new Expression(`x*(${Settings.yaxisstep})`)
let exprY = new Expression(`x*(${this._canvas.yaxisstep})`)
let y1 = exprY.execute(1)
let exprX = new Expression(`x*(${Settings.xaxisstep})`)
let exprX = new Expression(`x*(${this._canvas.xaxisstep})`)
let x1 = exprX.execute(1)
this.axesSteps = {
x: {
@ -269,10 +264,10 @@ class CanvasAPI extends Module {
*/
_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)
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)
}
/**
@ -280,7 +275,7 @@ class CanvasAPI extends Module {
* @private
*/
_drawGrid() {
this.#ctx.strokeStyle = "#C0C0C0"
this._ctx.strokeStyle = "#C0C0C0"
if(this.logscalex) {
for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow++) {
for(let xmulti = 1; xmulti < 10; xmulti++) {
@ -304,7 +299,7 @@ class CanvasAPI extends Module {
* @private
*/
_drawAxes() {
this.#ctx.strokeStyle = "#000000"
this._ctx.strokeStyle = "#000000"
let axisypos = this.logscalex ? 1 : 0
this.drawXLine(axisypos)
this.drawYLine(0)
@ -325,19 +320,19 @@ class CanvasAPI extends Module {
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)
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`
this._ctx.font = `${this.textsize - 4}px sans-serif`
let txtMinus = this.#ctx.measureText("-").width
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
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)))
}
@ -355,13 +350,13 @@ class CanvasAPI extends Module {
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
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"
this._ctx.fillStyle = "#FFFFFF"
}
//
@ -399,7 +394,7 @@ class CanvasAPI extends Module {
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))
this._ctx.fillText(txt, x, y + (this.textsize * i))
})
}
}
@ -414,8 +409,8 @@ class CanvasAPI extends Module {
* @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)
this._canvas.markDirty(Qt.rect(x, y, width, height))
this._ctx.drawImage(image, x, y, width, height)
}
/**
@ -429,7 +424,7 @@ class CanvasAPI extends Module {
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
if(this._ctx.measureText(txt).width > twidth) twidth = this._ctx.measureText(txt).width
}
return { "width": twidth, "height": theight }
}
@ -499,10 +494,10 @@ class CanvasAPI extends Module {
* @param {number} y2
*/
drawLine(x1, y1, x2, y2) {
this.#ctx.beginPath()
this.#ctx.moveTo(x1, y1)
this.#ctx.lineTo(x2, y2)
this.#ctx.stroke()
this._ctx.beginPath()
this._ctx.moveTo(x1, y1)
this._ctx.lineTo(x2, y2)
this._ctx.stroke()
}
/**
@ -514,9 +509,9 @@ class CanvasAPI extends Module {
* @param {number} dashPxSize
*/
drawDashedLine(x1, y1, x2, y2, dashPxSize = 6) {
this.#ctx.setLineDash([dashPxSize / 2, dashPxSize])
this._ctx.setLineDash([dashPxSize / 2, dashPxSize])
this.drawLine(x1, y1, x2, y2)
this.#ctx.setLineDash([])
this._ctx.setLineDash([])
}
/**
@ -526,22 +521,14 @@ class CanvasAPI extends Module {
* @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)) {
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)
})
this._canvas.loadImage(imgData.source)
this._canvas.imageLoaders[imgData.source] = [callback, imgData]
} 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)
@ -556,11 +543,11 @@ class CanvasAPI extends Module {
//
get font() {
return this.#ctx.font
return this._ctx.font
}
set font(value) {
return this.#ctx.font = value
return this._ctx.font = value
}
/**
@ -573,9 +560,9 @@ class CanvasAPI extends Module {
* @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()
this._ctx.beginPath()
this._ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise)
this._ctx.stroke()
}
/**
@ -585,9 +572,9 @@ class CanvasAPI extends Module {
* @param {number} radius
*/
disc(x, y, radius) {
this.#ctx.beginPath()
this.#ctx.arc(x, y, radius, 0, 2 * Math.PI)
this.#ctx.fill()
this._ctx.beginPath()
this._ctx.arc(x, y, radius, 0, 2 * Math.PI)
this._ctx.fill()
}
/**
@ -598,7 +585,7 @@ class CanvasAPI extends Module {
* @param {number} h
*/
fillRect(x, y, w, h) {
this.#ctx.fillRect(x, y, w, h)
this._ctx.fillRect(x, y, w, h)
}
}

View file

@ -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,6 @@
*/
import { Interface } from "./interface.mjs"
import { BaseEventEmitter } from "../events.mjs"
// Define Modules interface before they are imported.
globalThis.Modules = globalThis.Modules || {}
@ -25,13 +24,7 @@ globalThis.Modules = globalThis.Modules || {}
/**
* Base class for global APIs in runtime.
*/
export class Module extends BaseEventEmitter {
/** @type {string} */
#name
/** @type {Object.<string, (Interface|string|number|boolean)>} */
#initializationParameters
/** @type {boolean} */
#initialized = false
export class Module {
/**
*
@ -39,18 +32,11 @@ export class Module extends BaseEventEmitter {
* @param {Object.<string, (Interface|string|number|boolean)>} initializationParameters - List of parameters for the initialize function.
*/
constructor(name, initializationParameters = {}) {
super()
console.log(`Loading module ${name}...`)
this.#name = name
this.#initializationParameters = initializationParameters
}
this.__name = name
this.__initializationParameters = initializationParameters
this.initialized = false
get name() {
return this.#name;
}
get initialized() {
return this.#initialized
}
/**
@ -58,17 +44,17 @@ export class Module extends BaseEventEmitter {
* @param {Object.<string, any>} 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(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.`)
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])
Interface.check_implementation(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]}).`)
throw new Error(`Option '${name}' of initialize of module ${this.__name} is not a '${value}' (${typeof options[name]}).`)
}
this.#initialized = true
this.initialized = true
}
}

View file

@ -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,7 +19,7 @@
import { Module } from "./common.mjs"
import { Parser } from "../lib/expr-eval/parser.mjs"
const EVAL_VARIABLES = {
const evalVariables = {
// Variables not provided by expr-eval.js, needs to be provided manually
"pi": Math.PI,
"PI": Math.PI,
@ -35,17 +35,15 @@ const EVAL_VARIABLES = {
}
class ExprParserAPI extends Module {
#parser = new Parser()
constructor() {
super("ExprParser")
this.currentVars = {}
this.#parser = new Parser()
this._parser = new Parser()
this.#parser.consts = Object.assign({}, this.#parser.consts, EVAL_VARIABLES)
this._parser.consts = Object.assign({}, this._parser.consts, evalVariables)
this.#parser.functions.integral = this.integral.bind(this)
this.#parser.functions.derivative = this.derivative.bind(this)
this._parser.functions.integral = this.integral.bind(this)
this._parser.functions.derivative = this.derivative.bind(this)
}
/**
@ -70,7 +68,7 @@ class ExprParserAPI extends Module {
[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)
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
@ -81,14 +79,14 @@ class ExprParserAPI extends Module {
* @returns {ExprEvalExpression}
*/
parse(expression) {
return this.#parser.parse(expression)
return this._parser.parse(expression)
}
integral(a = null, b = null, ...args) {
integral(a, b, ...args) {
let usage1 = qsTranslate("usage", "integral(<from: number>, <to: number>, <f: ExecutableObject>)")
let usage2 = qsTranslate("usage", "integral(<from: number>, <to: number>, <f: string>, <variable: string>)")
let f = this.parseArgumentsForFunction(args, usage1, usage2)
if(typeof a !== "number" || typeof b !== "number")
if(a == null || b == null)
throw EvalError(qsTranslate("usage", "Usage:\n%1\n%2").arg(usage1).arg(usage2))
// https://en.wikipedia.org/wiki/Simpson%27s_rule
@ -101,10 +99,10 @@ class ExprParserAPI extends Module {
let usage2 = qsTranslate("usage", "derivative(<f: string>, <variable: string>, <x: number>)")
let x = args.pop()
let f = this.parseArgumentsForFunction(args, usage1, usage2)
if(typeof x !== "number")
if(x == null)
throw EvalError(qsTranslate("usage", "Usage:\n%1\n%2").arg(usage1).arg(usage2))
let derivative_precision = 1e-8
let derivative_precision = x / 10
return (f(x + derivative_precision / 2) - f(x - derivative_precision / 2)) / derivative_precision
}
}

View file

@ -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,164 +17,60 @@
*/
import { Module } from "./common.mjs"
import { HelperInterface, NUMBER, STRING } from "./interface.mjs"
import { BaseEvent } from "../events.mjs"
import { Action, Actions } from "../history/index.mjs"
import { HistoryInterface, NUMBER, STRING } from "./interface.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,
historyObj: HistoryInterface,
themeTextColor: STRING,
imageDepth: NUMBER,
fontSize: NUMBER
})
// History QML object
/** @type {Action[]} */
this.undoStack = []
/** @type {Action[]} */
this.redoStack = []
this.history = null
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
initialize({ historyObj, themeTextColor, imageDepth, fontSize }) {
super.initialize({ historyObj, themeTextColor, imageDepth, fontSize })
this.history = historyObj
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))
}
this.history.undo()
}
/**
* 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))
}
this.history.redo()
}
/**
* 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())
this.history.clear()
}
/**
* 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))
}
this.history.addToHistory(action)
}
/**
* Unserializes both the undo stack and redo stack from serialized content.
* @param {[string, any[]][]} undoSt
* @param {[string, any[]][]} redoSt
*/
unserialize(undoSt, redoSt) {
unserialize(...data) {
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())
this.history.unserialize(...data)
}
/**
* 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]
return this.history.serialize()
}
}

View file

@ -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,6 @@
*/
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"
@ -27,7 +26,6 @@ import Preferences from "./preferences.mjs"
export default {
Objects,
Settings,
ExprParser,
Latex,
History,

View file

@ -1,9 +1,9 @@
/**
/*!
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
*
* @author Ad5001 <mail@ad5001.eu>
* @license GPL-3.0-or-later
* @copyright (C) 2021-2025 Ad5001
* @copyright (C) 2021-2024 Ad5001
* @preserve
*
* This program is free software: you can redistribute it and/or modify
@ -35,8 +35,9 @@ export class Interface {
* Throws an error if the implementation does not conform to the interface.
* @param {typeof Interface} interface_
* @param {object} classToCheck
* @return {boolean}
*/
static checkImplementation(interface_, classToCheck) {
static check_implementation(interface_, classToCheck) {
const properties = new interface_()
const interfaceName = interface_.name
const toCheckName = classToCheck.constructor.name
@ -51,7 +52,7 @@ export class Interface {
else if((typeof value) === "object")
// Test type of object.
if(value instanceof Interface)
Interface.checkImplementation(value, classToCheck[property])
Interface.check_implementation(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}'.`)
}
@ -59,13 +60,32 @@ export class Interface {
}
export class CanvasInterface extends Interface {
export class SettingsInterface extends Interface {
width = NUMBER
height = NUMBER
xmin = NUMBER
ymax = NUMBER
xzoom = NUMBER
yzoom = NUMBER
xaxisstep = STRING
yaxisstep = STRING
xlabel = STRING
ylabel = STRING
linewidth = NUMBER
textsize = NUMBER
logscalex = BOOLEAN
showxgrad = BOOLEAN
showygrad = BOOLEAN
}
export class CanvasInterface extends SettingsInterface {
imageLoaders = OBJECT
/** @type {function(string): CanvasRenderingContext2D} */
getContext = FUNCTION
/** @type {function(rect)} */
markDirty = FUNCTION
/** @type {function(string): Promise} */
loadImageAsync = FUNCTION
/** @type {function(string)} */
loadImage = FUNCTION
/** @type {function(string)} */
isImageLoading = FUNCTION
/** @type {function(string)} */
@ -77,28 +97,30 @@ export class CanvasInterface extends Interface {
export class RootInterface extends Interface {
width = NUMBER
height = NUMBER
updateObjectsLists = FUNCTION
}
export class DialogInterface extends Interface {
show = FUNCTION
}
export class HistoryInterface extends Interface {
undo = FUNCTION
redo = FUNCTION
clear = FUNCTION
addToHistory = FUNCTION
unserialize = FUNCTION
serialize = 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<string>} - Comma separated data of the image (source, width, height)
*/
renderAsync = FUNCTION
render = FUNCTION
/**
* @param {string} markup - LaTeX markup to render
* @param {number} fontSize - Font size (in pt) to render
@ -117,13 +139,37 @@ 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
* @returns {boolean} Value of the setting
*/
getSettingBool = FUNCTION
/**
* Gets a setting from the config
* @param {string} settingName - Setting (and its dot-separated namespace) to get (e.g. "default_graph.xmin")
* @returns {number} Value of the setting
*/
getSettingInt = FUNCTION
/**
* Gets a setting from the config
* @param {string} settingName - Setting (and its dot-separated namespace) to get (e.g. "default_graph.xmin")
* @returns {string} 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
* @param {boolean} value
*/
setSettingBool = FUNCTION
/**
* Sets a setting in the config
* @param {string} settingName - Setting (and its dot-separated namespace) to set (e.g. "default_graph.xmin")
* @param {number} value
*/
setSettingInt = 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} value
*/
setSetting = FUNCTION
/**

View file

@ -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,69 +19,36 @@
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"
import Canvas from "./canvas.mjs"
import { DialogInterface, RootInterface, SettingsInterface } from "./interface.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
root: RootInterface,
settings: SettingsInterface
})
// 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}
* Path of the currently opened file. Empty if no file is opened.
* @type {string}
*/
get saved() { return this.#saved }
this.saveFileName = ""
}
/**
* Initializes module with QML elements.
* @param {RootInterface} root
* @param {SettingsInterface} settings
* @param {{show: function(string)}} alert
*/
initialize({ root, alert }) {
super.initialize({ root, alert })
this.#rootElement = root
this.#alert = alert
initialize({ root, settings, alert }) {
super.initialize({ root, settings, alert })
this.rootElement = root
this.settings = settings
this.alert = alert
}
/**
@ -93,7 +60,7 @@ class IOAPI extends Module {
// Add extension if necessary
if(["lpf"].indexOf(filename.split(".")[filename.split(".").length - 1]) === -1)
filename += ".lpf"
Settings.set("saveFilename", filename, false)
this.saveFilename = filename
let objs = {}
for(let objType in Objects.currentObjects) {
objs[objType] = []
@ -102,29 +69,28 @@ class IOAPI extends Module {
}
}
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,
"xzoom": this.settings.xzoom,
"yzoom": this.settings.yzoom,
"xmin": this.settings.xmin,
"ymax": this.settings.ymax,
"xaxisstep": this.settings.xaxisstep,
"yaxisstep": this.settings.yaxisstep,
"xaxislabel": this.settings.xlabel,
"yaxislabel": this.settings.ylabel,
"logscalex": this.settings.logscalex,
"linewidth": this.settings.linewidth,
"showxgrad": this.settings.showxgrad,
"showygrad": this.settings.showygrad,
"textsize": this.settings.textsize,
"history": History.serialize(),
"width": this.#rootElement.width,
"height": this.#rootElement.height,
"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())
this.alert.show(qsTranslate("io", "Saved plot to '%1'.").arg(filename.split("/").pop()))
History.history.saved = true
}
/**
@ -135,32 +101,32 @@ class IOAPI extends Module {
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))
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)
this.settings.saveFilename = filename
this.settings.xzoom = parseFloat(data["xzoom"]) || 100
this.settings.yzoom = parseFloat(data["yzoom"]) || 10
this.settings.xmin = parseFloat(data["xmin"]) || 5 / 10
this.settings.ymax = parseFloat(data["ymax"]) || 24
this.settings.xaxisstep = data["xaxisstep"] || "4"
this.settings.yaxisstep = data["yaxisstep"] || "4"
this.settings.xlabel = data["xaxislabel"] || ""
this.settings.ylabel = data["yaxislabel"] || ""
this.settings.logscalex = data["logscalex"] === true
if("showxgrad" in data)
Settings.set("showxgrad", data["showxgrad"], false)
this.settings.showxgrad = data["showxgrad"]
if("showygrad" in data)
Settings.set("showygrad", data["showygrad"], false)
this.settings.textsize = data["showygrad"]
if("linewidth" in data)
Settings.set("linewidth", data["linewidth"], false)
this.settings.linewidth = data["linewidth"]
if("textsize" in data)
Settings.set("textsize", data["textsize"], false)
this.#rootElement.height = parseFloat(data["height"]) || 500
this.#rootElement.width = parseFloat(data["width"]) || 1000
this.settings.textsize = data["textsize"]
this.rootElement.height = parseFloat(data["height"]) || 500
this.rootElement.width = parseFloat(data["width"]) || 1000
// Importing objects
Objects.currentObjects = {}
@ -191,18 +157,20 @@ class IOAPI extends Module {
if("history" in data)
History.unserialize(...data["history"])
// Refreshing sidebar
this.rootElement.updateObjectsLists()
} else {
error = qsTranslate("io", "Invalid file provided.")
}
if(error !== "") {
console.log(error)
this.#alert.show(qsTranslate("io", "Could not load file: ") + 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())
Canvas.redraw()
this.alert.show(qsTranslate("io", "Loaded file '%1'.").arg(basename))
History.history.saved = true
}
}

View file

@ -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,12 +17,11 @@
*/
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 unicodechars = [
"α", "β", "γ", "δ", "ε", "ζ", "η",
"π", "θ", "κ", "λ", "μ", "ξ", "ρ",
"ς", "σ", "τ", "φ", "χ", "ψ", "ω",
@ -31,9 +30,9 @@ const unicodechars = ["pi", "∞",
"ₕ", "ₖ", "ₗ", "ₘ", "ₙ", "ₚ", "ₛ",
"ₜ", "¹", "²", "³", "⁴", "⁵", "⁶",
"⁷", "⁸", "⁹", "⁰", "₁", "₂", "₃",
"₄", "₅", "₆", "₇", "₈", "₉", "₀"
]
const equivalchars = ["\\pi", "\\infty",
"₄", "₅", "₆", "₇", "₈", "₉", "₀",
"pi", "∞"]
const equivalchars = [
"\\alpha", "\\beta", "\\gamma", "\\delta", "\\epsilon", "\\zeta", "\\eta",
"\\pi", "\\theta", "\\kappa", "\\lambda", "\\mu", "\\xi", "\\rho",
"\\sigma", "\\sigma", "\\tau", "\\phi", "\\chi", "\\psi", "\\omega",
@ -43,29 +42,7 @@ const equivalchars = ["\\pi", "\\infty",
"{}_{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
}
}
"\\pi", "\\infty"]
/**
* Class containing the result of a LaTeX render.
@ -83,11 +60,6 @@ class LatexRenderResult {
}
class LatexAPI extends Module {
static emits = ["async-render-started", "async-render-finished"]
/** @type {LatexInterface} */
#latex = null
constructor() {
super("Latex", {
latex: LatexInterface,
@ -97,7 +69,6 @@ class LatexAPI extends Module {
* true if latex has been enabled by the user, false otherwise.
*/
this.enabled = false
this.promises = new Set()
}
/**
@ -106,8 +77,9 @@ class LatexAPI extends Module {
*/
initialize({ latex, helper }) {
super.initialize({ latex, helper })
this.#latex = latex
this.enabled = helper.getSetting("enable_latex")
this.latex = latex
this.helper = helper
this.enabled = helper.getSettingBool("enable_latex")
}
/**
@ -121,7 +93,7 @@ class LatexAPI extends Module {
*/
findPrerendered(markup, fontSize, color) {
if(!this.initialized) throw new Error("Attempting findPrerendered before initialize!")
const data = this.#latex.findPrerendered(markup, fontSize, color)
const data = this.latex.findPrerendered(markup, fontSize, color)
let ret = null
if(data !== "")
ret = new LatexRenderResult(...data.split(","))
@ -138,19 +110,7 @@ class LatexAPI extends Module {
*/
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(",")
let args = this.latex.render(markup, fontSize, color).split(",")
return new LatexRenderResult(...args)
}
@ -175,10 +135,9 @@ class LatexAPI extends Module {
*/
parif(elem, contents) {
elem = elem.toString()
const contains = contents.some(x => elem.indexOf(x) > 0)
if(contains && (elem[0] !== "(" || elem.at(-1) !== ")"))
if(elem[0] !== "(" && elem[elem.length - 1] !== ")" && contents.some(x => elem.indexOf(x) > 0))
return this.par(elem)
if(!contains && elem[0] === "(" && elem.at(-1) === ")")
if(elem[0] === "(" && elem[elem.length - 1] === ")")
return elem.removeEnclosure()
return elem
}
@ -196,21 +155,20 @@ class LatexAPI extends Module {
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]})`
return `\\frac{d${args[0]}}{dx}(x)`
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}}`
return `\\sqrt\\left(${args.join(", ")}\\right)`
case "abs":
return `\\left|${args.join(", ")}\\right|`
case "floor":
return `\\left\\lfloor{${args.join(", ")}}\\right\\rfloor`
return `\\left\\lfloor${args.join(", ")}\\right\\rfloor`
case "ceil":
return `\\left\\lceil{${args.join(", ")}}\\right\\rceil`
return `\\left\\lceil${args.join(", ")}\\right\\rceil`
default:
return `\\mathrm{${f}}\\left(${args.join(", ")}\\right)`
}
@ -224,17 +182,16 @@ class LatexAPI extends Module {
* @returns {string}
*/
variable(vari, wrapIn$ = false) {
if(wrapIn$) {
if(wrapIn$)
for(let i = 0; i < unicodechars.length; i++) {
if(vari.includes(unicodechars[i]))
vari = vari.replaceAll(unicodechars[i], "$" + equivalchars[i] + "$")
}
} else {
else
for(let i = 0; i < unicodechars.length; i++) {
if(vari.includes(unicodechars[i]))
vari = vari.replaceAll(unicodechars[i], equivalchars[i])
}
}
return vari
}
@ -302,7 +259,7 @@ class LatexAPI extends Module {
throw new EvalError("Unknown operator " + item.value + ".")
}
break
case Instruction.IOP3: // Ternary operator
case Instruction.IOP3: // Thirdiary operator
n3 = nstack.pop()
n2 = nstack.pop()
n1 = nstack.pop()
@ -329,7 +286,7 @@ class LatexAPI extends Module {
nstack.push(this.parif(n1, ["+", "-", "*", "/", "^"]) + "!")
break
default:
nstack.push(this.functionToLatex(f, [this.parif(n1, ["+", "-", "*", "/", "^"])]))
nstack.push(f + this.parif(n1, ["+", "-", "*", "/", "^"]))
break
}
break
@ -364,6 +321,9 @@ class LatexAPI extends Module {
throw new EvalError("invalid Expression")
}
}
if(nstack.length > 1) {
nstack = [nstack.join(";")]
}
return String(nstack[0])
}
}

View file

@ -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,17 +17,13 @@
*/
import { Module } from "./common.mjs"
import { textsub } from "../utils/index.mjs"
import { textsub } from "../utils.mjs"
class ObjectsAPI extends Module {
constructor() {
super("Objects")
/**
* List of object constructors.
* @type {Object.<string,typeof DrawableObject>}
*/
this.types = {}
/**
* List of objects for each type of object.
@ -69,7 +65,7 @@ class ObjectsAPI extends Module {
* @param {string} newName - Name to rename the object to.
*/
renameObject(oldName, newName) {
const obj = this.currentObjectsByName[oldName]
let obj = this.currentObjectsByName[oldName]
delete this.currentObjectsByName[oldName]
this.currentObjectsByName[newName] = obj
obj.name = newName
@ -80,7 +76,7 @@ class ObjectsAPI extends Module {
* @param {string} objName - Current name of the object.
*/
deleteObject(objName) {
const obj = this.currentObjectsByName[objName]
let obj = this.currentObjectsByName[objName]
if(obj !== undefined) {
this.currentObjects[obj.type].splice(this.currentObjects[obj.type].indexOf(obj), 1)
obj.delete()

View file

@ -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,9 +20,6 @@ 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")

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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<string, string|number|boolean>} */
#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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
@ -63,7 +63,7 @@ export default class BodePhase extends ExecutableObject {
// 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()))
History.history.addToHistory(new CreateNewObject(om_0.name, "Point", om_0.export()))
labelPosition = "below"
}
om_0.requiredBy.push(this)

View file

@ -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

View file

@ -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,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { getRandomColor } from "../utils.mjs"
import Objects from "../module/objects.mjs"
import Latex from "../module/latex.mjs"
import { getRandomColor } from "../utils/index.mjs"
import { ensureTypeSafety, serializesByPropertyType } from "../parameters.mjs"
// This file contains the default data to be imported from all other objects
@ -224,7 +224,7 @@ export class DrawableObject {
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()

View file

@ -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

Some files were not shown because too many files have changed in this diff Show more