Compare commits
No commits in common. "master" and "rollup-js" have entirely different histories.
6
.gitignore
vendored
|
@ -37,10 +37,8 @@ docs/html
|
||||||
*.lpf
|
*.lpf
|
||||||
*.lgg
|
*.lgg
|
||||||
|
|
||||||
# Tests
|
|
||||||
common/coverage/
|
|
||||||
**/.coverage
|
|
||||||
|
|
||||||
# npm
|
# npm
|
||||||
common/node_modules
|
common/node_modules
|
||||||
|
common/coverage/
|
||||||
|
common/.coverage
|
||||||
runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/index.mjs*
|
runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/index.mjs*
|
||||||
|
|
20
README.md
|
@ -1,4 +1,4 @@
|
||||||
#  LogarithmPlotter
|
#  LogarithmPlotter
|
||||||
|
|
||||||
[](https://ci.ad5001.eu/Ad5001/LogarithmPlotter)
|
[](https://ci.ad5001.eu/Ad5001/LogarithmPlotter)
|
||||||
[](https://hosted.weblate.org/engage/logarithmplotter/)
|
[](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`).
|
- [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
|
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
|
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`.
|
`python3 build/runtime-pyside6/LogarithmPlotter/logarithmplotter.py`.
|
||||||
|
@ -68,19 +68,13 @@ To run LogarithmPlotter's tests, follow these steps:
|
||||||
|
|
||||||
- Python
|
- Python
|
||||||
- Install python3 and [poetry](https://python-poetry.org/)
|
- Install python3 and [poetry](https://python-poetry.org/)
|
||||||
- Create and activate virtual env (recommended)
|
- Run `poetry install --with test`
|
||||||
- Go into `runtime-pyside6` and run `poetry install --with test`
|
|
||||||
- ECMAScript
|
|
||||||
- Install node with npm
|
|
||||||
- Go into `common` and run `npm install -D`
|
|
||||||
|
|
||||||
Finally, to actually run the tests:
|
|
||||||
- Run `scripts/run-tests.sh`
|
- Run `scripts/run-tests.sh`
|
||||||
|
|
||||||
## Legal notice
|
## Legal notice
|
||||||
|
|
||||||
LogarithmPlotter - 2D plotter software to make Bode plots, sequences and repartition functions.
|
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
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -95,8 +89,8 @@ Finally, to actually run the tests:
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
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
|
Language files translations located at LogarithmPlotter/i18n are licensed under GNU GPL3.0+ and are copyrighted by their
|
||||||
are copyrighted by their original authors:
|
original authors. See LICENSE.md for more details:
|
||||||
|
|
||||||
- 🇭🇺 Hungarian translation by [Óvári](https://github.com/ovari)
|
- 🇭🇺 Hungarian translation by [Óvári](https://github.com/ovari)
|
||||||
- 🇳🇴 Norwegian translation by [Allan Nordhøy](https://github.com/comradekingu)
|
- 🇳🇴 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
|
||||||
<r@undefined.ch>, ported to javascript by Matthew Crumley
|
<r@undefined.ch>, ported to javascript by Matthew Crumley
|
||||||
<email@matthewcrumley.com> (http://silentmatt.com/), and then to QMLJS by Ad5001.
|
<email@matthewcrumley.com> (http://silentmatt.com/), and then to QMLJS by Ad5001.
|
||||||
|
|
||||||
All files in (common/src/lib/expr-eval/) except integration.mjs are licensed
|
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).
|
under the [MIT License](https://raw.githubusercontent.com/silentmatt/expr-eval/master/LICENSE.txt).
|
||||||
|
|
1597
assets/i18n/lp_ta.ts
|
@ -1,2 +1,2 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
pyside6-lrelease *.ts
|
lrelease *.ts
|
||||||
|
|
|
@ -21,7 +21,7 @@ replace() {
|
||||||
|
|
||||||
rm ../qml/eu/ad5001/LogarithmPlotter/js/index.mjs # Remove index which should not be scanned
|
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
|
for file in $files; do
|
||||||
echo "Moving '$file' to '${file%.*}.js'..."
|
echo "Moving '$file' to '${file%.*}.js'..."
|
||||||
mv "$file" "${file%.*}.js"
|
mv "$file" "${file%.*}.js"
|
||||||
|
@ -33,14 +33,12 @@ for file in $files; do
|
||||||
replace "${file%.*}.js" "^export" "/*export*/"
|
replace "${file%.*}.js" "^export" "/*export*/"
|
||||||
replace "${file%.*}.js" "async " "/*async */"
|
replace "${file%.*}.js" "async " "/*async */"
|
||||||
replace "${file%.*}.js" "await" "/*await */"
|
replace "${file%.*}.js" "await" "/*await */"
|
||||||
replace "${file%.*}.js" " #" "// #"
|
|
||||||
replace "${file%.*}.js" "this.#" "/*this.#*/"
|
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "----------------------------"
|
echo "----------------------------"
|
||||||
echo "| Updating translations... |"
|
echo "| Updating translations... |"
|
||||||
echo "----------------------------"
|
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
|
# Updating locations in files
|
||||||
for lp in *.ts; do
|
for lp in *.ts; do
|
||||||
echo "Replacing locations in $lp..."
|
echo "Replacing locations in $lp..."
|
||||||
|
@ -57,9 +55,7 @@ for file in $files; do
|
||||||
replace "$file" "/*async */" "async "
|
replace "$file" "/*async */" "async "
|
||||||
replace "$file" "^/*export*/" "export"
|
replace "$file" "^/*export*/" "export"
|
||||||
replace "$file" "^/*export default*/" "export default"
|
replace "$file" "^/*export default*/" "export default"
|
||||||
replace "$file" '.mjs"*/' '.mjs"'
|
|
||||||
replace "$file" "^/*import" "import"
|
replace "$file" "^/*import" "import"
|
||||||
replace "$file" "^/*export" "export"
|
replace "$file" "^/*export" "export"
|
||||||
replace "$file" "// #" " #"
|
replace "$file" '.mjs"*/$' '.mjs"'
|
||||||
replace "$file" "/*this.#*/" "this.#"
|
|
||||||
done
|
done
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
../common/appearance.svg
|
|
|
@ -1 +0,0 @@
|
||||||
../common/appearance.svg
|
|
|
@ -1 +0,0 @@
|
||||||
../common/arrow.svg
|
|
|
@ -1 +0,0 @@
|
||||||
../common/position.svg
|
|
|
@ -1 +0,0 @@
|
||||||
../common/angle.svg
|
|
|
@ -1 +0,0 @@
|
||||||
../common/angle.svg
|
|
|
@ -1 +0,0 @@
|
||||||
../common/appearance.svg
|
|
|
@ -1 +0,0 @@
|
||||||
../common/target.svg
|
|
|
@ -1 +0,0 @@
|
||||||
../common/position.svg
|
|
|
@ -1 +0,0 @@
|
||||||
../common/label.svg
|
|
|
@ -1 +0,0 @@
|
||||||
../common/angle.svg
|
|
|
@ -1 +0,0 @@
|
||||||
../common/position.svg
|
|
|
@ -1 +0,0 @@
|
||||||
../common/position.svg
|
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
1
assets/icons/settings/custom/Display Mode.svg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../common/appearance.svg
|
1
assets/icons/settings/custom/Display Style.svg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../common/appearance.svg
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
1
assets/icons/settings/custom/Label Position.svg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../common/arrow.svg
|
1
assets/icons/settings/custom/Label X.svg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../common/position.svg
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
1
assets/icons/settings/custom/Phase.svg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../common/angle.svg
|
1
assets/icons/settings/custom/Point Style.svg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../common/appearance.svg
|
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
1
assets/icons/settings/custom/Target Element.svg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../common/target.svg
|
1
assets/icons/settings/custom/Target Value Position.svg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../common/position.svg
|
1
assets/icons/settings/custom/Text.svg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../common/label.svg
|
1
assets/icons/settings/custom/Unit.svg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../common/angle.svg
|
1
assets/icons/settings/custom/X.svg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../common/position.svg
|
1
assets/icons/settings/custom/Y.svg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../common/position.svg
|
1
assets/icons/settings/custom/ω_0.svg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../common/angle.svg
|
|
@ -1,171 +1,64 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
width="48.0px"
|
width="24.0px"
|
||||||
height="48.0px"
|
height="24.0px"
|
||||||
viewBox="0 0 48.0 48.0"
|
viewBox="0 0 24.0 24.0"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="SVGRoot"
|
id="SVGRoot"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xml:space="preserve"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:svg="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:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
xmlns:dc="http://purl.org/dc/elements/1.1/"><title
|
||||||
<title
|
id="title836">LogarithmPlotter Icon v1.0</title><defs
|
||||||
id="title38896">LogarithmPlotter Icon</title>
|
id="defs833" /><metadata
|
||||||
<defs
|
id="metadata836"><rdf:RDF><cc:Work
|
||||||
id="defs2254">
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
<linearGradient
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title>LogarithmPlotter Icon v1.0</dc:title><cc:license
|
||||||
id="linearGradient27593">
|
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
|
||||||
<stop
|
rdf:about="http://creativecommons.org/licenses/by-nc-sa/4.0/"><cc:permits
|
||||||
style="stop-color:#000000;stop-opacity:0.15000001;"
|
rdf:resource="http://creativecommons.org/ns#Reproduction" /><cc:permits
|
||||||
offset="0"
|
rdf:resource="http://creativecommons.org/ns#Distribution" /><cc:requires
|
||||||
id="stop27589" />
|
rdf:resource="http://creativecommons.org/ns#Notice" /><cc:requires
|
||||||
<stop
|
rdf:resource="http://creativecommons.org/ns#Attribution" /><cc:prohibits
|
||||||
style="stop-color:#000000;stop-opacity:0;"
|
rdf:resource="http://creativecommons.org/ns#CommercialUse" /><cc:permits
|
||||||
offset="1"
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /><cc:requires
|
||||||
id="stop27591" />
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" /></cc:License></rdf:RDF></metadata><g
|
||||||
</linearGradient>
|
id="layer2"
|
||||||
<linearGradient
|
transform="matrix(1,0,0,0.94444444,0,1.1666667)"
|
||||||
id="linearGradient13467">
|
style="fill:#666666"><rect
|
||||||
<stop
|
style="fill:#666666;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
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"
|
|
||||||
id="rect1546"
|
id="rect1546"
|
||||||
width="18"
|
width="18"
|
||||||
height="18.105883"
|
height="18"
|
||||||
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"
|
|
||||||
x="3"
|
x="3"
|
||||||
y="3"
|
y="3"
|
||||||
ry="2.3212669"
|
ry="2.25" /></g><g
|
||||||
rx="2.2499998"
|
id="layer2-6"
|
||||||
transform="matrix(2.2222222,0,0,2.1539961,-2.6666667,-2.4619883)" />
|
transform="matrix(1,0,0,0.94444444,0,0.16666668)"
|
||||||
</g>
|
style="fill:#f9f9f9"><rect
|
||||||
<g
|
style="fill:#f9f9f9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
id="layer3"
|
id="rect1546-7"
|
||||||
style="fill:#0000ff">
|
width="18"
|
||||||
<path
|
height="18"
|
||||||
id="path27475"
|
x="3"
|
||||||
style="fill:url(#linearGradient27595);fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
y="3"
|
||||||
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" />
|
ry="2.25" /></g><g
|
||||||
</g>
|
id="layer1"
|
||||||
<g
|
style="stroke-width:2;stroke-dasharray:none"><rect
|
||||||
id="layer1-6"
|
|
||||||
style="stroke-width:2;stroke-dasharray:none"
|
|
||||||
transform="matrix(2,0,0,2,0,1)">
|
|
||||||
<rect
|
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:1.86898;stroke-dasharray:none;stroke-opacity:0"
|
style="fill:#000000;fill-rule:evenodd;stroke-width:1.86898;stroke-dasharray:none;stroke-opacity:0"
|
||||||
id="rect1410"
|
id="rect1410"
|
||||||
width="14"
|
width="14"
|
||||||
height="2"
|
height="2"
|
||||||
x="5"
|
x="5"
|
||||||
y="15.5" />
|
y="15.5" /><rect
|
||||||
<rect
|
style="fill:#000000;fill-rule:evenodd;stroke-width:2;stroke-dasharray:none;stroke-opacity:0"
|
||||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2.06559;stroke-dasharray:none;stroke-opacity:0"
|
|
||||||
id="rect1412"
|
id="rect1412"
|
||||||
width="2"
|
width="2"
|
||||||
height="16"
|
height="15"
|
||||||
x="8"
|
x="9"
|
||||||
y="3.5" />
|
y="3.9768662" /><path
|
||||||
<path
|
|
||||||
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
id="path1529"
|
id="path1529"
|
||||||
d="m 18,3.5 c 0,7 -4,12 -13,12" />
|
d="M 18,4 C 18,10.017307 13.40948,15.5 5,15.5" /></g></svg>
|
||||||
</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 <mail@ad5001.eu></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>
|
|
||||||
|
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 3.1 KiB |
|
@ -3,7 +3,7 @@ Source: logarithmplotter
|
||||||
Version: 0.6.0
|
Version: 0.6.0
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Maintainer: Ad5001 <mail@ad5001.eu>
|
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
|
Build-Depends: debhelper (>=11~), dh-python, dpkg-dev (>= 1.16.1~), python-setuptools
|
||||||
Section: science
|
Section: science
|
|
@ -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
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Copyright 2025 Ad5001 -->
|
<!-- Copyright 2024 Ad5001 -->
|
||||||
<component type="desktop-application">
|
<component type="desktop-application">
|
||||||
<id>eu.ad5001.LogarithmPlotter</id>
|
<id>eu.ad5001.LogarithmPlotter</id>
|
||||||
<launchable type="desktop-id">logarithmplotter.desktop</launchable>
|
<launchable type="desktop-id">logarithmplotter.desktop</launchable>
|
||||||
|
@ -66,54 +66,50 @@
|
||||||
<categories>
|
<categories>
|
||||||
<category>Science</category>
|
<category>Science</category>
|
||||||
<category>Education</category>
|
<category>Education</category>
|
||||||
|
<category>Qt</category>
|
||||||
</categories>
|
</categories>
|
||||||
|
|
||||||
<url type="homepage">https://apps.ad5001.eu/logarithmplotter/</url>
|
<url type="homepage">https://apps.ad5001.eu/logarithmplotter/</url>
|
||||||
<url type="bugtracker">https://git.ad5001.eu/Ad5001/LogarithmPlotter/issues/</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="help">https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/</url>
|
||||||
<url type="translate">https://hosted.weblate.org/engage/logarithmplotter/</url>
|
<url type="translate">https://hosted.weblate.org/engage/logarithmplotter/</url>
|
||||||
|
|
||||||
<screenshots>
|
<screenshots>
|
||||||
<screenshot type="default">
|
<screenshot type="default">
|
||||||
<image>https://apps.ad5001.eu/img/en/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.6</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.6</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.6</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.6</image>
|
<image xml:lang="no">https://apps.ad5001.eu/img/no/logarithmplotter/gain.png?v=0.5</image>
|
||||||
<image xml:lang="es">https://apps.ad5001.eu/img/es/logarithmplotter/gain.png?v=0.6</image>
|
|
||||||
<caption>Main view of LogarithmPlotter showing an asymptotic Bode magnitude plot.</caption>
|
<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="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="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="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="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>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image>https://apps.ad5001.eu/img/en/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.6</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.6</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.6</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.6</image>
|
<image xml:lang="no">https://apps.ad5001.eu/img/no/logarithmplotter/phase.png?v=0.5</image>
|
||||||
<image xml:lang="es">https://apps.ad5001.eu/img/es/logarithmplotter/phase.png?v=0.6</image>
|
|
||||||
<caption>Main view of LogarithmPlotter showing an asymptotic Bode phase plot.</caption>
|
<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="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="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="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="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>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image>https://apps.ad5001.eu/img/en/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.6</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.6</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.6</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.6</image>
|
<image xml:lang="no">https://apps.ad5001.eu/img/no/logarithmplotter/welcome.png?v=0.5</image>
|
||||||
<image xml:lang="es">https://apps.ad5001.eu/img/es/logarithmplotter/welcome.png?v=0.6</image>
|
|
||||||
<caption>LogarithmPlotter's welcome page.</caption>
|
<caption>LogarithmPlotter's welcome page.</caption>
|
||||||
<caption xml:lang="de">LogarithmPlotter's Willkommensseite.</caption>
|
<caption xml:lang="de">LogarithmPlotter's Willkommensseite.</caption>
|
||||||
<caption xml:lang="fr">Page d'accueil de LogarithmPlotter.</caption>
|
<caption xml:lang="fr">Page d'accueil de LogarithmPlotter.</caption>
|
||||||
<caption xml:lang="hu">LogarithmPlotter üdvözlő oldala.</caption>
|
<caption xml:lang="hu">LogarithmPlotter üdvözlő oldala.</caption>
|
||||||
<caption xml:lang="no">LogarithmPlotters velkomstside.</caption>
|
<caption xml:lang="no">LogarithmPlotters velkomstside.</caption>
|
||||||
<caption xml:lang="es">Página de bienvenida de LogarithmPlotter.</caption>
|
|
||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
<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">
|
<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>
|
<comment xml:lang="fr">Fichier Graphe Logarithmique</comment>
|
||||||
<icon name="application-x-logarithm-plot"/>
|
<icon name="application-x-logarithm-plot"/>
|
||||||
<glob-deleteall/>
|
<glob-deleteall/>
|
||||||
|
|
|
@ -13,7 +13,7 @@ Unicode True
|
||||||
!define WEBSITE "https://apps.ad5001.eu/logarithmplotter"
|
!define WEBSITE "https://apps.ad5001.eu/logarithmplotter"
|
||||||
!define VERSION_SHORT "0.6.0"
|
!define VERSION_SHORT "0.6.0"
|
||||||
!define APP_VERSION "${VERSION_SHORT}.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 DESCRIPTION "Create graphs with logarithmic scales."
|
||||||
|
|
||||||
!define REG_UNINSTALL "Software\Microsoft\Windows\CurrentVersion\Uninstall\LogarithmPlotter"
|
!define REG_UNINSTALL "Software\Microsoft\Windows\CurrentVersion\Uninstall\LogarithmPlotter"
|
||||||
|
|
|
@ -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"}
|
|
17
ci/drone.yml
|
@ -12,29 +12,32 @@ steps:
|
||||||
- git submodule update --init --recursive
|
- git submodule update --init --recursive
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
image: ad5001/ubuntu-pyside-xvfb:linux-6-latest-latex-node
|
image: node:18-bookworm
|
||||||
commands:
|
commands:
|
||||||
- cd common && npm install && cd ..
|
- cd common && npm install && cd ..
|
||||||
|
- apt update
|
||||||
|
- apt install -y qtchooser qttools5-dev-tools
|
||||||
|
# Start building
|
||||||
- bash scripts/build.sh
|
- bash scripts/build.sh
|
||||||
|
when:
|
||||||
|
event: [ push, tag ]
|
||||||
|
|
||||||
- name: Unit Tests
|
- name: Unit Tests
|
||||||
image: ad5001/ubuntu-pyside-xvfb:linux-6-latest-latex-node
|
image: ad5001/ubuntu-pyside-xvfb:linux-6-latest-latex
|
||||||
commands:
|
commands:
|
||||||
|
- apt update
|
||||||
|
- apt install -y npm
|
||||||
- cd common && npm install -D && cd ..
|
- cd common && npm install -D && cd ..
|
||||||
- xvfb-run bash scripts/run-tests.sh --no-rebuild
|
- xvfb-run bash scripts/run-tests.sh --no-rebuild
|
||||||
when:
|
when:
|
||||||
event: [ push, tag ]
|
event: [ push, tag ]
|
||||||
|
|
||||||
- name: File Tests
|
- name: File Tests
|
||||||
image: ad5001/ubuntu-pyside-xvfb:linux-6-latest-latex-node
|
image: ad5001/ubuntu-pyside-xvfb:linux-6-latest-latex
|
||||||
commands:
|
commands:
|
||||||
- xvfb-run python3 run.py --test-build --no-check-for-updates
|
- 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/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/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:
|
when:
|
||||||
event: [ push, tag ]
|
event: [ push, tag ]
|
||||||
|
|
||||||
|
|
|
@ -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"}
|
|
1539
common/package-lock.json
generated
|
@ -2,7 +2,7 @@
|
||||||
"name": "logarithmplotter",
|
"name": "logarithmplotter",
|
||||||
"version": "0.6.0",
|
"version": "0.6.0",
|
||||||
"description": "2D plotter software to make Bode plots, sequences and distribution functions.",
|
"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": {
|
"scripts": {
|
||||||
"build": "rollup --config rollup.config.mjs",
|
"build": "rollup --config rollup.config.mjs",
|
||||||
"test": "c8 mocha test/**/*.mjs"
|
"test": "c8 mocha test/**/*.mjs"
|
||||||
|
@ -24,12 +24,9 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^5.0.0",
|
"@types/chai": "^5.0.0",
|
||||||
"@types/chai-spies": "^1.0.6",
|
|
||||||
"@types/chai-as-promised": "^8.0.1",
|
|
||||||
"@types/mocha": "^10.0.8",
|
"@types/mocha": "^10.0.8",
|
||||||
"chai": "^5.1.1",
|
"chai": "^5.1.1",
|
||||||
"chai-as-promised": "^8.0.0",
|
"chai-as-promised": "^8.0.0",
|
||||||
"chai-spies": "^1.1.0",
|
|
||||||
"esm": "^3.2.25",
|
"esm": "^3.2.25",
|
||||||
"mocha": "^10.7.3"
|
"mocha": "^10.7.3"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -22,7 +22,7 @@ import { babel } from "@rollup/plugin-babel"
|
||||||
import cleanup from "rollup-plugin-cleanup"
|
import cleanup from "rollup-plugin-cleanup"
|
||||||
|
|
||||||
const src = "./src/index.mjs"
|
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 {
|
export default {
|
||||||
input: src,
|
input: src,
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -95,15 +95,11 @@ export class Action {
|
||||||
if(!Latex.enabled)
|
if(!Latex.enabled)
|
||||||
throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.")
|
throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.")
|
||||||
const imgDepth = History.imageDepth
|
const imgDepth = History.imageDepth
|
||||||
const renderArguments = [
|
const { source, width, height } = await Latex.requestAsyncRender(
|
||||||
latexString,
|
latexString,
|
||||||
imgDepth * (History.fontSize + 2),
|
imgDepth * (History.fontSize + 2),
|
||||||
History.themeTextColor
|
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"/>`
|
return `<img src="${source}" width="${width / imgDepth}" height="${height / imgDepth}" style="vertical-align: middle"/>`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
import Objects from "../module/objects.mjs"
|
import Objects from "../module/objects.mjs"
|
||||||
import Latex from "../module/latex.mjs"
|
import Latex from "../module/latex.mjs"
|
||||||
import * as MathLib from "../math/index.mjs"
|
import * as MathLib from "../math/index.mjs"
|
||||||
import { escapeHTML } from "../utils/index.mjs"
|
import { escapeHTML } from "../utils.mjs"
|
||||||
import { Action } from "./common.mjs"
|
import { Action } from "./common.mjs"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -18,11 +18,10 @@
|
||||||
|
|
||||||
import js from "./lib/polyfills/js.mjs"
|
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"
|
import * as ObjsAutoload from "./objs/autoload.mjs"
|
||||||
|
|
||||||
export * as Modules from "./module/index.mjs"
|
|
||||||
export * as MathLib from "./math/index.mjs"
|
export * as MathLib from "./math/index.mjs"
|
||||||
export * as HistoryLib from "./history/index.mjs"
|
export * as HistoryLib from "./history/index.mjs"
|
||||||
export * as Parsing from "./parsing/index.mjs"
|
export * as Parsing from "./parsing/index.mjs"
|
||||||
|
export * as Utils from "./utils.mjs"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*
|
*
|
||||||
* Ported to QMLJS with modifications done accordingly done by Ad5001 <mail@ad5001.eu> (https://ad5001.eu)
|
* 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
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* 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.
|
* In the given instructions, replaces variable by expr.
|
||||||
* @param {Instruction[]} tokens
|
* @param {Instruction[]} tokens
|
||||||
* @param {string} variable
|
* @param {string} variable
|
||||||
* @param {ExprEvalExpression} expr
|
* @param {number} expr
|
||||||
* @return {Instruction[]}
|
* @return {Instruction[]}
|
||||||
*/
|
*/
|
||||||
function substitute(tokens, variable, expr) {
|
function substitute(tokens, variable, expr) {
|
||||||
|
@ -171,6 +171,9 @@ function evaluate(tokens, expr, values) {
|
||||||
nstack.push(n1 ? !!evaluate(n2, expr, values) : false)
|
nstack.push(n1 ? !!evaluate(n2, expr, values) : false)
|
||||||
} else if(item.value === "or") {
|
} else if(item.value === "or") {
|
||||||
nstack.push(n1 ? true : !!evaluate(n2, expr, values))
|
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 {
|
} else {
|
||||||
f = expr.binaryOps[item.value]
|
f = expr.binaryOps[item.value]
|
||||||
nstack.push(f(resolveExpression(n1, values), resolveExpression(n2, values)))
|
nstack.push(f(resolveExpression(n1, values), resolveExpression(n2, values)))
|
||||||
|
@ -487,6 +490,18 @@ export class ExprEvalExpression {
|
||||||
return evaluate(this.tokens, this, values)
|
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() {
|
toString() {
|
||||||
return expressionToString(this.tokens, false)
|
return expressionToString(this.tokens, false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*
|
*
|
||||||
* Ported to QMLJS with modifications done accordingly done by Ad5001 <mail@ad5001.eu> (https://ad5001.eu)
|
* 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
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*
|
*
|
||||||
* Ported to QMLJS with modifications done accordingly done by Ad5001 <mail@ad5001.eu> (https://ad5001.eu)
|
* 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
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
@ -47,7 +47,9 @@ const optionNameMap = {
|
||||||
"not": "logical",
|
"not": "logical",
|
||||||
"?": "conditional",
|
"?": "conditional",
|
||||||
":": "conditional",
|
":": "conditional",
|
||||||
|
//'=': 'assignment', // Disable assignment
|
||||||
"[": "array"
|
"[": "array"
|
||||||
|
//'()=': 'fndef' // Diable function definition
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Parser {
|
export class Parser {
|
||||||
|
@ -107,6 +109,7 @@ export class Parser {
|
||||||
and: Polyfill.andOperator,
|
and: Polyfill.andOperator,
|
||||||
or: Polyfill.orOperator,
|
or: Polyfill.orOperator,
|
||||||
"in": Polyfill.inOperator,
|
"in": Polyfill.inOperator,
|
||||||
|
"=": Polyfill.setVar,
|
||||||
"[": Polyfill.arrayIndex
|
"[": Polyfill.arrayIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,13 +123,18 @@ export class Parser {
|
||||||
min: Polyfill.min,
|
min: Polyfill.min,
|
||||||
max: Polyfill.max,
|
max: Polyfill.max,
|
||||||
hypot: Math.hypot || Polyfill.hypot,
|
hypot: Math.hypot || Polyfill.hypot,
|
||||||
pyt: Math.hypot || Polyfill.hypot,
|
pyt: Math.hypot || Polyfill.hypot, // backward compat
|
||||||
pow: Math.pow,
|
pow: Math.pow,
|
||||||
atan2: Math.atan2,
|
atan2: Math.atan2,
|
||||||
"if": Polyfill.condition,
|
"if": Polyfill.condition,
|
||||||
gamma: Polyfill.gamma,
|
gamma: Polyfill.gamma,
|
||||||
"Γ": Polyfill.gamma,
|
"Γ": Polyfill.gamma,
|
||||||
roundTo: Polyfill.roundTo,
|
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.
|
// These constants will automatically be replaced the MOMENT they are parsed.
|
||||||
|
@ -151,6 +159,10 @@ export class Parser {
|
||||||
return new ExprEvalExpression(instr, this)
|
return new ExprEvalExpression(instr, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
evaluate(expr, variables) {
|
||||||
|
return this.parse(expr).evaluate(variables)
|
||||||
|
}
|
||||||
|
|
||||||
isOperatorEnabled(op) {
|
isOperatorEnabled(op) {
|
||||||
const optionName = optionNameMap.hasOwnProperty(op) ? optionNameMap[op] : op
|
const optionName = optionNameMap.hasOwnProperty(op) ? optionNameMap[op] : op
|
||||||
const operators = this.options.operators || {}
|
const operators = this.options.operators || {}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*
|
*
|
||||||
* Ported to QMLJS with modifications done accordingly done by Ad5001 <mail@ad5001.eu> (https://ad5001.eu)
|
* 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
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*
|
*
|
||||||
* Ported to QMLJS with modifications done accordingly done by Ad5001 <mail@ad5001.eu> (https://ad5001.eu)
|
* 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
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
@ -210,8 +210,9 @@ export function gamma(n) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stringOrArrayLength(s) {
|
export function stringOrArrayLength(s) {
|
||||||
if(Array.isArray(s))
|
if(Array.isArray(s)) {
|
||||||
return s.length
|
return s.length
|
||||||
|
}
|
||||||
return String(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))
|
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) {
|
export function arrayIndex(array, index) {
|
||||||
return array[index | 0]
|
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) {
|
export function sign(x) {
|
||||||
return ((x > 0) - (x < 0)) || +x
|
return ((x > 0) - (x < 0)) || +x
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*
|
*
|
||||||
* Ported to QMLJS with modifications done accordingly done by Ad5001 <mail@ad5001.eu> (https://ad5001.eu)
|
* 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
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
@ -472,7 +472,7 @@ export class TokenStream {
|
||||||
this.current = this.newToken(TOP, "==")
|
this.current = this.newToken(TOP, "==")
|
||||||
this.pos++
|
this.pos++
|
||||||
} else {
|
} else {
|
||||||
return false
|
this.current = this.newToken(TOP, c)
|
||||||
}
|
}
|
||||||
} else if(c === "!") {
|
} else if(c === "!") {
|
||||||
if(this.expression.charAt(this.pos + 1) === "=") {
|
if(this.expression.charAt(this.pos + 1) === "=") {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*!
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -64,20 +64,10 @@ function arrayFlatMap(callbackFn, thisArg) {
|
||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
function stringReplaceAll(from, to) {
|
function stringReplaceAll(from, to) {
|
||||||
return this.split(from).join(to)
|
let str = this
|
||||||
}
|
while(str.includes(from))
|
||||||
|
str = str.replace(from, to)
|
||||||
/**
|
return str
|
||||||
* 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]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,8 +98,8 @@ const polyfills = {
|
||||||
[String.prototype, "replaceAll", stringReplaceAll]
|
[String.prototype, "replaceAll", stringReplaceAll]
|
||||||
],
|
],
|
||||||
2022: [
|
2022: [
|
||||||
[Array.prototype, "at", arrayAt],
|
[Array.prototype, "at", notPolyfilled("Array.prototype.at")],
|
||||||
[String.prototype, "at", arrayAt],
|
[String.prototype, "at", notPolyfilled("String.prototype.at")],
|
||||||
[Object, "hasOwn", notPolyfilled("Object.hasOwn")]
|
[Object, "hasOwn", notPolyfilled("Object.hasOwn")]
|
||||||
],
|
],
|
||||||
2023: [
|
2023: [
|
||||||
|
@ -119,7 +109,7 @@ const polyfills = {
|
||||||
[Array.prototype, "toSpliced", notPolyfilled("Array.prototype.toSpliced")],
|
[Array.prototype, "toSpliced", notPolyfilled("Array.prototype.toSpliced")],
|
||||||
[Array.prototype, "with", notPolyfilled("Array.prototype.with")]
|
[Array.prototype, "with", notPolyfilled("Array.prototype.with")]
|
||||||
],
|
],
|
||||||
2025: [
|
2024: [
|
||||||
[Object, "groupBy", notPolyfilled("Object.groupBy")],
|
[Object, "groupBy", notPolyfilled("Object.groupBy")],
|
||||||
[Map, "groupBy", notPolyfilled("Map.groupBy")]
|
[Map, "groupBy", notPolyfilled("Map.groupBy")]
|
||||||
]
|
]
|
||||||
|
@ -133,4 +123,4 @@ for(const [year, entries] of Object.entries(polyfills)) {
|
||||||
for(const [context, functionName, polyfill] of entries.filter(x => x[0][x[1]] === undefined)) {
|
for(const [context, functionName, polyfill] of entries.filter(x => x[0][x[1]] === undefined)) {
|
||||||
context[functionName] = polyfill
|
context[functionName] = polyfill
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -24,7 +24,6 @@ import { Expression, executeExpression } from "./expression.mjs"
|
||||||
*/
|
*/
|
||||||
export class Domain {
|
export class Domain {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.latexMarkup = "#INVALID"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -207,8 +206,8 @@ export class Range extends Domain {
|
||||||
}
|
}
|
||||||
|
|
||||||
includes(x) {
|
includes(x) {
|
||||||
if(typeof x == "string") x = executeExpression(x)
|
|
||||||
if(x instanceof Expression) x = x.execute()
|
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())) &&
|
return ((this.openBegin && x > this.begin.execute()) || (!this.openBegin && x >= this.begin.execute())) &&
|
||||||
((this.openEnd && x < this.end.execute()) || (!this.openEnd && x <= this.end.execute()))
|
((this.openEnd && x < this.end.execute()) || (!this.openEnd && x <= this.end.execute()))
|
||||||
}
|
}
|
||||||
|
@ -250,17 +249,15 @@ export class SpecialDomain extends Domain {
|
||||||
/**
|
/**
|
||||||
* @constructs SpecialDomain
|
* @constructs SpecialDomain
|
||||||
* @param {string} displayName
|
* @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} isValid - function returning true when number is in domain false when it isn't.
|
||||||
* @param {function} next - function provides the next positive value in the domain after the one given.
|
* @param {function} next - function provides the next positive value in the domain after the one given.
|
||||||
* @param {function} previous - function provides the previous positive value in the domain before the one given.
|
* @param {function} previous - function provides the previous positive value in the domain before the one given.
|
||||||
* @param {boolean} moveSupported - Only true if next and previous functions are valid.
|
* @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) {
|
moveSupported = true) {
|
||||||
super()
|
super()
|
||||||
this.displayName = displayName
|
this.displayName = displayName
|
||||||
this.latexMarkup = latexMarkup
|
|
||||||
this.isValid = isValid
|
this.isValid = isValid
|
||||||
this.nextValue = next
|
this.nextValue = next
|
||||||
this.prevValue = previous
|
this.prevValue = previous
|
||||||
|
@ -565,54 +562,39 @@ Domain.RPE.latexMarkup = "\\mathbb{R}^{+*}"
|
||||||
Domain.RME = new Range(-Infinity, 0, true, true)
|
Domain.RME = new Range(-Infinity, 0, true, true)
|
||||||
Domain.RME.displayName = "ℝ⁻*"
|
Domain.RME.displayName = "ℝ⁻*"
|
||||||
Domain.RME.latexMarkup = "\\mathbb{R}^{+*}"
|
Domain.RME.latexMarkup = "\\mathbb{R}^{+*}"
|
||||||
Domain.N = new SpecialDomain(
|
Domain.N = new SpecialDomain("ℕ", x => x % 1 === 0 && x >= 0,
|
||||||
"ℕ", "\\mathbb{N}",
|
|
||||||
x => x % 1 === 0 && x >= 0,
|
|
||||||
x => Math.max(Math.floor(x) + 1, 0),
|
x => Math.max(Math.floor(x) + 1, 0),
|
||||||
x => Math.max(Math.ceil(x) - 1, 0)
|
x => Math.max(Math.ceil(x) - 1, 0))
|
||||||
)
|
Domain.N.latexMarkup = "\\mathbb{N}"
|
||||||
Domain.NE = new SpecialDomain(
|
Domain.NE = new SpecialDomain("ℕ*", x => x % 1 === 0 && x > 0,
|
||||||
"ℕ*", "\\mathbb{N}^{*}",
|
|
||||||
x => x % 1 === 0 && x > 0,
|
|
||||||
x => Math.max(Math.floor(x) + 1, 1),
|
x => Math.max(Math.floor(x) + 1, 1),
|
||||||
x => Math.max(Math.ceil(x) - 1, 1)
|
x => Math.max(Math.ceil(x) - 1, 1))
|
||||||
)
|
Domain.NE.latexMarkup = "\\mathbb{N}^{*}"
|
||||||
Domain.Z = new SpecialDomain(
|
Domain.Z = new SpecialDomain("ℤ", x => x % 1 === 0, x => Math.floor(x) + 1, x => Math.ceil(x) - 1)
|
||||||
"ℤ", "\\mathbb{Z}",
|
Domain.Z.latexMarkup = "\\mathbb{Z}"
|
||||||
x => x % 1 === 0,
|
Domain.ZE = new SpecialDomain("ℤ*", x => x % 1 === 0 && x !== 0,
|
||||||
x => Math.floor(x) + 1,
|
|
||||||
x => Math.ceil(x) - 1
|
|
||||||
)
|
|
||||||
Domain.ZE = new SpecialDomain(
|
|
||||||
"ℤ*", "\\mathbb{Z}^{*}",
|
|
||||||
x => x % 1 === 0 && x !== 0,
|
|
||||||
x => Math.floor(x) + 1 === 0 ? Math.floor(x) + 2 : Math.floor(x) + 1,
|
x => Math.floor(x) + 1 === 0 ? Math.floor(x) + 2 : Math.floor(x) + 1,
|
||||||
x => Math.ceil(x) - 1 === 0 ? Math.ceil(x) - 2 : Math.ceil(x) - 1
|
x => Math.ceil(x) - 1 === 0 ? Math.ceil(x) - 2 : Math.ceil(x) - 1)
|
||||||
)
|
Domain.ZE.latexMarkup = "\\mathbb{Z}^{*}"
|
||||||
Domain.ZM = new SpecialDomain(
|
Domain.ZM = new SpecialDomain("ℤ⁻", x => x % 1 === 0 && x <= 0,
|
||||||
"ℤ⁻", "\\mathbb{Z}^{-}",
|
|
||||||
x => x % 1 === 0 && x <= 0,
|
|
||||||
x => Math.min(Math.floor(x) + 1, 0),
|
x => Math.min(Math.floor(x) + 1, 0),
|
||||||
x => Math.min(Math.ceil(x) - 1, 0)
|
x => Math.min(Math.ceil(x) - 1, 0))
|
||||||
)
|
Domain.ZM.latexMarkup = "\\mathbb{Z}^{-}"
|
||||||
Domain.ZME = new SpecialDomain(
|
Domain.ZME = new SpecialDomain("ℤ⁻*", x => x % 1 === 0 && x < 0,
|
||||||
"ℤ⁻*", "\\mathbb{Z}^{-*}",
|
|
||||||
x => x % 1 === 0 && x < 0,
|
|
||||||
x => Math.min(Math.floor(x) + 1, -1),
|
x => Math.min(Math.floor(x) + 1, -1),
|
||||||
x => Math.min(Math.ceil(x) - 1, -1)
|
x => Math.min(Math.ceil(x) - 1, -1))
|
||||||
)
|
Domain.ZME.latexMarkup = "\\mathbb{Z}^{-*}"
|
||||||
Domain.NLog = new SpecialDomain(
|
Domain.NLog = new SpecialDomain("ℕˡᵒᵍ",
|
||||||
"ℕˡᵒᵍ", "\\mathbb{N}^{log}",
|
|
||||||
x => x / Math.pow(10, Math.ceil(Math.log10(x))) % 1 === 0 && x > 0,
|
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)))
|
let x10pow = Math.pow(10, Math.ceil(Math.log10(x)))
|
||||||
return Math.max(1, (Math.floor(x / x10pow) + 1) * x10pow)
|
return Math.max(1, (Math.floor(x / x10pow) + 1) * x10pow)
|
||||||
},
|
},
|
||||||
x => {
|
function(x) {
|
||||||
let x10pow = Math.pow(10, Math.ceil(Math.log10(x)))
|
let x10pow = Math.pow(10, Math.ceil(Math.log10(x)))
|
||||||
return Math.max(1, (Math.ceil(x / x10pow) - 1) * x10pow)
|
return Math.max(1, (Math.ceil(x / x10pow) - 1) * x10pow)
|
||||||
}
|
})
|
||||||
)
|
Domain.NLog.latexMarkup = "\\mathbb{N}^{log}"
|
||||||
|
|
||||||
let refedDomains = []
|
let refedDomains = []
|
||||||
|
|
||||||
|
@ -644,7 +626,7 @@ export function parseDomainSimple(domain) {
|
||||||
if(domain.includes("U") || domain.includes("∪")) return UnionDomain.import(domain)
|
if(domain.includes("U") || domain.includes("∪")) return UnionDomain.import(domain)
|
||||||
if(domain.includes("∩")) return IntersectionDomain.import(domain)
|
if(domain.includes("∩")) return IntersectionDomain.import(domain)
|
||||||
if(domain.includes("∖") || domain.includes("\\")) return MinusDomain.import(domain)
|
if(domain.includes("∖") || domain.includes("\\")) return MinusDomain.import(domain)
|
||||||
if(domain.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(domain.includes("]") || domain.includes("[")) return Range.import(domain)
|
||||||
if(["R", "ℝ", "N", "ℕ", "Z", "ℤ"].some(str => domain.toUpperCase().includes(str)))
|
if(["R", "ℝ", "N", "ℕ", "Z", "ℤ"].some(str => domain.toUpperCase().includes(str)))
|
||||||
return Domain.import(domain)
|
return Domain.import(domain)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,38 +17,28 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import * as Utils from "../utils/index.mjs"
|
import * as Utils from "../utils.mjs"
|
||||||
import { ExprEvalExpression } from "../lib/expr-eval/expression.mjs"
|
|
||||||
import Latex from "../module/latex.mjs"
|
import Latex from "../module/latex.mjs"
|
||||||
import ExprParser from "../module/expreval.mjs"
|
import ExprParser from "../module/expreval.mjs"
|
||||||
import Objects from "../module/objects.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.
|
* Represents any kind of x-based or non variable based expression.
|
||||||
*/
|
*/
|
||||||
export class Expression {
|
export class Expression {
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {string|ExprEvalExpression} expr
|
|
||||||
*/
|
|
||||||
constructor(expr) {
|
constructor(expr) {
|
||||||
if(typeof expr === "string") {
|
if(typeof expr === "string") {
|
||||||
this.expr = Utils.exponentsToExpression(expr)
|
this.expr = Utils.exponentsToExpression(expr)
|
||||||
this.calc = ExprParser.parse(this.expr).simplify()
|
this.calc = ExprParser.parse(this.expr).simplify()
|
||||||
} else if(expr instanceof ExprEvalExpression) {
|
} else {
|
||||||
// Passed an expression here directly.
|
// Passed an expression here directly.
|
||||||
this.calc = expr.simplify()
|
this.calc = expr.simplify()
|
||||||
this.expr = expr.toString()
|
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
|
this.cachedValue = null
|
||||||
if(this.canBeCached && this.allRequirementsFulfilled())
|
if(this.cached && this.allRequirementsFulfilled())
|
||||||
this.recache()
|
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
|
||||||
this.latexMarkup = Latex.expression(this.calc.tokens)
|
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.
|
* Returns a list of names whose corresponding objects this expression is dependant on and are missing.
|
||||||
* @return {string[]}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
undefinedVariables() {
|
undefinedVariables() {
|
||||||
return this.requiredObjects().filter(objName => !(objName in Objects.currentObjectsByName))
|
return this.requiredObjects().filter(objName => !(objName in Objects.currentObjectsByName))
|
||||||
}
|
}
|
||||||
|
|
||||||
recache() {
|
recache() {
|
||||||
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
|
if(this.cached)
|
||||||
|
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x = 1) {
|
execute(x = 1) {
|
||||||
if(this.canBeCached) {
|
if(this.cached) {
|
||||||
if(this.cachedValue == null)
|
if(this.cachedValue == null)
|
||||||
this.recache()
|
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
|
||||||
return this.cachedValue
|
return this.cachedValue
|
||||||
}
|
}
|
||||||
ExprParser.currentVars = Object.assign({ "x": x }, Objects.currentObjectsByName)
|
ExprParser.currentVars = Object.assign({ "x": x }, Objects.currentObjectsByName)
|
||||||
|
@ -108,10 +99,9 @@ export class Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
simplify(x) {
|
simplify(x) {
|
||||||
let expr = new Expression(this.calc.substitute("x", x).simplify())
|
let expr = this.calc.substitute("x", x).simplify()
|
||||||
if(expr.allRequirementsFulfilled() && expr.execute() === 0)
|
if(expr.evaluate() === 0) expr = "0"
|
||||||
expr = new Expression("0")
|
return new Expression(expr)
|
||||||
return expr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toEditableString() {
|
toEditableString() {
|
||||||
|
@ -120,28 +110,17 @@ export class Expression {
|
||||||
|
|
||||||
toString(forceSign = false) {
|
toString(forceSign = false) {
|
||||||
let str = Utils.makeExpressionReadable(this.calc.toString())
|
let str = Utils.makeExpressionReadable(this.calc.toString())
|
||||||
if(str !== undefined && str.match(NUMBER_MATCHER)) {
|
if(str !== undefined && str.match(/^\d*\.\d+$/)) {
|
||||||
const decimals = str.split(".")[1].split("e")[0]
|
if(str.split(".")[1].split("0").length > 7) {
|
||||||
const zeros = decimals.split("0").length
|
|
||||||
const nines = decimals.split("9").length
|
|
||||||
if(zeros > 7 || nines > 7) {
|
|
||||||
// Likely rounding error
|
// Likely rounding error
|
||||||
str = parseFloat(str).toDecimalPrecision(8).toString()
|
str = parseFloat(str.substring(0, str.length - 1)).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(str[0] === "(" && str.at(-1) === ")")
|
if(str[0] !== "-" && forceSign) str = "+" + str
|
||||||
str = str.substring(1, str.length - 1)
|
|
||||||
if(str[0] !== "-" && forceSign)
|
|
||||||
str = "+" + str
|
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses and executes the given expression
|
|
||||||
* @param {string} expr
|
|
||||||
* @return {number}
|
|
||||||
*/
|
|
||||||
export function executeExpression(expr) {
|
export function executeExpression(expr) {
|
||||||
return (new Expression(expr.toString())).execute()
|
return (new Expression(expr.toString())).execute()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as Expr from "./expression.mjs"
|
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 Latex from "../module/latex.mjs"
|
||||||
import Objects from "../module/objects.mjs"
|
import Objects from "../module/objects.mjs"
|
||||||
import ExprParser from "../module/expreval.mjs"
|
import ExprParser from "../module/expreval.mjs"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -18,33 +18,30 @@
|
||||||
|
|
||||||
import { Module } from "./common.mjs"
|
import { Module } from "./common.mjs"
|
||||||
import { CanvasInterface, DialogInterface } from "./interface.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 { Expression } from "../math/index.mjs"
|
||||||
import Latex from "./latex.mjs"
|
import Latex from "./latex.mjs"
|
||||||
import Objects from "./objects.mjs"
|
import Objects from "./objects.mjs"
|
||||||
import History from "./history.mjs"
|
import History from "./history.mjs"
|
||||||
import Settings from "./settings.mjs"
|
|
||||||
|
|
||||||
|
|
||||||
class CanvasAPI extends Module {
|
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() {
|
constructor() {
|
||||||
super("Canvas", {
|
super("Canvas", {
|
||||||
canvas: CanvasInterface,
|
canvas: CanvasInterface,
|
||||||
drawingErrorDialog: DialogInterface
|
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}>}
|
* @type {Object.<string, {expression: Expression, value: number, maxDraw: number}>}
|
||||||
|
@ -70,18 +67,18 @@ class CanvasAPI extends Module {
|
||||||
*/
|
*/
|
||||||
initialize({ canvas, drawingErrorDialog }) {
|
initialize({ canvas, drawingErrorDialog }) {
|
||||||
super.initialize({ canvas, drawingErrorDialog })
|
super.initialize({ canvas, drawingErrorDialog })
|
||||||
this.#canvas = canvas
|
this._canvas = canvas
|
||||||
this.#drawingErrorDialog = drawingErrorDialog
|
this._drawingErrorDialog = drawingErrorDialog
|
||||||
}
|
}
|
||||||
|
|
||||||
get width() {
|
get width() {
|
||||||
if(!this.initialized) throw new Error("Attempting width before initialize!")
|
if(!this.initialized) throw new Error("Attempting width before initialize!")
|
||||||
return this.#canvas.width
|
return this._canvas.width
|
||||||
}
|
}
|
||||||
|
|
||||||
get height() {
|
get height() {
|
||||||
if(!this.initialized) throw new Error("Attempting height before initialize!")
|
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() {
|
get xmin() {
|
||||||
if(!this.initialized) throw new Error("Attempting xmin before initialize!")
|
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() {
|
get xzoom() {
|
||||||
if(!this.initialized) throw new Error("Attempting xzoom before initialize!")
|
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() {
|
get ymax() {
|
||||||
if(!this.initialized) throw new Error("Attempting ymax before initialize!")
|
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() {
|
get yzoom() {
|
||||||
if(!this.initialized) throw new Error("Attempting yzoom before initialize!")
|
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() {
|
get xlabel() {
|
||||||
if(!this.initialized) throw new Error("Attempting xlabel before initialize!")
|
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() {
|
get ylabel() {
|
||||||
if(!this.initialized) throw new Error("Attempting ylabel before initialize!")
|
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() {
|
get linewidth() {
|
||||||
if(!this.initialized) throw new Error("Attempting linewidth before initialize!")
|
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() {
|
get textsize() {
|
||||||
if(!this.initialized) throw new Error("Attempting textsize before initialize!")
|
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() {
|
get logscalex() {
|
||||||
if(!this.initialized) throw new Error("Attempting logscalex before initialize!")
|
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() {
|
get showxgrad() {
|
||||||
if(!this.initialized) throw new Error("Attempting showxgrad before initialize!")
|
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() {
|
get showygrad() {
|
||||||
if(!this.initialized) throw new Error("Attempting showygrad before initialize!")
|
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() {
|
requestPaint() {
|
||||||
if(!this.initialized) throw new Error("Attempting requestPaint before initialize!")
|
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() {
|
redraw() {
|
||||||
if(!this.initialized) throw new Error("Attempting redraw before initialize!")
|
if(!this.initialized) throw new Error("Attempting redraw before initialize!")
|
||||||
if(this.#ctx == null)
|
this._ctx = this._canvas.getContext("2d")
|
||||||
this.#ctx = this.#canvas.getContext("2d")
|
|
||||||
this.#redrawCount = (this.#redrawCount + 1) % 10000
|
|
||||||
this._computeAxes()
|
this._computeAxes()
|
||||||
this._reset()
|
this._reset()
|
||||||
this._drawGrid()
|
this._drawGrid()
|
||||||
this._drawAxes()
|
this._drawAxes()
|
||||||
this._drawLabels()
|
this._drawLabels()
|
||||||
this.#ctx.lineWidth = this.linewidth
|
this._ctx.lineWidth = this.linewidth
|
||||||
for(let objType in Objects.currentObjects) {
|
for(let objType in Objects.currentObjects) {
|
||||||
for(let obj of Objects.currentObjects[objType]) {
|
for(let obj of Objects.currentObjects[objType]) {
|
||||||
this.#ctx.strokeStyle = obj.color
|
this._ctx.strokeStyle = obj.color
|
||||||
this.#ctx.fillStyle = obj.color
|
this._ctx.fillStyle = obj.color
|
||||||
if(obj.visible)
|
if(obj.visible)
|
||||||
try {
|
try {
|
||||||
obj.draw(this)
|
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)
|
// Drawing throws an error. Generally, it's due to a new modification (or the opening of a file)
|
||||||
console.error(e)
|
console.error(e)
|
||||||
console.log(e.stack)
|
console.log(e.stack)
|
||||||
this.#drawingErrorDialog.show(objType, obj.name, e.message)
|
this._drawingErrorDialog.show(objType, obj.name, e.message)
|
||||||
History.undo()
|
History.undo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.#ctx.lineWidth = 1
|
this._ctx.lineWidth = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -245,9 +240,9 @@ class CanvasAPI extends Module {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_computeAxes() {
|
_computeAxes() {
|
||||||
let exprY = new Expression(`x*(${Settings.yaxisstep})`)
|
let exprY = new Expression(`x*(${this._canvas.yaxisstep})`)
|
||||||
let y1 = exprY.execute(1)
|
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)
|
let x1 = exprX.execute(1)
|
||||||
this.axesSteps = {
|
this.axesSteps = {
|
||||||
x: {
|
x: {
|
||||||
|
@ -269,10 +264,10 @@ class CanvasAPI extends Module {
|
||||||
*/
|
*/
|
||||||
_reset() {
|
_reset() {
|
||||||
// Reset
|
// Reset
|
||||||
this.#ctx.fillStyle = "#FFFFFF"
|
this._ctx.fillStyle = "#FFFFFF"
|
||||||
this.#ctx.strokeStyle = "#000000"
|
this._ctx.strokeStyle = "#000000"
|
||||||
this.#ctx.font = `${this.textsize}px sans-serif`
|
this._ctx.font = `${this.textsize}px sans-serif`
|
||||||
this.#ctx.fillRect(0, 0, this.width, this.height)
|
this._ctx.fillRect(0, 0, this.width, this.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -280,7 +275,7 @@ class CanvasAPI extends Module {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_drawGrid() {
|
_drawGrid() {
|
||||||
this.#ctx.strokeStyle = "#C0C0C0"
|
this._ctx.strokeStyle = "#C0C0C0"
|
||||||
if(this.logscalex) {
|
if(this.logscalex) {
|
||||||
for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow++) {
|
for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow++) {
|
||||||
for(let xmulti = 1; xmulti < 10; xmulti++) {
|
for(let xmulti = 1; xmulti < 10; xmulti++) {
|
||||||
|
@ -304,7 +299,7 @@ class CanvasAPI extends Module {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_drawAxes() {
|
_drawAxes() {
|
||||||
this.#ctx.strokeStyle = "#000000"
|
this._ctx.strokeStyle = "#000000"
|
||||||
let axisypos = this.logscalex ? 1 : 0
|
let axisypos = this.logscalex ? 1 : 0
|
||||||
this.drawXLine(axisypos)
|
this.drawXLine(axisypos)
|
||||||
this.drawYLine(0)
|
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 axisypx = this.x2px(this.logscalex ? 1 : 0) // X coordinate of Y axis
|
||||||
let axisxpx = this.y2px(0) // Y coordinate of X axis
|
let axisxpx = this.y2px(0) // Y coordinate of X axis
|
||||||
// Labels
|
// Labels
|
||||||
this.#ctx.fillStyle = "#000000"
|
this._ctx.fillStyle = "#000000"
|
||||||
this.#ctx.font = `${this.textsize}px sans-serif`
|
this._ctx.font = `${this.textsize}px sans-serif`
|
||||||
this.#ctx.fillText(this.ylabel, axisypx + 10, 24)
|
this._ctx.fillText(this.ylabel, axisypx + 10, 24)
|
||||||
let textWidth = this.#ctx.measureText(this.xlabel).width
|
let textWidth = this._ctx.measureText(this.xlabel).width
|
||||||
this.#ctx.fillText(this.xlabel, this.width - 14 - textWidth, axisxpx - 5)
|
this._ctx.fillText(this.xlabel, this.width - 14 - textWidth, axisxpx - 5)
|
||||||
// Axis graduation labels
|
// 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.showxgrad) {
|
||||||
if(this.logscalex) {
|
if(this.logscalex) {
|
||||||
for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow += 1) {
|
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)
|
if(xpow !== 0)
|
||||||
this.drawVisibleText("10" + textsup(xpow), this.x2px(Math.pow(10, xpow)) - textWidth / 2, axisxpx + 16 + (6 * (xpow === 1)))
|
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) {
|
for(let y = 0; y < this.axesSteps.y.maxDraw; y += 1) {
|
||||||
let drawY = y * this.axesSteps.y.value
|
let drawY = y * this.axesSteps.y.value
|
||||||
let txtY = this.axesSteps.y.expression.simplify(y).toString().replace(/^\((.+)\)$/, "$1")
|
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)))
|
this.drawVisibleText(txtY, axisypx - 6 - textWidth, this.y2px(drawY) + 4 + (10 * (y === 0)))
|
||||||
if(y !== 0)
|
if(y !== 0)
|
||||||
this.drawVisibleText("-" + txtY, axisypx - 6 - textWidth - txtMinus, this.y2px(-drawY) + 4)
|
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) {
|
drawVisibleText(text, x, y) {
|
||||||
if(x > 0 && x < this.width && y > 0 && y < this.height) {
|
if(x > 0 && x < this.width && y > 0 && y < this.height) {
|
||||||
text.toString().split("\n").forEach((txt, i) => {
|
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
|
* @param {number} height
|
||||||
*/
|
*/
|
||||||
drawVisibleImage(image, x, y, width, height) {
|
drawVisibleImage(image, x, y, width, height) {
|
||||||
this.#canvas.markDirty(Qt.rect(x, y, width, height))
|
this._canvas.markDirty(Qt.rect(x, y, width, height))
|
||||||
this.#ctx.drawImage(image, 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!
|
let defaultHeight = this.textsize * 1.2 // Approximate but good enough!
|
||||||
for(let txt of text.split("\n")) {
|
for(let txt of text.split("\n")) {
|
||||||
theight += defaultHeight
|
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 }
|
return { "width": twidth, "height": theight }
|
||||||
}
|
}
|
||||||
|
@ -499,10 +494,10 @@ class CanvasAPI extends Module {
|
||||||
* @param {number} y2
|
* @param {number} y2
|
||||||
*/
|
*/
|
||||||
drawLine(x1, y1, x2, y2) {
|
drawLine(x1, y1, x2, y2) {
|
||||||
this.#ctx.beginPath()
|
this._ctx.beginPath()
|
||||||
this.#ctx.moveTo(x1, y1)
|
this._ctx.moveTo(x1, y1)
|
||||||
this.#ctx.lineTo(x2, y2)
|
this._ctx.lineTo(x2, y2)
|
||||||
this.#ctx.stroke()
|
this._ctx.stroke()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -514,9 +509,9 @@ class CanvasAPI extends Module {
|
||||||
* @param {number} dashPxSize
|
* @param {number} dashPxSize
|
||||||
*/
|
*/
|
||||||
drawDashedLine(x1, y1, x2, y2, dashPxSize = 6) {
|
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.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
|
* @param {function(LatexRenderResult|{width: number, height: number, source: string})} callback
|
||||||
*/
|
*/
|
||||||
renderLatexImage(ltxText, color, callback) {
|
renderLatexImage(ltxText, color, callback) {
|
||||||
const currentRedrawCount = this.#redrawCount
|
|
||||||
const onRendered = (imgData) => {
|
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.
|
// Wait until the image is loaded to callback.
|
||||||
this.#canvas.loadImageAsync(imgData.source).then(() => {
|
this._canvas.loadImage(imgData.source)
|
||||||
if(this.#redrawCount === currentRedrawCount)
|
this._canvas.imageLoaders[imgData.source] = [callback, imgData]
|
||||||
callback(imgData)
|
|
||||||
else
|
|
||||||
console.log("1. Discard render of", imgData.source, this.#redrawCount, currentRedrawCount)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
// Callback directly
|
// Callback directly
|
||||||
if(this.#redrawCount === currentRedrawCount)
|
callback(imgData)
|
||||||
callback(imgData)
|
|
||||||
else
|
|
||||||
console.log("2. Discard render of", imgData.source, this.#redrawCount, currentRedrawCount)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const prerendered = Latex.findPrerendered(ltxText, this.textsize, color)
|
const prerendered = Latex.findPrerendered(ltxText, this.textsize, color)
|
||||||
|
@ -556,11 +543,11 @@ class CanvasAPI extends Module {
|
||||||
//
|
//
|
||||||
|
|
||||||
get font() {
|
get font() {
|
||||||
return this.#ctx.font
|
return this._ctx.font
|
||||||
}
|
}
|
||||||
|
|
||||||
set font(value) {
|
set font(value) {
|
||||||
return this.#ctx.font = value
|
return this._ctx.font = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -573,9 +560,9 @@ class CanvasAPI extends Module {
|
||||||
* @param {boolean} counterclockwise
|
* @param {boolean} counterclockwise
|
||||||
*/
|
*/
|
||||||
arc(x, y, radius, startAngle, endAngle, counterclockwise = false) {
|
arc(x, y, radius, startAngle, endAngle, counterclockwise = false) {
|
||||||
this.#ctx.beginPath()
|
this._ctx.beginPath()
|
||||||
this.#ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise)
|
this._ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise)
|
||||||
this.#ctx.stroke()
|
this._ctx.stroke()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -585,9 +572,9 @@ class CanvasAPI extends Module {
|
||||||
* @param {number} radius
|
* @param {number} radius
|
||||||
*/
|
*/
|
||||||
disc(x, y, radius) {
|
disc(x, y, radius) {
|
||||||
this.#ctx.beginPath()
|
this._ctx.beginPath()
|
||||||
this.#ctx.arc(x, y, radius, 0, 2 * Math.PI)
|
this._ctx.arc(x, y, radius, 0, 2 * Math.PI)
|
||||||
this.#ctx.fill()
|
this._ctx.fill()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -598,7 +585,7 @@ class CanvasAPI extends Module {
|
||||||
* @param {number} h
|
* @param {number} h
|
||||||
*/
|
*/
|
||||||
fillRect(x, y, w, h) {
|
fillRect(x, y, w, h) {
|
||||||
this.#ctx.fillRect(x, y, w, h)
|
this._ctx.fillRect(x, y, w, h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,7 +17,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Interface } from "./interface.mjs"
|
import { Interface } from "./interface.mjs"
|
||||||
import { BaseEventEmitter } from "../events.mjs"
|
|
||||||
|
|
||||||
// Define Modules interface before they are imported.
|
// Define Modules interface before they are imported.
|
||||||
globalThis.Modules = globalThis.Modules || {}
|
globalThis.Modules = globalThis.Modules || {}
|
||||||
|
@ -25,13 +24,7 @@ globalThis.Modules = globalThis.Modules || {}
|
||||||
/**
|
/**
|
||||||
* Base class for global APIs in runtime.
|
* Base class for global APIs in runtime.
|
||||||
*/
|
*/
|
||||||
export class Module extends BaseEventEmitter {
|
export class Module {
|
||||||
/** @type {string} */
|
|
||||||
#name
|
|
||||||
/** @type {Object.<string, (Interface|string|number|boolean)>} */
|
|
||||||
#initializationParameters
|
|
||||||
/** @type {boolean} */
|
|
||||||
#initialized = false
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -39,18 +32,11 @@ export class Module extends BaseEventEmitter {
|
||||||
* @param {Object.<string, (Interface|string|number|boolean)>} initializationParameters - List of parameters for the initialize function.
|
* @param {Object.<string, (Interface|string|number|boolean)>} initializationParameters - List of parameters for the initialize function.
|
||||||
*/
|
*/
|
||||||
constructor(name, initializationParameters = {}) {
|
constructor(name, initializationParameters = {}) {
|
||||||
super()
|
|
||||||
console.log(`Loading module ${name}...`)
|
console.log(`Loading module ${name}...`)
|
||||||
this.#name = name
|
this.__name = name
|
||||||
this.#initializationParameters = initializationParameters
|
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
|
* @param {Object.<string, any>} options
|
||||||
*/
|
*/
|
||||||
initialize(options) {
|
initialize(options) {
|
||||||
if(this.#initialized)
|
if(this.initialized)
|
||||||
throw new Error(`Cannot reinitialize module ${this.#name}.`)
|
throw new Error(`Cannot reinitialize module ${this.__name}.`)
|
||||||
console.log(`Initializing ${this.#name}...`)
|
console.log(`Initializing ${this.__name}...`)
|
||||||
for(const [name, value] of Object.entries(this.#initializationParameters)) {
|
for(const [name, value] of Object.entries(this.__initializationParameters)) {
|
||||||
if(!options.hasOwnProperty(name))
|
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)
|
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])
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
import { Module } from "./common.mjs"
|
import { Module } from "./common.mjs"
|
||||||
import { Parser } from "../lib/expr-eval/parser.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
|
// Variables not provided by expr-eval.js, needs to be provided manually
|
||||||
"pi": Math.PI,
|
"pi": Math.PI,
|
||||||
"PI": Math.PI,
|
"PI": Math.PI,
|
||||||
|
@ -35,17 +35,15 @@ const EVAL_VARIABLES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExprParserAPI extends Module {
|
class ExprParserAPI extends Module {
|
||||||
#parser = new Parser()
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("ExprParser")
|
super("ExprParser")
|
||||||
this.currentVars = {}
|
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.integral = this.integral.bind(this)
|
||||||
this.#parser.functions.derivative = this.derivative.bind(this)
|
this._parser.functions.derivative = this.derivative.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,7 +68,7 @@ class ExprParserAPI extends Module {
|
||||||
[f, variable] = args
|
[f, variable] = args
|
||||||
if(typeof f !== "string" || typeof variable !== "string")
|
if(typeof f !== "string" || typeof variable !== "string")
|
||||||
throw EvalError(qsTranslate("usage", "Usage:\n%1").arg(usage2))
|
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
|
} else
|
||||||
throw EvalError(qsTranslate("usage", "Usage:\n%1\n%2").arg(usage1).arg(usage2))
|
throw EvalError(qsTranslate("usage", "Usage:\n%1\n%2").arg(usage1).arg(usage2))
|
||||||
return f
|
return f
|
||||||
|
@ -81,14 +79,14 @@ class ExprParserAPI extends Module {
|
||||||
* @returns {ExprEvalExpression}
|
* @returns {ExprEvalExpression}
|
||||||
*/
|
*/
|
||||||
parse(expression) {
|
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 usage1 = qsTranslate("usage", "integral(<from: number>, <to: number>, <f: ExecutableObject>)")
|
||||||
let usage2 = qsTranslate("usage", "integral(<from: number>, <to: number>, <f: string>, <variable: string>)")
|
let usage2 = qsTranslate("usage", "integral(<from: number>, <to: number>, <f: string>, <variable: string>)")
|
||||||
let f = this.parseArgumentsForFunction(args, usage1, usage2)
|
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))
|
throw EvalError(qsTranslate("usage", "Usage:\n%1\n%2").arg(usage1).arg(usage2))
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/Simpson%27s_rule
|
// 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 usage2 = qsTranslate("usage", "derivative(<f: string>, <variable: string>, <x: number>)")
|
||||||
let x = args.pop()
|
let x = args.pop()
|
||||||
let f = this.parseArgumentsForFunction(args, usage1, usage2)
|
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))
|
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
|
return (f(x + derivative_precision / 2) - f(x - derivative_precision / 2)) / derivative_precision
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,164 +17,60 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Module } from "./common.mjs"
|
import { Module } from "./common.mjs"
|
||||||
import { HelperInterface, NUMBER, STRING } from "./interface.mjs"
|
import { HistoryInterface, NUMBER, STRING } from "./interface.mjs"
|
||||||
import { BaseEvent } from "../events.mjs"
|
|
||||||
import { Action, Actions } from "../history/index.mjs"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ClearedEvent extends BaseEvent {
|
|
||||||
constructor() {
|
|
||||||
super("cleared")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoadedEvent extends BaseEvent {
|
|
||||||
constructor() {
|
|
||||||
super("loaded")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AddedEvent extends BaseEvent {
|
|
||||||
constructor(action) {
|
|
||||||
super("added")
|
|
||||||
this.action = action
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UndoneEvent extends BaseEvent {
|
|
||||||
constructor(action) {
|
|
||||||
super("undone")
|
|
||||||
this.undid = action
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RedoneEvent extends BaseEvent {
|
|
||||||
constructor(action) {
|
|
||||||
super("redone")
|
|
||||||
this.redid = action
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HistoryAPI extends Module {
|
class HistoryAPI extends Module {
|
||||||
static emits = ["cleared", "loaded", "added", "undone", "redone"]
|
|
||||||
|
|
||||||
#helper
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("History", {
|
super("History", {
|
||||||
helper: HelperInterface,
|
historyObj: HistoryInterface,
|
||||||
themeTextColor: STRING,
|
themeTextColor: STRING,
|
||||||
imageDepth: NUMBER,
|
imageDepth: NUMBER,
|
||||||
fontSize: NUMBER
|
fontSize: NUMBER
|
||||||
})
|
})
|
||||||
// History QML object
|
// History QML object
|
||||||
/** @type {Action[]} */
|
this.history = null
|
||||||
this.undoStack = []
|
|
||||||
/** @type {Action[]} */
|
|
||||||
this.redoStack = []
|
|
||||||
|
|
||||||
this.themeTextColor = "#FF0000"
|
this.themeTextColor = "#FF0000"
|
||||||
this.imageDepth = 2
|
this.imageDepth = 2
|
||||||
this.fontSize = 28
|
this.fontSize = 28
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
initialize({ historyObj, themeTextColor, imageDepth, fontSize }) {
|
||||||
* @param {HelperInterface} historyObj
|
super.initialize({ historyObj, themeTextColor, imageDepth, fontSize })
|
||||||
* @param {string} themeTextColor
|
this.history = historyObj
|
||||||
* @param {number} imageDepth
|
|
||||||
* @param {number} fontSize
|
|
||||||
*/
|
|
||||||
initialize({ helper, themeTextColor, imageDepth, fontSize }) {
|
|
||||||
super.initialize({ helper, themeTextColor, imageDepth, fontSize })
|
|
||||||
this.#helper = helper
|
|
||||||
this.themeTextColor = themeTextColor
|
this.themeTextColor = themeTextColor
|
||||||
this.imageDepth = imageDepth
|
this.imageDepth = imageDepth
|
||||||
this.fontSize = fontSize
|
this.fontSize = fontSize
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Undoes the Action at the top of the undo stack and pushes it to the top of the redo stack.
|
|
||||||
*/
|
|
||||||
undo() {
|
undo() {
|
||||||
if(!this.initialized) throw new Error("Attempting undo before initialize!")
|
if(!this.initialized) throw new Error("Attempting undo before initialize!")
|
||||||
if(this.undoStack.length > 0) {
|
this.history.undo()
|
||||||
const action = this.undoStack.pop()
|
|
||||||
action.undo()
|
|
||||||
this.redoStack.push(action)
|
|
||||||
this.emit(new UndoneEvent(action))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Redoes the Action at the top of the redo stack and pushes it to the top of the undo stack.
|
|
||||||
*/
|
|
||||||
redo() {
|
redo() {
|
||||||
if(!this.initialized) throw new Error("Attempting redo before initialize!")
|
if(!this.initialized) throw new Error("Attempting redo before initialize!")
|
||||||
if(this.redoStack.length > 0) {
|
this.history.redo()
|
||||||
const action = this.redoStack.pop()
|
|
||||||
action.redo()
|
|
||||||
this.undoStack.push(action)
|
|
||||||
this.emit(new RedoneEvent(action))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears both undo and redo stacks completely.
|
|
||||||
*/
|
|
||||||
clear() {
|
clear() {
|
||||||
if(!this.initialized) throw new Error("Attempting clear before initialize!")
|
if(!this.initialized) throw new Error("Attempting clear before initialize!")
|
||||||
this.undoStack = []
|
this.history.clear()
|
||||||
this.redoStack = []
|
|
||||||
this.emit(new ClearedEvent())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an instance of HistoryLib.Action to history.
|
|
||||||
* @param action
|
|
||||||
*/
|
|
||||||
addToHistory(action) {
|
addToHistory(action) {
|
||||||
if(!this.initialized) throw new Error("Attempting addToHistory before initialize!")
|
if(!this.initialized) throw new Error("Attempting addToHistory before initialize!")
|
||||||
if(action instanceof Action) {
|
this.history.addToHistory(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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
unserialize(...data) {
|
||||||
* Unserializes both the undo stack and redo stack from serialized content.
|
|
||||||
* @param {[string, any[]][]} undoSt
|
|
||||||
* @param {[string, any[]][]} redoSt
|
|
||||||
*/
|
|
||||||
unserialize(undoSt, redoSt) {
|
|
||||||
if(!this.initialized) throw new Error("Attempting unserialize before initialize!")
|
if(!this.initialized) throw new Error("Attempting unserialize before initialize!")
|
||||||
this.clear()
|
this.history.unserialize(...data)
|
||||||
for(const [name, args] of undoSt)
|
|
||||||
this.undoStack.push(
|
|
||||||
new Actions[name](...args)
|
|
||||||
)
|
|
||||||
for(const [name, args] of redoSt)
|
|
||||||
this.redoStack.push(
|
|
||||||
new Actions[name](...args)
|
|
||||||
)
|
|
||||||
this.emit(new LoadedEvent())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Serializes history into JSON-able content.
|
|
||||||
* @return {[[string, any[]], [string, any[]]]}
|
|
||||||
*/
|
|
||||||
serialize() {
|
serialize() {
|
||||||
if(!this.initialized) throw new Error("Attempting serialize before initialize!")
|
if(!this.initialized) throw new Error("Attempting serialize before initialize!")
|
||||||
let undoSt = [], redoSt = [];
|
return this.history.serialize()
|
||||||
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]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,7 +17,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Objects from "./objects.mjs"
|
import Objects from "./objects.mjs"
|
||||||
import Settings from "./settings.mjs"
|
|
||||||
import ExprParser from "./expreval.mjs"
|
import ExprParser from "./expreval.mjs"
|
||||||
import Latex from "./latex.mjs"
|
import Latex from "./latex.mjs"
|
||||||
import History from "./history.mjs"
|
import History from "./history.mjs"
|
||||||
|
@ -27,7 +26,6 @@ import Preferences from "./preferences.mjs"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Objects,
|
Objects,
|
||||||
Settings,
|
|
||||||
ExprParser,
|
ExprParser,
|
||||||
Latex,
|
Latex,
|
||||||
History,
|
History,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/**
|
/*!
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
*
|
*
|
||||||
* @author Ad5001 <mail@ad5001.eu>
|
* @author Ad5001 <mail@ad5001.eu>
|
||||||
* @license GPL-3.0-or-later
|
* @license GPL-3.0-or-later
|
||||||
* @copyright (C) 2021-2025 Ad5001
|
* @copyright (C) 2021-2024 Ad5001
|
||||||
* @preserve
|
* @preserve
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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.
|
* Throws an error if the implementation does not conform to the interface.
|
||||||
* @param {typeof Interface} interface_
|
* @param {typeof Interface} interface_
|
||||||
* @param {object} classToCheck
|
* @param {object} classToCheck
|
||||||
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
static checkImplementation(interface_, classToCheck) {
|
static check_implementation(interface_, classToCheck) {
|
||||||
const properties = new interface_()
|
const properties = new interface_()
|
||||||
const interfaceName = interface_.name
|
const interfaceName = interface_.name
|
||||||
const toCheckName = classToCheck.constructor.name
|
const toCheckName = classToCheck.constructor.name
|
||||||
|
@ -51,7 +52,7 @@ export class Interface {
|
||||||
else if((typeof value) === "object")
|
else if((typeof value) === "object")
|
||||||
// Test type of object.
|
// Test type of object.
|
||||||
if(value instanceof Interface)
|
if(value instanceof Interface)
|
||||||
Interface.checkImplementation(value, classToCheck[property])
|
Interface.check_implementation(value, classToCheck[property])
|
||||||
else if(value.prototype && !(classToCheck[property] instanceof value))
|
else if(value.prototype && !(classToCheck[property] instanceof value))
|
||||||
throw new Error(`Property '${property}' of ${interfaceName} implementation ${toCheckName} is not '${value.constructor.name}'.`)
|
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} */
|
/** @type {function(string): CanvasRenderingContext2D} */
|
||||||
getContext = FUNCTION
|
getContext = FUNCTION
|
||||||
/** @type {function(rect)} */
|
/** @type {function(rect)} */
|
||||||
markDirty = FUNCTION
|
markDirty = FUNCTION
|
||||||
/** @type {function(string): Promise} */
|
/** @type {function(string)} */
|
||||||
loadImageAsync = FUNCTION
|
loadImage = FUNCTION
|
||||||
/** @type {function(string)} */
|
/** @type {function(string)} */
|
||||||
isImageLoading = FUNCTION
|
isImageLoading = FUNCTION
|
||||||
/** @type {function(string)} */
|
/** @type {function(string)} */
|
||||||
|
@ -77,28 +97,30 @@ export class CanvasInterface extends Interface {
|
||||||
export class RootInterface extends Interface {
|
export class RootInterface extends Interface {
|
||||||
width = NUMBER
|
width = NUMBER
|
||||||
height = NUMBER
|
height = NUMBER
|
||||||
|
updateObjectsLists = FUNCTION
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DialogInterface extends Interface {
|
export class DialogInterface extends Interface {
|
||||||
show = FUNCTION
|
show = FUNCTION
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class HistoryInterface extends Interface {
|
||||||
|
undo = FUNCTION
|
||||||
|
redo = FUNCTION
|
||||||
|
clear = FUNCTION
|
||||||
|
addToHistory = FUNCTION
|
||||||
|
unserialize = FUNCTION
|
||||||
|
serialize = FUNCTION
|
||||||
|
}
|
||||||
|
|
||||||
export class LatexInterface extends Interface {
|
export class LatexInterface extends Interface {
|
||||||
supportsAsyncRender = BOOLEAN
|
|
||||||
/**
|
/**
|
||||||
* @param {string} markup - LaTeX markup to render
|
* @param {string} markup - LaTeX markup to render
|
||||||
* @param {number} fontSize - Font size (in pt) to render
|
* @param {number} fontSize - Font size (in pt) to render
|
||||||
* @param {string} color - Color of the text to render
|
* @param {string} color - Color of the text to render
|
||||||
* @returns {string} - Comma separated data of the image (source, width, height)
|
* @returns {string} - Comma separated data of the image (source, width, height)
|
||||||
*/
|
*/
|
||||||
renderSync = FUNCTION
|
render = 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
|
|
||||||
/**
|
/**
|
||||||
* @param {string} markup - LaTeX markup to render
|
* @param {string} markup - LaTeX markup to render
|
||||||
* @param {number} fontSize - Font size (in pt) 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
|
* Gets a setting from the config
|
||||||
* @param {string} settingName - Setting (and its dot-separated namespace) to get (e.g. "default_graph.xmin")
|
* @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
|
getSetting = FUNCTION
|
||||||
/**
|
/**
|
||||||
* Sets a setting in the config
|
* Sets a setting in the config
|
||||||
* @param {string} settingName - Setting (and its dot-separated namespace) to set (e.g. "default_graph.xmin")
|
* @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
|
setSetting = FUNCTION
|
||||||
/**
|
/**
|
||||||
|
@ -138,4 +184,4 @@ export class HelperInterface extends Interface {
|
||||||
* @returns {string} the loaded data - just JSON encoded, requires the "LPFv1" mime to be stripped
|
* @returns {string} the loaded data - just JSON encoded, requires the "LPFv1" mime to be stripped
|
||||||
*/
|
*/
|
||||||
load = FUNCTION
|
load = FUNCTION
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,69 +19,36 @@
|
||||||
import { Module } from "./common.mjs"
|
import { Module } from "./common.mjs"
|
||||||
import Objects from "./objects.mjs"
|
import Objects from "./objects.mjs"
|
||||||
import History from "./history.mjs"
|
import History from "./history.mjs"
|
||||||
import Settings from "./settings.mjs"
|
import Canvas from "./canvas.mjs"
|
||||||
import { DialogInterface, RootInterface } from "./interface.mjs"
|
import { DialogInterface, RootInterface, SettingsInterface } from "./interface.mjs"
|
||||||
import { BaseEvent } from "../events.mjs"
|
|
||||||
|
|
||||||
|
|
||||||
class LoadedEvent extends BaseEvent {
|
|
||||||
constructor() {
|
|
||||||
super("loaded")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SavedEvent extends BaseEvent {
|
|
||||||
constructor() {
|
|
||||||
super("saved")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ModifiedEvent extends BaseEvent {
|
|
||||||
constructor() {
|
|
||||||
super("modified")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IOAPI extends Module {
|
class IOAPI extends Module {
|
||||||
static emits = ["loaded", "saved", "modified"]
|
|
||||||
|
|
||||||
/** @type {RootInterface} */
|
|
||||||
#rootElement
|
|
||||||
/** @type {{show: function(string)}} */
|
|
||||||
#alert
|
|
||||||
#saved = true
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("IO", {
|
super("IO", {
|
||||||
alert: DialogInterface,
|
alert: DialogInterface,
|
||||||
root: RootInterface
|
root: RootInterface,
|
||||||
|
settings: SettingsInterface
|
||||||
})
|
})
|
||||||
|
/**
|
||||||
// Settings.on("changed", this.__emitModified.bind(this))
|
* Path of the currently opened file. Empty if no file is opened.
|
||||||
History.on("added undone redone", this.__emitModified.bind(this))
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.saveFileName = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
__emitModified() {
|
|
||||||
this.#saved = false
|
|
||||||
this.emit(new ModifiedEvent())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if no changes have been made since last save, false otherwise.
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
get saved() { return this.#saved }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes module with QML elements.
|
* Initializes module with QML elements.
|
||||||
* @param {RootInterface} root
|
* @param {RootInterface} root
|
||||||
|
* @param {SettingsInterface} settings
|
||||||
* @param {{show: function(string)}} alert
|
* @param {{show: function(string)}} alert
|
||||||
*/
|
*/
|
||||||
initialize({ root, alert }) {
|
initialize({ root, settings, alert }) {
|
||||||
super.initialize({ root, alert })
|
super.initialize({ root, settings, alert })
|
||||||
this.#rootElement = root
|
this.rootElement = root
|
||||||
this.#alert = alert
|
this.settings = settings
|
||||||
|
this.alert = alert
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,7 +60,7 @@ class IOAPI extends Module {
|
||||||
// Add extension if necessary
|
// Add extension if necessary
|
||||||
if(["lpf"].indexOf(filename.split(".")[filename.split(".").length - 1]) === -1)
|
if(["lpf"].indexOf(filename.split(".")[filename.split(".").length - 1]) === -1)
|
||||||
filename += ".lpf"
|
filename += ".lpf"
|
||||||
Settings.set("saveFilename", filename, false)
|
this.saveFilename = filename
|
||||||
let objs = {}
|
let objs = {}
|
||||||
for(let objType in Objects.currentObjects) {
|
for(let objType in Objects.currentObjects) {
|
||||||
objs[objType] = []
|
objs[objType] = []
|
||||||
|
@ -102,29 +69,28 @@ class IOAPI extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let settings = {
|
let settings = {
|
||||||
"xzoom": Settings.xzoom,
|
"xzoom": this.settings.xzoom,
|
||||||
"yzoom": Settings.yzoom,
|
"yzoom": this.settings.yzoom,
|
||||||
"xmin": Settings.xmin,
|
"xmin": this.settings.xmin,
|
||||||
"ymax": Settings.ymax,
|
"ymax": this.settings.ymax,
|
||||||
"xaxisstep": Settings.xaxisstep,
|
"xaxisstep": this.settings.xaxisstep,
|
||||||
"yaxisstep": Settings.yaxisstep,
|
"yaxisstep": this.settings.yaxisstep,
|
||||||
"xaxislabel": Settings.xlabel,
|
"xaxislabel": this.settings.xlabel,
|
||||||
"yaxislabel": Settings.ylabel,
|
"yaxislabel": this.settings.ylabel,
|
||||||
"logscalex": Settings.logscalex,
|
"logscalex": this.settings.logscalex,
|
||||||
"linewidth": Settings.linewidth,
|
"linewidth": this.settings.linewidth,
|
||||||
"showxgrad": Settings.showxgrad,
|
"showxgrad": this.settings.showxgrad,
|
||||||
"showygrad": Settings.showygrad,
|
"showygrad": this.settings.showygrad,
|
||||||
"textsize": Settings.textsize,
|
"textsize": this.settings.textsize,
|
||||||
"history": History.serialize(),
|
"history": History.serialize(),
|
||||||
"width": this.#rootElement.width,
|
"width": this.rootElement.width,
|
||||||
"height": this.#rootElement.height,
|
"height": this.rootElement.height,
|
||||||
"objects": objs,
|
"objects": objs,
|
||||||
"type": "logplotv1"
|
"type": "logplotv1"
|
||||||
}
|
}
|
||||||
Helper.write(filename, JSON.stringify(settings))
|
Helper.write(filename, JSON.stringify(settings))
|
||||||
this.#alert.show(qsTranslate("io", "Saved plot to '%1'.").arg(filename.split("/").pop()))
|
this.alert.show(qsTranslate("io", "Saved plot to '%1'.").arg(filename.split("/").pop()))
|
||||||
this.#saved = true
|
History.history.saved = true
|
||||||
this.emit(new SavedEvent())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -135,32 +101,32 @@ class IOAPI extends Module {
|
||||||
if(!this.initialized) throw new Error("Attempting loadDiagram before initialize!")
|
if(!this.initialized) throw new Error("Attempting loadDiagram before initialize!")
|
||||||
if(!History.initialized) throw new Error("Attempting loadDiagram before history is initialized!")
|
if(!History.initialized) throw new Error("Attempting loadDiagram before history is initialized!")
|
||||||
let basename = filename.split("/").pop()
|
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 data = JSON.parse(Helper.load(filename))
|
||||||
let error = ""
|
let error = ""
|
||||||
if(data.hasOwnProperty("type") && data["type"] === "logplotv1") {
|
if(data.hasOwnProperty("type") && data["type"] === "logplotv1") {
|
||||||
History.clear()
|
History.clear()
|
||||||
// Importing settings
|
// Importing settings
|
||||||
Settings.set("saveFilename", filename, false)
|
this.settings.saveFilename = filename
|
||||||
Settings.set("xzoom", parseFloat(data["xzoom"]) || 100, false)
|
this.settings.xzoom = parseFloat(data["xzoom"]) || 100
|
||||||
Settings.set("yzoom", parseFloat(data["yzoom"]) || 10, false)
|
this.settings.yzoom = parseFloat(data["yzoom"]) || 10
|
||||||
Settings.set("xmin", parseFloat(data["xmin"]) || 5 / 10, false)
|
this.settings.xmin = parseFloat(data["xmin"]) || 5 / 10
|
||||||
Settings.set("ymax", parseFloat(data["ymax"]) || 24, false)
|
this.settings.ymax = parseFloat(data["ymax"]) || 24
|
||||||
Settings.set("xaxisstep", data["xaxisstep"] || "4", false)
|
this.settings.xaxisstep = data["xaxisstep"] || "4"
|
||||||
Settings.set("yaxisstep", data["yaxisstep"] || "4", false)
|
this.settings.yaxisstep = data["yaxisstep"] || "4"
|
||||||
Settings.set("xlabel", data["xaxislabel"] || "", false)
|
this.settings.xlabel = data["xaxislabel"] || ""
|
||||||
Settings.set("ylabel", data["yaxislabel"] || "", false)
|
this.settings.ylabel = data["yaxislabel"] || ""
|
||||||
Settings.set("logscalex", data["logscalex"] === true, false)
|
this.settings.logscalex = data["logscalex"] === true
|
||||||
if("showxgrad" in data)
|
if("showxgrad" in data)
|
||||||
Settings.set("showxgrad", data["showxgrad"], false)
|
this.settings.showxgrad = data["showxgrad"]
|
||||||
if("showygrad" in data)
|
if("showygrad" in data)
|
||||||
Settings.set("showygrad", data["showygrad"], false)
|
this.settings.textsize = data["showygrad"]
|
||||||
if("linewidth" in data)
|
if("linewidth" in data)
|
||||||
Settings.set("linewidth", data["linewidth"], false)
|
this.settings.linewidth = data["linewidth"]
|
||||||
if("textsize" in data)
|
if("textsize" in data)
|
||||||
Settings.set("textsize", data["textsize"], false)
|
this.settings.textsize = data["textsize"]
|
||||||
this.#rootElement.height = parseFloat(data["height"]) || 500
|
this.rootElement.height = parseFloat(data["height"]) || 500
|
||||||
this.#rootElement.width = parseFloat(data["width"]) || 1000
|
this.rootElement.width = parseFloat(data["width"]) || 1000
|
||||||
|
|
||||||
// Importing objects
|
// Importing objects
|
||||||
Objects.currentObjects = {}
|
Objects.currentObjects = {}
|
||||||
|
@ -191,18 +157,20 @@ class IOAPI extends Module {
|
||||||
if("history" in data)
|
if("history" in data)
|
||||||
History.unserialize(...data["history"])
|
History.unserialize(...data["history"])
|
||||||
|
|
||||||
|
// Refreshing sidebar
|
||||||
|
this.rootElement.updateObjectsLists()
|
||||||
} else {
|
} else {
|
||||||
error = qsTranslate("io", "Invalid file provided.")
|
error = qsTranslate("io", "Invalid file provided.")
|
||||||
}
|
}
|
||||||
if(error !== "") {
|
if(error !== "") {
|
||||||
console.log(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
|
// TODO: Error handling
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.#alert.show(qsTranslate("io", "Loaded file '%1'.").arg(basename))
|
Canvas.redraw()
|
||||||
this.#saved = true
|
this.alert.show(qsTranslate("io", "Loaded file '%1'.").arg(basename))
|
||||||
this.emit(new LoadedEvent())
|
History.history.saved = true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,12 +17,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Module } from "./common.mjs"
|
import { Module } from "./common.mjs"
|
||||||
import { BaseEvent } from "../events.mjs"
|
|
||||||
import * as Instruction from "../lib/expr-eval/instruction.mjs"
|
import * as Instruction from "../lib/expr-eval/instruction.mjs"
|
||||||
import { escapeValue } from "../lib/expr-eval/expression.mjs"
|
import { escapeValue } from "../lib/expr-eval/expression.mjs"
|
||||||
import { HelperInterface, LatexInterface } from "./interface.mjs"
|
import { HelperInterface, LatexInterface } from "./interface.mjs"
|
||||||
|
|
||||||
const unicodechars = ["pi", "∞",
|
const unicodechars = [
|
||||||
"α", "β", "γ", "δ", "ε", "ζ", "η",
|
"α", "β", "γ", "δ", "ε", "ζ", "η",
|
||||||
"π", "θ", "κ", "λ", "μ", "ξ", "ρ",
|
"π", "θ", "κ", "λ", "μ", "ξ", "ρ",
|
||||||
"ς", "σ", "τ", "φ", "χ", "ψ", "ω",
|
"ς", "σ", "τ", "φ", "χ", "ψ", "ω",
|
||||||
|
@ -31,9 +30,9 @@ const unicodechars = ["pi", "∞",
|
||||||
"ₕ", "ₖ", "ₗ", "ₘ", "ₙ", "ₚ", "ₛ",
|
"ₕ", "ₖ", "ₗ", "ₘ", "ₙ", "ₚ", "ₛ",
|
||||||
"ₜ", "¹", "²", "³", "⁴", "⁵", "⁶",
|
"ₜ", "¹", "²", "³", "⁴", "⁵", "⁶",
|
||||||
"⁷", "⁸", "⁹", "⁰", "₁", "₂", "₃",
|
"⁷", "⁸", "⁹", "⁰", "₁", "₂", "₃",
|
||||||
"₄", "₅", "₆", "₇", "₈", "₉", "₀"
|
"₄", "₅", "₆", "₇", "₈", "₉", "₀",
|
||||||
]
|
"pi", "∞"]
|
||||||
const equivalchars = ["\\pi", "\\infty",
|
const equivalchars = [
|
||||||
"\\alpha", "\\beta", "\\gamma", "\\delta", "\\epsilon", "\\zeta", "\\eta",
|
"\\alpha", "\\beta", "\\gamma", "\\delta", "\\epsilon", "\\zeta", "\\eta",
|
||||||
"\\pi", "\\theta", "\\kappa", "\\lambda", "\\mu", "\\xi", "\\rho",
|
"\\pi", "\\theta", "\\kappa", "\\lambda", "\\mu", "\\xi", "\\rho",
|
||||||
"\\sigma", "\\sigma", "\\tau", "\\phi", "\\chi", "\\psi", "\\omega",
|
"\\sigma", "\\sigma", "\\tau", "\\phi", "\\chi", "\\psi", "\\omega",
|
||||||
|
@ -43,29 +42,7 @@ const equivalchars = ["\\pi", "\\infty",
|
||||||
"{}_{t}", "{}^{1}", "{}^{2}", "{}^{3}", "{}^{4}", "{}^{5}", "{}^{6}",
|
"{}_{t}", "{}^{1}", "{}^{2}", "{}^{3}", "{}^{4}", "{}^{5}", "{}^{6}",
|
||||||
"{}^{7}", "{}^{8}", "{}^{9}", "{}^{0}", "{}_{1}", "{}_{2}", "{}_{3}",
|
"{}^{7}", "{}^{8}", "{}^{9}", "{}^{0}", "{}_{1}", "{}_{2}", "{}_{3}",
|
||||||
"{}_{4}", "{}_{5}", "{}_{6}", "{}_{7}", "{}_{8}", "{}_{9}", "{}_{0}",
|
"{}_{4}", "{}_{5}", "{}_{6}", "{}_{7}", "{}_{8}", "{}_{9}", "{}_{0}",
|
||||||
]
|
"\\pi", "\\infty"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AsyncRenderStartedEvent extends BaseEvent {
|
|
||||||
constructor(markup, fontSize, color) {
|
|
||||||
super("async-render-started")
|
|
||||||
this.markup = markup
|
|
||||||
this.fontSize = fontSize
|
|
||||||
this.color = color
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class AsyncRenderFinishedEvent extends BaseEvent {
|
|
||||||
constructor(markup, fontSize, color) {
|
|
||||||
super("async-render-finished")
|
|
||||||
this.markup = markup
|
|
||||||
this.fontSize = fontSize
|
|
||||||
this.color = color
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class containing the result of a LaTeX render.
|
* Class containing the result of a LaTeX render.
|
||||||
|
@ -83,11 +60,6 @@ class LatexRenderResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
class LatexAPI extends Module {
|
class LatexAPI extends Module {
|
||||||
static emits = ["async-render-started", "async-render-finished"]
|
|
||||||
|
|
||||||
/** @type {LatexInterface} */
|
|
||||||
#latex = null
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("Latex", {
|
super("Latex", {
|
||||||
latex: LatexInterface,
|
latex: LatexInterface,
|
||||||
|
@ -97,7 +69,6 @@ class LatexAPI extends Module {
|
||||||
* true if latex has been enabled by the user, false otherwise.
|
* true if latex has been enabled by the user, false otherwise.
|
||||||
*/
|
*/
|
||||||
this.enabled = false
|
this.enabled = false
|
||||||
this.promises = new Set()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -106,8 +77,9 @@ class LatexAPI extends Module {
|
||||||
*/
|
*/
|
||||||
initialize({ latex, helper }) {
|
initialize({ latex, helper }) {
|
||||||
super.initialize({ latex, helper })
|
super.initialize({ latex, helper })
|
||||||
this.#latex = latex
|
this.latex = latex
|
||||||
this.enabled = helper.getSetting("enable_latex")
|
this.helper = helper
|
||||||
|
this.enabled = helper.getSettingBool("enable_latex")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,7 +93,7 @@ class LatexAPI extends Module {
|
||||||
*/
|
*/
|
||||||
findPrerendered(markup, fontSize, color) {
|
findPrerendered(markup, fontSize, color) {
|
||||||
if(!this.initialized) throw new Error("Attempting findPrerendered before initialize!")
|
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
|
let ret = null
|
||||||
if(data !== "")
|
if(data !== "")
|
||||||
ret = new LatexRenderResult(...data.split(","))
|
ret = new LatexRenderResult(...data.split(","))
|
||||||
|
@ -138,19 +110,7 @@ class LatexAPI extends Module {
|
||||||
*/
|
*/
|
||||||
async requestAsyncRender(markup, fontSize, color) {
|
async requestAsyncRender(markup, fontSize, color) {
|
||||||
if(!this.initialized) throw new Error("Attempting requestAsyncRender before initialize!")
|
if(!this.initialized) throw new Error("Attempting requestAsyncRender before initialize!")
|
||||||
let render
|
let args = this.latex.render(markup, fontSize, color).split(",")
|
||||||
if(this.#latex.supportsAsyncRender) {
|
|
||||||
this.emit(new AsyncRenderStartedEvent(markup, fontSize, color))
|
|
||||||
// Storing promise so that it does not get dereferenced.
|
|
||||||
const promise = this.#latex.renderAsync(markup, fontSize, color)
|
|
||||||
this.promises.add(promise)
|
|
||||||
render = await promise
|
|
||||||
this.promises.delete(promise)
|
|
||||||
this.emit(new AsyncRenderFinishedEvent(markup, fontSize, color))
|
|
||||||
} else {
|
|
||||||
render = this.#latex.renderSync(markup, fontSize, color)
|
|
||||||
}
|
|
||||||
const args = render.split(",")
|
|
||||||
return new LatexRenderResult(...args)
|
return new LatexRenderResult(...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,10 +135,9 @@ class LatexAPI extends Module {
|
||||||
*/
|
*/
|
||||||
parif(elem, contents) {
|
parif(elem, contents) {
|
||||||
elem = elem.toString()
|
elem = elem.toString()
|
||||||
const contains = contents.some(x => elem.indexOf(x) > 0)
|
if(elem[0] !== "(" && elem[elem.length - 1] !== ")" && contents.some(x => elem.indexOf(x) > 0))
|
||||||
if(contains && (elem[0] !== "(" || elem.at(-1) !== ")"))
|
|
||||||
return this.par(elem)
|
return this.par(elem)
|
||||||
if(!contains && elem[0] === "(" && elem.at(-1) === ")")
|
if(elem[0] === "(" && elem[elem.length - 1] === ")")
|
||||||
return elem.removeEnclosure()
|
return elem.removeEnclosure()
|
||||||
return elem
|
return elem
|
||||||
}
|
}
|
||||||
|
@ -196,21 +155,20 @@ class LatexAPI extends Module {
|
||||||
if(args.length === 3)
|
if(args.length === 3)
|
||||||
return `\\frac{d${args[0].removeEnclosure().replaceAll(args[1].removeEnclosure(), "x")}}{dx}`
|
return `\\frac{d${args[0].removeEnclosure().replaceAll(args[1].removeEnclosure(), "x")}}{dx}`
|
||||||
else
|
else
|
||||||
return `\\frac{d${args[0]}}{dx}(${args[1]})`
|
return `\\frac{d${args[0]}}{dx}(x)`
|
||||||
case "integral":
|
case "integral":
|
||||||
if(args.length === 4)
|
if(args.length === 4)
|
||||||
return `\\int\\limits_{${args[0]}}^{${args[1]}}${args[2].removeEnclosure()} d${args[3].removeEnclosure()}`
|
return `\\int\\limits_{${args[0]}}^{${args[1]}}${args[2].removeEnclosure()} d${args[3].removeEnclosure()}`
|
||||||
else
|
else
|
||||||
return `\\int\\limits_{${args[0]}}^{${args[1]}}${args[2]}(t) dt`
|
return `\\int\\limits_{${args[0]}}^{${args[1]}}${args[2]}(t) dt`
|
||||||
case "sqrt":
|
case "sqrt":
|
||||||
const arg = this.parif(args.join(", "), [])
|
return `\\sqrt\\left(${args.join(", ")}\\right)`
|
||||||
return `\\sqrt{${arg}}`
|
|
||||||
case "abs":
|
case "abs":
|
||||||
return `\\left|${args.join(", ")}\\right|`
|
return `\\left|${args.join(", ")}\\right|`
|
||||||
case "floor":
|
case "floor":
|
||||||
return `\\left\\lfloor{${args.join(", ")}}\\right\\rfloor`
|
return `\\left\\lfloor${args.join(", ")}\\right\\rfloor`
|
||||||
case "ceil":
|
case "ceil":
|
||||||
return `\\left\\lceil{${args.join(", ")}}\\right\\rceil`
|
return `\\left\\lceil${args.join(", ")}\\right\\rceil`
|
||||||
default:
|
default:
|
||||||
return `\\mathrm{${f}}\\left(${args.join(", ")}\\right)`
|
return `\\mathrm{${f}}\\left(${args.join(", ")}\\right)`
|
||||||
}
|
}
|
||||||
|
@ -224,17 +182,16 @@ class LatexAPI extends Module {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
variable(vari, wrapIn$ = false) {
|
variable(vari, wrapIn$ = false) {
|
||||||
if(wrapIn$) {
|
if(wrapIn$)
|
||||||
for(let i = 0; i < unicodechars.length; i++) {
|
for(let i = 0; i < unicodechars.length; i++) {
|
||||||
if(vari.includes(unicodechars[i]))
|
if(vari.includes(unicodechars[i]))
|
||||||
vari = vari.replaceAll(unicodechars[i], "$" + equivalchars[i] + "$")
|
vari = vari.replaceAll(unicodechars[i], "$" + equivalchars[i] + "$")
|
||||||
}
|
}
|
||||||
} else {
|
else
|
||||||
for(let i = 0; i < unicodechars.length; i++) {
|
for(let i = 0; i < unicodechars.length; i++) {
|
||||||
if(vari.includes(unicodechars[i]))
|
if(vari.includes(unicodechars[i]))
|
||||||
vari = vari.replaceAll(unicodechars[i], equivalchars[i])
|
vari = vari.replaceAll(unicodechars[i], equivalchars[i])
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return vari
|
return vari
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +259,7 @@ class LatexAPI extends Module {
|
||||||
throw new EvalError("Unknown operator " + item.value + ".")
|
throw new EvalError("Unknown operator " + item.value + ".")
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case Instruction.IOP3: // Ternary operator
|
case Instruction.IOP3: // Thirdiary operator
|
||||||
n3 = nstack.pop()
|
n3 = nstack.pop()
|
||||||
n2 = nstack.pop()
|
n2 = nstack.pop()
|
||||||
n1 = nstack.pop()
|
n1 = nstack.pop()
|
||||||
|
@ -329,7 +286,7 @@ class LatexAPI extends Module {
|
||||||
nstack.push(this.parif(n1, ["+", "-", "*", "/", "^"]) + "!")
|
nstack.push(this.parif(n1, ["+", "-", "*", "/", "^"]) + "!")
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
nstack.push(this.functionToLatex(f, [this.parif(n1, ["+", "-", "*", "/", "^"])]))
|
nstack.push(f + this.parif(n1, ["+", "-", "*", "/", "^"]))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -364,6 +321,9 @@ class LatexAPI extends Module {
|
||||||
throw new EvalError("invalid Expression")
|
throw new EvalError("invalid Expression")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(nstack.length > 1) {
|
||||||
|
nstack = [nstack.join(";")]
|
||||||
|
}
|
||||||
return String(nstack[0])
|
return String(nstack[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,17 +17,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Module } from "./common.mjs"
|
import { Module } from "./common.mjs"
|
||||||
import { textsub } from "../utils/index.mjs"
|
import { textsub } from "../utils.mjs"
|
||||||
|
|
||||||
class ObjectsAPI extends Module {
|
class ObjectsAPI extends Module {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("Objects")
|
super("Objects")
|
||||||
|
|
||||||
/**
|
|
||||||
* List of object constructors.
|
|
||||||
* @type {Object.<string,typeof DrawableObject>}
|
|
||||||
*/
|
|
||||||
this.types = {}
|
this.types = {}
|
||||||
/**
|
/**
|
||||||
* List of objects for each type of object.
|
* 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.
|
* @param {string} newName - Name to rename the object to.
|
||||||
*/
|
*/
|
||||||
renameObject(oldName, newName) {
|
renameObject(oldName, newName) {
|
||||||
const obj = this.currentObjectsByName[oldName]
|
let obj = this.currentObjectsByName[oldName]
|
||||||
delete this.currentObjectsByName[oldName]
|
delete this.currentObjectsByName[oldName]
|
||||||
this.currentObjectsByName[newName] = obj
|
this.currentObjectsByName[newName] = obj
|
||||||
obj.name = newName
|
obj.name = newName
|
||||||
|
@ -80,7 +76,7 @@ class ObjectsAPI extends Module {
|
||||||
* @param {string} objName - Current name of the object.
|
* @param {string} objName - Current name of the object.
|
||||||
*/
|
*/
|
||||||
deleteObject(objName) {
|
deleteObject(objName) {
|
||||||
const obj = this.currentObjectsByName[objName]
|
let obj = this.currentObjectsByName[objName]
|
||||||
if(obj !== undefined) {
|
if(obj !== undefined) {
|
||||||
this.currentObjects[obj.type].splice(this.currentObjects[obj.type].indexOf(obj), 1)
|
this.currentObjects[obj.type].splice(this.currentObjects[obj.type].indexOf(obj), 1)
|
||||||
obj.delete()
|
obj.delete()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,9 +20,6 @@ import General from "../preferences/general.mjs"
|
||||||
import Editor from "../preferences/expression.mjs"
|
import Editor from "../preferences/expression.mjs"
|
||||||
import DefaultGraph from "../preferences/default.mjs"
|
import DefaultGraph from "../preferences/default.mjs"
|
||||||
|
|
||||||
/**
|
|
||||||
* Module for application wide settings.
|
|
||||||
*/
|
|
||||||
class PreferencesAPI extends Module {
|
class PreferencesAPI extends Module {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("Preferences")
|
super("Preferences")
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -63,7 +63,7 @@ export default class BodePhase extends ExecutableObject {
|
||||||
// Create new point
|
// Create new point
|
||||||
om_0 = Objects.createNewRegisteredObject("Point", [Objects.getNewName("ω"), this.color, "name"])
|
om_0 = Objects.createNewRegisteredObject("Point", [Objects.getNewName("ω"), this.color, "name"])
|
||||||
om_0.labelPosition = this.phase.execute() >= 0 ? "above" : "below"
|
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"
|
labelPosition = "below"
|
||||||
}
|
}
|
||||||
om_0.requiredBy.push(this)
|
om_0.requiredBy.push(this)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,9 +16,9 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { getRandomColor } from "../utils.mjs"
|
||||||
import Objects from "../module/objects.mjs"
|
import Objects from "../module/objects.mjs"
|
||||||
import Latex from "../module/latex.mjs"
|
import Latex from "../module/latex.mjs"
|
||||||
import { getRandomColor } from "../utils/index.mjs"
|
|
||||||
import { ensureTypeSafety, serializesByPropertyType } from "../parameters.mjs"
|
import { ensureTypeSafety, serializesByPropertyType } from "../parameters.mjs"
|
||||||
|
|
||||||
// This file contains the default data to be imported from all other objects
|
// This file contains the default data to be imported from all other objects
|
||||||
|
@ -224,7 +224,7 @@ export class DrawableObject {
|
||||||
currentObjectsByName[objName].requiredBy.push(this)
|
currentObjectsByName[objName].requiredBy.push(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(this[property].canBeCached && this[property].requiredObjects().length > 0)
|
if(this[property].cached && this[property].requiredObjects().length > 0)
|
||||||
// Recalculate
|
// Recalculate
|
||||||
this[property].recache()
|
this[property].recache()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
* Copyright (C) 2021-2025 Ad5001
|
* Copyright (C) 2021-2024 Ad5001
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|