Compare commits
458 commits
1142ca1c00
...
master
Author | SHA1 | Date | |
---|---|---|---|
fdcc8105ba | |||
da8aa59b0a | |||
f5b489ef44 | |||
8429ff3739 | |||
440575325e | |||
45fef876ec | |||
43e41a5da4 | |||
687b14429a | |||
|
49e94317d4 | ||
727dda2623 | |||
27c9fe0473 | |||
e6de739d0c | |||
f52ee65c56 | |||
8da10497d2 | |||
5d0542ffcc | |||
90f4691c54 | |||
c39498c60f | |||
2594fd6844 | |||
a01b7a17ef | |||
67799e9908 | |||
d53f50193a | |||
14e8cef6af | |||
b989a685e9 | |||
e35f6cebec | |||
6251835aa0 | |||
14c092b9fa | |||
811262b1fb | |||
3c0d99d9c0 | |||
2899ac6cde | |||
a182c703f4 | |||
ef465b34e7 | |||
8fab9d8e52 | |||
34caf20593 | |||
a85a4721e3 | |||
aeaaba759f | |||
ccddb068a6 | |||
37ac400f23 | |||
5313428250 | |||
cf73b35a9a | |||
f734e40ad9 | |||
b33e1329db | |||
2995b2271a | |||
a26dbc8a00 | |||
89e78913de | |||
c03afdf4ee | |||
3a81441d0b | |||
edf4518494 | |||
345458f453 | |||
974baa6cc2 | |||
4c1b705240 | |||
885d1f5dc3 | |||
0abb22130f | |||
42d5add810 | |||
e2d259f866 | |||
8a878b4cc1 | |||
07e58a3a55 | |||
c592b92212 | |||
7935d0134d | |||
5745587c72 | |||
84adc787e5 | |||
f3307b47d9 | |||
9017f84c06 | |||
00ab895b21 | |||
82e6d2ffe3 | |||
b91dbfb311 | |||
448d94fee3 | |||
2dc9234b22 | |||
54363b25bc | |||
52f859349a | |||
d1ac70a946 | |||
f4920aadb6 | |||
af2950c3d2 | |||
|
bd346240bd | ||
9663c33563 | |||
934dd3ea1b | |||
b02ed87a29 | |||
40d86c8f82 | |||
6b3cce4252 | |||
8c273f4220 | |||
a60ac79d83 | |||
23c3b771c2 | |||
041d4f424e | |||
1fc19f6ba3 | |||
|
7ef55e48e8 | ||
07e556da56 | |||
cd6f258720 | |||
1c7e9d627d | |||
e2841c0129 | |||
ca5c7492dc | |||
34cb856dd4 | |||
e9d204daab | |||
a2443d7915 | |||
56a0817960 | |||
c74c2fb747 | |||
c2eae30bd6 | |||
80cea6d280 | |||
f8ce98d4ad | |||
c806f09b10 | |||
c32d70e9ed | |||
8cefc56ac7 | |||
850d076268 | |||
95b47effdf | |||
49aa23de92 | |||
4a1b333198 | |||
b5bdbb6294 | |||
309b0fafb0 | |||
fbb85083c1 | |||
f9af0c34dd | |||
|
17b6e40d60 | ||
937cb07d0b | |||
6a1f01ba1f | |||
4c1403c983 | |||
8c8964e75e | |||
45dff33bb5 | |||
bac802ec5b | |||
34a436fe49 | |||
cb2ffcf77a | |||
a27d3109ed | |||
a998c52eec | |||
8b8a280b37 | |||
3befff41fa | |||
9810435456 | |||
84a65cd1fc | |||
5bdf81b2ed | |||
c66d08b352 | |||
a4e9ad7f5a | |||
a88be86350 | |||
4baf4f8aa6 | |||
1f2b8e5c4b | |||
|
8eac624bb2 | ||
976088ad03 | |||
dcb63c48e9 | |||
dcc47104ef | |||
a32d480b43 | |||
e0601379ba | |||
3c42cbb6d0 | |||
cc0f277da7 | |||
1299fe469d | |||
5d0f3eec56 | |||
|
c8ada79776 | ||
43a0aa3529 | |||
9c76d469d6 | |||
3c57f207b6 | |||
77d03e5837 | |||
ed5b7e95a6 | |||
2bf7df12a8 | |||
0e23d9181e | |||
7e698eda32 | |||
6e92c428f9 | |||
b18e1ca56e | |||
1ef1d27452 | |||
7c12b757ee | |||
484888f80f | |||
4e265b66c2 | |||
6d3f4ba372 | |||
2321a1076c | |||
dd2ae7a2c8 | |||
2fc9bdee86 | |||
67190e1b4c | |||
8ab461ff72 | |||
9b5356f8e7 | |||
370402f303 | |||
7f57ed13c7 | |||
325eef57e2 | |||
956de5f9e3 | |||
48427b7923 | |||
fceb5e711c | |||
836d13386e | |||
c3daa92280 | |||
d566c285fd | |||
a9e47dbc17 | |||
4a5756f24d | |||
a250f532d9 | |||
e68411e93c | |||
b55b2a11fe | |||
7dd64c8e31 | |||
8f95479689 | |||
9712ff15bb | |||
a3cf99f3af | |||
2ee7da7995 | |||
3104dea918 | |||
cb0db7fae1 | |||
4ac7de48b0 | |||
5211821a84 | |||
29e48e284c | |||
4759bdcd33 | |||
9072e94d14 | |||
b50d56d511 | |||
1bf175b09c | |||
52fd95551c | |||
769ad22ea6 | |||
|
8b36ad81ab | ||
1e32faa1d1 | |||
78d7e6f310 | |||
ab45109206 | |||
12b48a2e74 | |||
1bc3aaf53b | |||
b52cc1de29 | |||
214dd687b1 | |||
6b4da2b061 | |||
|
bcf76dcd28 | ||
207a2254f3 | |||
0f39930b88 | |||
d4e97f2860 | |||
a2fa16949a | |||
91e4220397 | |||
d7fe760900 | |||
a16f02fd5f | |||
601efc6122 | |||
51807a80d0 | |||
ef14db8bbb | |||
7e0262e4fe | |||
70f1c03cb6 | |||
8d6891c4f0 | |||
c9a597ea82 | |||
c1a468148d | |||
59d4d2f728 | |||
|
9ca48bbcfa | ||
3994d8d49d | |||
53124fc8d7 | |||
35fef4cb1e | |||
f1e2278695 | |||
|
6a1f0013d6 | ||
6626362d23 | |||
107cea1308 | |||
7b0bc4469f | |||
e3eea751cb | |||
7b76a8fe08 | |||
4b1cf2cd9d | |||
88797d00be | |||
f98852c336 | |||
606ec428ab | |||
|
136ac4c7bc | ||
|
87d8882db1 | ||
915c6b5246 | |||
54f9802975 | |||
997a1645a0 | |||
fefb0f92b0 | |||
665906ecb3 | |||
781a0f4aae | |||
5f8c756dc7 | |||
8e75f13e9a | |||
82e8413e56 | |||
73cba85592 | |||
861bb001c9 | |||
08fea34366 | |||
a6fcf6da19 | |||
f730121047 | |||
80f1077b35 | |||
33c790b113 | |||
f9b0bb99ce | |||
557592708f | |||
1f72c76fb4 | |||
66b4c8df90 | |||
70b1ac09f7 | |||
2d6389bfd5 | |||
d0851b819f | |||
cff994d9d7 | |||
fde2526c54 | |||
e5fe8afd06 | |||
f9a7443631 | |||
874046960f | |||
d53573d468 | |||
1da1221c51 | |||
|
8bb147cbee | ||
bd2e692285 | |||
298acaace2 | |||
5284c71ee7 | |||
b8d679e118 | |||
22106f97b5 | |||
bf211df67d | |||
803416d08d | |||
d991deee7b | |||
ed4d30573c | |||
3f1d089a78 | |||
4b692894f2 | |||
062fde6b16 | |||
2b5d442d3a | |||
038dd9f4a8 | |||
cf754a7a34 | |||
bc35b18da0 | |||
7542d63121 | |||
4fe1086d68 | |||
a66ccd1319 | |||
9879e7fbc9 | |||
999999832a | |||
7408520819 | |||
9f2b08b938 | |||
3039aade29 | |||
d6a83b0f4b | |||
0b5aa36c23 | |||
25347a8eff | |||
|
d6d7f3a511 | ||
e67619771b | |||
df3e7d4796 | |||
19e403ce98 | |||
273d5539ec | |||
5e7dbb8fa6 | |||
1e30bee220 | |||
ebf07dccfa | |||
95546bd14e | |||
59145905ea | |||
66da751b36 | |||
deb4355756 | |||
8358cc142c | |||
62e70885e0 | |||
6fa0aea0e5 | |||
08edd7be14 | |||
16effe064c | |||
98f01845e1 | |||
87b4787a2b | |||
a610d0949e | |||
803670635b | |||
09c1a44150 | |||
40743e54c3 | |||
9239eac78a | |||
35ce1c4824 | |||
75e70903f1 | |||
016a21ecb4 | |||
f7dd40d2f5 | |||
d1843b455a | |||
6abbbe5e61 | |||
5cef8e23fa | |||
d6dee6a2be | |||
3da51430f1 | |||
ae930be621 | |||
abae3ef94b | |||
82d53748b3 | |||
fce2a5ba0d | |||
3904dc0217 | |||
7a80455e25 | |||
20c1ed005e | |||
d8534d1e0d | |||
4be92a07e3 | |||
b143f7e10d | |||
0ac690d0c1 | |||
37150b5823 | |||
0539f988e2 | |||
bf067b7e7c | |||
9f20a80228 | |||
5ef8cac1c0 | |||
7ec80e6682 | |||
43d78e17e5 | |||
93308f2bfa | |||
508f316bc5 | |||
424eef6e17 | |||
f40c242877 | |||
aecc02c606 | |||
78ffc8c645 | |||
98f26d4919 | |||
65c42abd26 | |||
ba98fc3611 | |||
839f8e9f36 | |||
fe13351e99 | |||
225ae67834 | |||
6307855b87 | |||
3a36e41738 | |||
ee26bc04c5 | |||
|
02c7cb8afc | ||
0a064694f5 | |||
b0b77834a8 | |||
fb1c4c0de7 | |||
d969661b33 | |||
86656fe46c | |||
4b6d333cee | |||
9ca8903a3e | |||
a3faeeb7d0 | |||
244935d7e2 | |||
677d13278d | |||
7241add9e5 | |||
356169c749 | |||
7e427e5bb9 | |||
3cd4ad6a20 | |||
0e41c12e03 | |||
fc68d3da6a | |||
0561782293 | |||
47a9f63d66 | |||
7f548796f2 | |||
15b87fc15d | |||
77ae54fa18 | |||
5da8dcefe5 | |||
16efe31b5f | |||
6e21f2eea1 | |||
c85dba737d | |||
218c55b491 | |||
e98be96b0d | |||
c5851e6d95 | |||
de1be925b0 | |||
ddb5156396 | |||
7686204786 | |||
acadad2bd1 | |||
1ceccc15cf | |||
97338506a4 | |||
e7ba1d9ff5 | |||
b519e34016 | |||
dc532fcd19 | |||
971a9d10d2 | |||
4564d5446f | |||
32db56304b | |||
64d1419452 | |||
fcf5ef9539 | |||
6116ffe4e7 | |||
2a8cab9398 | |||
29e62fee6e | |||
d65f343b60 | |||
f76b601139 | |||
9facc2389c | |||
15fd660e0c | |||
dfdd576296 | |||
b8d312bb23 | |||
8c6784663e | |||
1a433eba27 | |||
1ba594c4f7 | |||
6b535dd8a2 | |||
fad5325501 | |||
3dc69cc9ba | |||
2d46de35d2 | |||
|
ef04b2aa58 | ||
|
876ea1eaa8 | ||
20f8d68f13 | |||
a5ce17e7f3 | |||
cf3e775834 | |||
cb733a556c | |||
22c9151e0c | |||
871630ef0b | |||
6b2afd3a1b | |||
b496807576 | |||
b45e105202 | |||
6129bcf928 | |||
b69aaa2d7a | |||
1b91176f2d | |||
3a2fcb1be1 | |||
836e6d8922 | |||
eef42655e5 | |||
1f581c46ec | |||
f77b1ce331 | |||
86f3399a53 | |||
3e7b36a420 | |||
27759362cb | |||
7120e3a781 | |||
166d1a2485 | |||
2691ba687f | |||
4c9c9668bb | |||
b72bbeab4f | |||
7723107ff6 | |||
6539ca8caa | |||
12ecf3b19b | |||
07ae71de36 | |||
06bb00cc17 | |||
ec90779912 | |||
2ce66df4dd | |||
8251504fbe | |||
650e43894c | |||
de0220fecf | |||
c4ffcdaa35 | |||
0975189615 | |||
ccf3de5783 | |||
b5600046e9 |
37
.gitignore
vendored
|
@ -1,33 +1,46 @@
|
|||
# Building
|
||||
build/
|
||||
dist/
|
||||
deb_dist/
|
||||
linux/flatpak/AppDir
|
||||
linux/flatpak/repo
|
||||
linux/flatpak/build-dir
|
||||
linux/flatpak/.flatpak-builder
|
||||
assets/linux/flatpak/AppDir
|
||||
assets/linux/flatpak/repo
|
||||
assets/linux/flatpak/build-dir
|
||||
assets/linux/flatpak/.flatpak-builder
|
||||
*.snap
|
||||
*.spec
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.spec
|
||||
*.egg-info/
|
||||
|
||||
# Runtime data
|
||||
**/**.qmlc
|
||||
**/**.jsc
|
||||
**/**.pyc
|
||||
**/**.qm
|
||||
**/**.log
|
||||
*.jsc
|
||||
*.qmlc
|
||||
*.log
|
||||
**/*.dxvk-cache
|
||||
.DS_Store
|
||||
**/.DS_Store
|
||||
**/__pycache__/
|
||||
|
||||
# IDE Data
|
||||
.ropeproject
|
||||
.vscode
|
||||
build
|
||||
*.kdev4
|
||||
.kdev4
|
||||
docs/html
|
||||
.directory
|
||||
*.kdev4
|
||||
*.lpf
|
||||
*.lgg
|
||||
*.spec
|
||||
.kdev4
|
||||
AccountFree.pro
|
||||
AccountFree.pro.user
|
||||
*.egg-info/
|
||||
*.tar.gz
|
||||
|
||||
# Tests
|
||||
common/coverage/
|
||||
**/.coverage
|
||||
|
||||
# npm
|
||||
common/node_modules
|
||||
runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/index.mjs*
|
||||
|
|
7
.gitmodules
vendored
|
@ -1,6 +1,3 @@
|
|||
[submodule "LogarithmPlotter/qml/eu/ad5001/MixedMenu"]
|
||||
path = LogarithmPlotter/qml/eu/ad5001/MixedMenu
|
||||
[submodule "runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/MixedMenu"]
|
||||
path = runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/MixedMenu
|
||||
url = https://git.ad5001.eu/Ad5001/MixedMenu
|
||||
[submodule "linux/flatpak"]
|
||||
path = linux/flatpak
|
||||
url = https://github.com/Ad5001/eu.ad5001.LogarithmPlotter
|
||||
|
|
157
CHANGELOG.md
|
@ -1,5 +1,162 @@
|
|||
# Changelog
|
||||
|
||||
## v0.5.0 (11 Jan 2024)
|
||||
|
||||
**New**
|
||||
|
||||
* New, reworked application icon.
|
||||
* Graph is now mouse interactive:
|
||||
* You can now drag to move and scroll to zoom!
|
||||
* Builtin functions now provide usage when used in the autocomplete of the expression editor.
|
||||
|
||||
**Changes**
|
||||
|
||||
* When creating an object that can be positioned, new default behavior is to pick first instead of opening object settings.
|
||||
* Icons with text now use the SVG's text element, allowing them to integrate better with the system's default font.
|
||||
* Special characters popup is now context aware (e.g. no sub/supscript symbols in expressions).
|
||||
* New symbols in special characters popup.
|
||||
* Integrals and derivatives can now be provided with an executable object (e.g. Functions) instead of strings as function.
|
||||
* New description on Linux.
|
||||
|
||||
**Fixed bugs**
|
||||
|
||||
* Fixing ∞ 'variable' in domains and expressions.
|
||||
* Several other bugs related to constants in expresions were fixed as well.
|
||||
* Builtin functions now send an error message when not provided with the proper arguments.
|
||||
|
||||
**Internal changes**
|
||||
|
||||
* Updated to PySide6 v6.6.1.
|
||||
* Reworked continuous functions' rendering to make it faster.
|
||||
* Removed old bits from an unfinished new parser that weren't used.
|
||||
|
||||
## v0.4.0 (27 May 2023)
|
||||
|
||||
**Changes**
|
||||
|
||||
* Fully ported to PySide6 (Qt6).
|
||||
* Greet screen settings are now scrollable.
|
||||
* Changelog is now freezed to current version.
|
||||
|
||||
**New**
|
||||
|
||||
* Customizable color schemes for expressions.
|
||||
* New, rewamped and improved picked location overlay settings:
|
||||
* It's now possible to disable picking x or y when setting a location.
|
||||
* Properties which are related to positioning (X, Y, Label's X position) can now be set using the picker.
|
||||
* Visual redesign that enhances readability of settings.
|
||||
* There is now a button to hide picker settings.
|
||||
|
||||
**Fixed bugs**
|
||||
|
||||
* Cursors in expression are now easier to see.
|
||||
* Symbols in LaTeX rendered Texts cause the LaTeX renderer to crash.
|
||||
* Underscores in distribution names are automatically removed if the name is modified.
|
||||
* Autocomplete categories now properly respect theme colors.
|
||||
* Functions in expressions (like indexOf, map...) now properly send errors when the arguments are of the wrong type or count.
|
||||
* Executable Objects called (like functions, bode magnitures, phases...) now send an error if provided with no arguments.
|
||||
* Function calls with no argument no longer make LogarithmPlotter crash under certain circumstances.
|
||||
* Thank you dialog's lists are no longer draggable.
|
||||
|
||||
**Internal changes**
|
||||
|
||||
* A lot of inner changes led by porting to Qt6, fixing a lot of bugs at the same time.
|
||||
* Disabled auto detect of visual theme if the `QT_QUICK_CONTROLS_STYLE` environment variable is set.
|
||||
* (macOS, Windows, Flatpak) Drastically reducing installer sizes (more than halved).
|
||||
* (Launchpad/Ubuntu) Using custom built packages of PySide6, meaning smaller installation and distro dependency.
|
||||
|
||||
## v0.3.0 (28 Oct 2022)
|
||||
|
||||
**New**
|
||||
|
||||
* New completely revamped expression editor:
|
||||
* Automatic closing of parentheses and brackets (can be disabled in settings).
|
||||
* Syntax highlighting (can be disabled in the settings).
|
||||
* Autocompletion is now available (for functions, variables and constants, object names and properties) (can be disabled in the settings).
|
||||
* Object properties can now be used in expressions (e.g. if you have a point named A, you can use A.x to access its x value).
|
||||
* Executable objects can be now be used in expressions (e.g. if you have a function named 'f', it's accessible using `f(<value>)`).
|
||||
* LaTeX-rendered formulas are now used in the Objects and History tabs when LaTeX rendering is enabled.
|
||||
* Errors in formulas are now reported in message boxes.
|
||||
|
||||
**Changes**
|
||||
|
||||
* The Object Editor dialog has been completely reworked internally, resulting in notable performance improvements.
|
||||
* Vast improvements to the objects system: names can no longer be shared amongst different objects.
|
||||
* Disabled access to custom variable and function definition in expressions (can cause issues and vulnerabilities)
|
||||
* When using the set position cursor, the position change is now saved a single history action.
|
||||
* Distribution are now prefixed with an 'F_' to prevent confusion with X Cursors.
|
||||
|
||||
**Added translations**
|
||||
|
||||
* Autocompletion categories (English, French, German, Hungarian).
|
||||
* Expression editor settings (English, French, German, Hungarian).
|
||||
* Expression syntax errors (English, French, German, Hungarian).
|
||||
* On top of the above:
|
||||
* Hungarian: v0.2.0 added text (thanks @ovari!)
|
||||
* Spanish: Menu bars (thanks @Sergio Varela)
|
||||
* You can contribute to translation on [Weblate](https://hosted.weblate.org/projects/logarithmplotter/logarithmplotter/#repository).
|
||||
|
||||
**Fixed bugs**
|
||||
|
||||
* Fixing Texts not being properly recognized as texts when saving.
|
||||
* Text's 'Disable LaTeX' property is now properly saved.
|
||||
* X Cursors LaTeX rendering made the app crash.
|
||||
* Attempting to insert special character no longer automatically saves the expression you're editing.
|
||||
* Proper HDPI support for icons and buttons (note: HDPI is not available for the rendered canvas yet).
|
||||
* Support for non-latin characters in variables (e.g. greek letters, subtext, suptext)
|
||||
* Silent error when misentering variable names in the expression editor causing internal issues.
|
||||
* Fixing some utils function simplifying parentheses when they shouldn't have.
|
||||
* (flatpak and KDE SDK) Fixing the sometimes invisible buttons on the objects tab on startup.
|
||||
* (macos) Application string version does not match LogarithmPlotter's version.
|
||||
* (debian) (Normally) Fixing deb building.
|
||||
|
||||
**Internal changes**
|
||||
|
||||
* Object dependencies are now registered on both the dependant object, and the object it's depending on.
|
||||
* Objects now have a proper per-name registry.
|
||||
* Object Editor Dialog has been reworked to use loaders insteads.
|
||||
* Reworked the file loading system to be able to load dependencies properly.
|
||||
|
||||
|
||||
## v0.2.0 (22 Apr 2022)
|
||||
|
||||
**New**
|
||||
|
||||
* (EXPERIMENTAL) LogarithmPlotter now has an optional LaTeX integration.
|
||||
* It requires a LaTeX installation, including `latexmk` and `dvipng` available in the PATH.
|
||||
* NOTE: LaTeX support is disabled by default and is only for working for the rendering on the graph.
|
||||
* NOTE: The objects and history tab still use the legacy text based expression rendering.
|
||||
* Thanks and contributions dialog, showing included libraries and translations, their license and author(s).
|
||||
* LaTeX rendering can be disabled for texts, even if LaTeX is enabled.
|
||||
|
||||
**Changes**
|
||||
|
||||
* History re/undos only redraw the graph every 4 change at most in order to speed up the process when re/undoing a lot of changes.
|
||||
* Gradients are no longer hidden when filtered out in the history tab.
|
||||
|
||||
**Added translations**
|
||||
|
||||
* LaTeX options and error messages
|
||||
* Thanks and contribution dialog
|
||||
* New option for text.
|
||||
* Fixed translation of "repartition" which should be "distribution" in certain remaining strings.
|
||||
|
||||
**Fixed bugs**
|
||||
|
||||
* (macos) #1 - Opening files don't work on compiled versions of LogarithmPlotter on MacOS
|
||||
* (snapcraft) Fixed bug preventing from launching LogarithmPlotter. This fix has been backported to v0.1.8.
|
||||
* (snapcraft) Files are now properly opened.
|
||||
* (snapcraft) Added changelog support.
|
||||
|
||||
**Internal changes**
|
||||
|
||||
* Moved python modules to "util" directory for more clarity.
|
||||
* Moved flatpak metainfo to eu.ad5001.LogarithmPlotter repository.
|
||||
* Componented the Mathlib library in order to have a more readable source.
|
||||
* Added documentation for most internal JavaScript modules.
|
||||
* Merge label drawing methods due to it's complexity.
|
||||
* (flatpak) Updated SDK version to v5.15-21.08.
|
||||
|
||||
## v0.1.8 (19 Feb 2022)
|
||||
|
||||
**New**
|
||||
|
|
|
@ -630,8 +630,8 @@ attach them to the start of each source file to most effectively state
|
|||
the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
LogarithmPlotter - Create graphs with logarithm scales.
|
||||
Copyright (C) 2021 Ad5001
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -652,7 +652,7 @@ mail.
|
|||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
LogarithmPlotter Copyright (C) 2021 Ad5001
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
@ -673,4 +673,3 @@ library, you may consider it more useful to permit linking proprietary
|
|||
applications with the library. If this is what you want to do, use the
|
||||
GNU Lesser General Public License instead of this License. But first,
|
||||
please read <https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
lrelease *.ts
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
lupdate -extensions js,qs,qml,py -recursive .. -ts lp_*.ts
|
|
@ -1,149 +0,0 @@
|
|||
"""
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from time import time
|
||||
|
||||
start_time = time()
|
||||
|
||||
from PySide2.QtWidgets import QApplication
|
||||
from PySide2.QtQml import QQmlApplicationEngine
|
||||
from PySide2.QtCore import QTranslator, QLocale
|
||||
from PySide2.QtGui import QIcon
|
||||
|
||||
from tempfile import TemporaryDirectory
|
||||
from os import getcwd, chdir, environ, path, remove, close
|
||||
from platform import release as os_release
|
||||
from sys import platform, argv, version as sys_version, exit
|
||||
|
||||
# Create the temporary directory for saving copied screenshots and latex files
|
||||
tempdir = TemporaryDirectory()
|
||||
tmpfile = path.join(tempdir.name, 'graph.png')
|
||||
pwd = getcwd()
|
||||
|
||||
chdir(path.dirname(path.realpath(__file__)))
|
||||
|
||||
from sys import path as sys_path
|
||||
if path.realpath(path.join(getcwd(), "..")) not in sys_path:
|
||||
sys_path.append(path.realpath(path.join(getcwd(), "..")))
|
||||
|
||||
|
||||
from LogarithmPlotter import __VERSION__
|
||||
from LogarithmPlotter.util import config, native
|
||||
from LogarithmPlotter.util.update import check_for_updates
|
||||
from LogarithmPlotter.util.helper import Helper
|
||||
from LogarithmPlotter.util.latex import Latex
|
||||
|
||||
config.init()
|
||||
|
||||
def get_linux_theme():
|
||||
des = {
|
||||
"KDE": "org.kde.desktop",
|
||||
"gnome": "default",
|
||||
"lxqt": "fusion",
|
||||
"mate": "fusion",
|
||||
}
|
||||
if "XDG_SESSION_DESKTOP" in environ:
|
||||
return des[environ["XDG_SESSION_DESKTOP"]] if environ["XDG_SESSION_DESKTOP"] in des else "fusion"
|
||||
else:
|
||||
# Android
|
||||
return "Material"
|
||||
|
||||
def run():
|
||||
|
||||
environ["QT_QUICK_CONTROLS_STYLE"] = {
|
||||
"linux": get_linux_theme(),
|
||||
"freebsd": get_linux_theme(),
|
||||
"win32": "universal" if os_release == "10" else "fusion",
|
||||
"cygwin": "fusion",
|
||||
"darwin": "default"
|
||||
}[platform]
|
||||
|
||||
dep_time = time()
|
||||
print("Loaded dependencies in " + str((dep_time - start_time)*1000) + "ms.")
|
||||
|
||||
icon_fallbacks = QIcon.fallbackSearchPaths();
|
||||
base_icon_path = path.join(getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "icons")
|
||||
icon_fallbacks.append(path.realpath(path.join(base_icon_path, "common")))
|
||||
icon_fallbacks.append(path.realpath(path.join(base_icon_path, "objects")))
|
||||
icon_fallbacks.append(path.realpath(path.join(base_icon_path, "history")))
|
||||
icon_fallbacks.append(path.realpath(path.join(base_icon_path, "settings")))
|
||||
icon_fallbacks.append(path.realpath(path.join(base_icon_path, "settings", "custom")))
|
||||
QIcon.setFallbackSearchPaths(icon_fallbacks);
|
||||
|
||||
app = QApplication(argv)
|
||||
app.setApplicationName("LogarithmPlotter")
|
||||
app.setOrganizationName("Ad5001")
|
||||
app.styleHints().setShowShortcutsInContextMenus(True)
|
||||
app.setWindowIcon(QIcon(path.realpath(path.join(getcwd(), "logarithmplotter.svg"))))
|
||||
|
||||
# Installing translators
|
||||
translator = QTranslator()
|
||||
# Check if lang is forced.
|
||||
forcedlang = [p for p in argv if p[:7]=="--lang="]
|
||||
locale = QLocale(forcedlang[0][7:]) if len(forcedlang) > 0 else QLocale()
|
||||
if (translator.load(locale, "lp", "_", path.realpath(path.join(getcwd(), "i18n")))):
|
||||
app.installTranslator(translator);
|
||||
|
||||
# Installing macOS file handler.
|
||||
macOSFileOpenHandler = None
|
||||
if platform == "darwin":
|
||||
macOSFileOpenHandler = native.MacOSFileOpenHandler()
|
||||
app.installEventFilter(macOSFileOpenHandler)
|
||||
|
||||
engine = QQmlApplicationEngine()
|
||||
global tmpfile
|
||||
helper = Helper(pwd, tmpfile)
|
||||
latex = Latex(tempdir, app.palette())
|
||||
engine.rootContext().setContextProperty("Helper", helper)
|
||||
engine.rootContext().setContextProperty("Latex", latex)
|
||||
engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv)
|
||||
engine.rootContext().setContextProperty("StartTime", dep_time)
|
||||
|
||||
app.translate("About","About LogarithmPlotter") # FOR SOME REASON, if this isn't included, Qt refuses to load the QML file.
|
||||
|
||||
engine.addImportPath(path.realpath(path.join(getcwd(), "qml")))
|
||||
engine.load(path.realpath(path.join(getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "LogarithmPlotter.qml")))
|
||||
|
||||
|
||||
if not engine.rootObjects():
|
||||
print("No root object", path.realpath(path.join(getcwd(), "qml")))
|
||||
print(path.realpath(path.join(getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "LogarithmPlotter.qml")))
|
||||
exit(-1)
|
||||
|
||||
# Open the current diagram
|
||||
chdir(pwd)
|
||||
if len(argv) > 0 and path.exists(argv[-1]) and argv[-1].split('.')[-1] in ['lpf']:
|
||||
engine.rootObjects()[0].loadDiagram(argv[-1])
|
||||
chdir(path.dirname(path.realpath(__file__)))
|
||||
|
||||
if platform == "darwin":
|
||||
macOSFileOpenHandler.init_graphics(engine.rootObjects()[0])
|
||||
|
||||
# Check for updates
|
||||
if config.getSetting("check_for_updates"):
|
||||
check_for_updates(__VERSION__, engine.rootObjects()[0])
|
||||
|
||||
exit_code = app.exec_()
|
||||
|
||||
tempdir.cleanup()
|
||||
config.save()
|
||||
exit(exit_code)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<svg id="SVGRoot" width="48" height="48" version="1.1" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>LogarithmPlotter Icon v1.0</title>
|
||||
<g fill-rule="evenodd" stroke-width="2">
|
||||
<rect width="48" height="48" ry="6" fill="#fff"/>
|
||||
<rect x="2" y="38" width="44" height="4" stroke-opacity="0"/>
|
||||
<rect x="18" y="2" width="4" height="44" stroke-opacity="0"/>
|
||||
</g>
|
||||
<path d="M 42.05,2 A 40.05,36.05 0 0 1 2,38.05" fill="none" stroke="#f00" stroke-width="3.9012"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 488 B |
|
@ -1,215 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQml 2.12
|
||||
import "../js/objects.js" as Objects
|
||||
import "../js/historylib.js" as HistoryLib
|
||||
import "../js/history/common.js" as HistoryCommon
|
||||
|
||||
/*!
|
||||
\qmltype History
|
||||
\inqmlmodule eu.ad5001.LogarithmPlotter.History
|
||||
\brief QObject holding persistantly for undo & redo stacks.
|
||||
|
||||
\sa HistoryBrowser, historylib
|
||||
*/
|
||||
Item {
|
||||
// Using a QtObject is necessary in order to have proper property propagation in QML
|
||||
id: historyObj
|
||||
|
||||
/*!
|
||||
\qmlproperty int History::undoCount
|
||||
Count of undo actions.
|
||||
*/
|
||||
property int undoCount: 0
|
||||
/*!
|
||||
\qmlproperty int History::redoCount
|
||||
Count of redo actions.
|
||||
*/
|
||||
property int redoCount: 0
|
||||
/*!
|
||||
\qmlproperty var History::undoStack
|
||||
Stack of undo actions.
|
||||
*/
|
||||
property var undoStack: []
|
||||
/*!
|
||||
\qmlproperty var History::redoStack
|
||||
Stack of redo actions.
|
||||
*/
|
||||
property var redoStack: []
|
||||
/*!
|
||||
\qmlproperty bool History::saved
|
||||
true when no modification was done to the current working file, false otherwise.
|
||||
*/
|
||||
property bool saved: true
|
||||
|
||||
|
||||
/*!
|
||||
\qmlmethod void History::clear()
|
||||
Clears both undo and redo stacks completly.
|
||||
*/
|
||||
function clear() {
|
||||
undoCount = 0
|
||||
redoCount = 0
|
||||
undoStack = []
|
||||
redoStack = []
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\qmlmethod var History::serialize()
|
||||
Serializes history into JSON-able content.
|
||||
*/
|
||||
function serialize() {
|
||||
let undoSt = [], redoSt = [];
|
||||
for(let i = 0; i < undoCount; i++)
|
||||
undoSt.push([
|
||||
undoStack[i].type(),
|
||||
undoStack[i].export()
|
||||
]);
|
||||
for(let i = 0; i < redoCount; i++)
|
||||
redoSt.push([
|
||||
redoStack[i].type(),
|
||||
redoStack[i].export()
|
||||
]);
|
||||
return [undoSt, redoSt]
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void History::unserialize(var undoSt, var redoSt)
|
||||
Unserializes both \c undoSt stack and \c redoSt stack from serialized content.
|
||||
*/
|
||||
function unserialize(undoSt, redoSt) {
|
||||
clear();
|
||||
for(let i = 0; i < undoSt.length; i++)
|
||||
undoStack.push(new HistoryLib.Actions[undoSt[i][0]](...undoSt[i][1]))
|
||||
for(let i = 0; i < redoSt.length; i++)
|
||||
redoStack.push(new HistoryLib.Actions[redoSt[i][0]](...redoSt[i][1]))
|
||||
undoCount = undoSt.length;
|
||||
redoCount = redoSt.length;
|
||||
objectLists.update()
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void History::addToHistory(var action)
|
||||
Adds an instance of historylib.Action to history.
|
||||
*/
|
||||
function addToHistory(action) {
|
||||
if(action instanceof HistoryLib.Action) {
|
||||
console.log("Added new entry to history: " + action.getReadableString())
|
||||
undoStack.push(action)
|
||||
undoCount++;
|
||||
if(Helper.getSettingBool("reset_redo_stack")) {
|
||||
redoStack = []
|
||||
redoCount = 0
|
||||
}
|
||||
saved = false
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void History::undo()
|
||||
Undoes the historylib.Action at the top of the undo stack and pushes it to the top of the redo stack.
|
||||
*/
|
||||
function undo() {
|
||||
if(undoStack.length > 0) {
|
||||
var action = undoStack.pop()
|
||||
action.undo()
|
||||
objectLists.update()
|
||||
redoStack.push(action)
|
||||
undoCount--;
|
||||
redoCount++;
|
||||
saved = false
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void History::redo()
|
||||
Redoes the historylib.Action at the top of the redo stack and pushes it to the top of the undo stack.
|
||||
*/
|
||||
function redo() {
|
||||
if(redoStack.length > 0) {
|
||||
var action = redoStack.pop()
|
||||
action.redo()
|
||||
objectLists.update()
|
||||
undoStack.push(action)
|
||||
undoCount++;
|
||||
redoCount--;
|
||||
saved = false
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void History::undoMultipleDefered(int toUndoCount)
|
||||
Undoes several historylib.Action at the top of the undo stack and pushes them to the top of the redo stack.
|
||||
It undoes them deferedly to avoid overwhelming the computer while creating a cool short accelerated summary of all changes.
|
||||
*/
|
||||
function undoMultipleDefered(toUndoCount) {
|
||||
undoTimer.toUndoCount = toUndoCount;
|
||||
undoTimer.start()
|
||||
if(toUndoCount > 0)
|
||||
saved = false
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\qmlmethod void History::redoMultipleDefered(int toRedoCount)
|
||||
Redoes several historylib.Action at the top of the redo stack and pushes them to the top of the undo stack.
|
||||
It redoes them deferedly to avoid overwhelming the computer while creating a cool short accelerated summary of all changes.
|
||||
*/
|
||||
function redoMultipleDefered(toRedoCount) {
|
||||
redoTimer.toRedoCount = toRedoCount;
|
||||
redoTimer.start()
|
||||
if(toRedoCount > 0)
|
||||
saved = false
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: undoTimer
|
||||
interval: 5; running: false; repeat: true
|
||||
property int toUndoCount: 0
|
||||
onTriggered: {
|
||||
if(toUndoCount > 0) {
|
||||
historyObj.undo()
|
||||
toUndoCount--;
|
||||
} else {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: redoTimer
|
||||
interval: 5; running: false; repeat: true
|
||||
property int toRedoCount: 0
|
||||
onTriggered: {
|
||||
if(toRedoCount > 0) {
|
||||
historyObj.redo()
|
||||
toRedoCount--;
|
||||
} else {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
HistoryLib.history = historyObj
|
||||
HistoryCommon.themeTextColor = sysPalette.windowText
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
module eu.ad5001.LogarithmPlotter.History
|
||||
|
||||
History 1.0 History.qml
|
||||
HistoryBrowser 1.0 HistoryBrowser.qml
|
||||
HistoryItem 1.0 HistoryItem.qml
|
|
@ -1,466 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.12
|
||||
import "js/objects.js" as Objects
|
||||
import "js/utils.js" as Utils
|
||||
import "js/mathlib.js" as MathLib
|
||||
|
||||
/*!
|
||||
\qmltype LogGraphCanvas
|
||||
\inqmlmodule eu.ad5001.LogarithmPlotter
|
||||
\brief Canvas used to display the diagram.
|
||||
|
||||
Provides a customized canvas with several helper methods to be used by objects.
|
||||
|
||||
\sa LogarithmPlotter, PickLocationOverlay
|
||||
*/
|
||||
Canvas {
|
||||
id: canvas
|
||||
anchors.top: separator.bottom
|
||||
anchors.left: parent.left
|
||||
height: parent.height - 90
|
||||
width: parent.width
|
||||
|
||||
/*!
|
||||
\qmlproperty double LogGraphCanvas::xmin
|
||||
Minimum x of the diagram, provided from settings.
|
||||
\sa Settings
|
||||
*/
|
||||
property double xmin: 0
|
||||
/*!
|
||||
\qmlproperty double LogGraphCanvas::ymax
|
||||
Maximum y of the diagram, provided from settings.
|
||||
\sa Settings
|
||||
*/
|
||||
property double ymax: 0
|
||||
/*!
|
||||
\qmlproperty double LogGraphCanvas::xzoom
|
||||
Zoom on the x axis of the diagram, provided from settings.
|
||||
\sa Settings
|
||||
*/
|
||||
property double xzoom: 10
|
||||
/*!
|
||||
\qmlproperty double LogGraphCanvas::yzoom
|
||||
Zoom on the y axis of the diagram, provided from settings.
|
||||
\sa Settings
|
||||
*/
|
||||
property double yzoom: 10
|
||||
/*!
|
||||
\qmlproperty string LogGraphCanvas::xaxisstep
|
||||
Step of the x axis graduation, provided from settings.
|
||||
\note: Only available in non-logarithmic mode.
|
||||
\sa Settings
|
||||
*/
|
||||
property string xaxisstep: "4"
|
||||
/*!
|
||||
\qmlproperty string LogGraphCanvas::yaxisstep
|
||||
Step of the y axis graduation, provided from settings.
|
||||
\sa Settings
|
||||
*/
|
||||
property string yaxisstep: "4"
|
||||
/*!
|
||||
\qmlproperty string LogGraphCanvas::xlabel
|
||||
Label used on the x axis, provided from settings.
|
||||
\sa Settings
|
||||
*/
|
||||
property string xlabel: ""
|
||||
/*!
|
||||
\qmlproperty string LogGraphCanvas::ylabel
|
||||
Label used on the y axis, provided from settings.
|
||||
\sa Settings
|
||||
*/
|
||||
property string ylabel: ""
|
||||
/*!
|
||||
\qmlproperty double LogGraphCanvas::linewidth
|
||||
Width of lines that will be drawn into the canvas, provided from settings.
|
||||
\sa Settings
|
||||
*/
|
||||
property double linewidth: 1
|
||||
/*!
|
||||
\qmlproperty double LogGraphCanvas::textsize
|
||||
Font size of the text that will be drawn into the canvas, provided from settings.
|
||||
\sa Settings
|
||||
*/
|
||||
property double textsize: 14
|
||||
/*!
|
||||
\qmlproperty bool LogGraphCanvas::logscalex
|
||||
true if the canvas should be in logarithmic mode, false otherwise.
|
||||
Provided from settings.
|
||||
\sa Settings
|
||||
*/
|
||||
property bool logscalex: false
|
||||
/*!
|
||||
\qmlproperty bool LogGraphCanvas::showxgrad
|
||||
true if the x graduation should be shown, false otherwise.
|
||||
Provided from settings.
|
||||
\sa Settings
|
||||
*/
|
||||
property bool showxgrad: false
|
||||
/*!
|
||||
\qmlproperty bool LogGraphCanvas::showygrad
|
||||
true if the y graduation should be shown, false otherwise.
|
||||
Provided from settings.
|
||||
\sa Settings
|
||||
*/
|
||||
property bool showygrad: false
|
||||
|
||||
/*!
|
||||
\qmlproperty int LogGraphCanvas::maxgradx
|
||||
Max power of the logarithmic scaled on the x axis in logarithmic mode.
|
||||
*/
|
||||
property int maxgradx: 20
|
||||
|
||||
/*!
|
||||
\qmlproperty var LogGraphCanvas::yaxisstepExpr
|
||||
Expression for the y axis step (used to create labels).
|
||||
*/
|
||||
property var yaxisstepExpr: (new MathLib.Expression(`x*(${yaxisstep})`))
|
||||
/*!
|
||||
\qmlproperty double LogGraphCanvas::yaxisstep1
|
||||
Value of the for the y axis step.
|
||||
*/
|
||||
property double yaxisstep1: yaxisstepExpr.execute(1)
|
||||
/*!
|
||||
\qmlproperty int LogGraphCanvas::drawMaxY
|
||||
Minimum value of y that should be drawn onto the canvas.
|
||||
*/
|
||||
property int drawMaxY: Math.ceil(Math.max(Math.abs(ymax), Math.abs(px2y(canvasSize.height)))/yaxisstep1)
|
||||
/*!
|
||||
\qmlproperty var LogGraphCanvas::xaxisstepExpr
|
||||
Expression for the x axis step (used to create labels).
|
||||
*/
|
||||
property var xaxisstepExpr: (new MathLib.Expression(`x*(${xaxisstep})`))
|
||||
/*!
|
||||
\qmlproperty double LogGraphCanvas::xaxisstep1
|
||||
Value of the for the x axis step.
|
||||
*/
|
||||
property double xaxisstep1: xaxisstepExpr.execute(1)
|
||||
/*!
|
||||
\qmlproperty int LogGraphCanvas::drawMaxX
|
||||
Maximum value of x that should be drawn onto the canvas.
|
||||
*/
|
||||
property int drawMaxX: Math.ceil(Math.max(Math.abs(xmin), Math.abs(px2x(canvasSize.width)))/xaxisstep1)
|
||||
|
||||
property var imageLoaders: {}
|
||||
property var ctx
|
||||
|
||||
Component.onCompleted: imageLoaders = {}
|
||||
|
||||
onPaint: function(rect) {
|
||||
//console.log('Redrawing')
|
||||
if(rect.width == canvas.width) { // Redraw full canvas
|
||||
ctx = getContext("2d");
|
||||
reset(ctx)
|
||||
drawGrille(ctx)
|
||||
drawAxises(ctx)
|
||||
drawLabels(ctx)
|
||||
ctx.lineWidth = linewidth
|
||||
for(var objType in Objects.currentObjects) {
|
||||
for(var obj of Objects.currentObjects[objType]){
|
||||
ctx.strokeStyle = obj.color
|
||||
ctx.fillStyle = obj.color
|
||||
if(obj.visible) obj.draw(canvas, ctx)
|
||||
}
|
||||
}
|
||||
ctx.lineWidth = 1
|
||||
}
|
||||
}
|
||||
|
||||
onImageLoaded: {
|
||||
Object.keys(imageLoaders).forEach((key) => {
|
||||
if(isImageLoaded(key)) {
|
||||
// Calling callback
|
||||
imageLoaders[key][0](canvas, ctx, imageLoaders[key][1])
|
||||
delete imageLoaders[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void LogGraphCanvas::reset(var ctx)
|
||||
Resets the canvas to a blank one with default setting using 2D \c ctx.
|
||||
*/
|
||||
function reset(ctx){
|
||||
// Reset
|
||||
ctx.fillStyle = "#FFFFFF"
|
||||
ctx.strokeStyle = "#000000"
|
||||
ctx.font = `${canvas.textsize-2}px sans-serif`
|
||||
ctx.fillRect(0,0,width,height)
|
||||
}
|
||||
|
||||
// Drawing the log based graph
|
||||
|
||||
/*!
|
||||
\qmlmethod void LogGraphCanvas::drawGrille(var ctx)
|
||||
Draws the grid using 2D \c ctx.
|
||||
*/
|
||||
function drawGrille(ctx) {
|
||||
ctx.strokeStyle = "#C0C0C0"
|
||||
if(logscalex) {
|
||||
for(var xpow = -maxgradx; xpow <= maxgradx; xpow++) {
|
||||
for(var xmulti = 1; xmulti < 10; xmulti++) {
|
||||
drawXLine(ctx, Math.pow(10, xpow)*xmulti)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(var x = 0; x < drawMaxX; x+=1) {
|
||||
drawXLine(ctx, x*xaxisstep1)
|
||||
drawXLine(ctx, -x*xaxisstep1)
|
||||
}
|
||||
}
|
||||
for(var y = 0; y < drawMaxY; y+=1) {
|
||||
drawYLine(ctx, y*yaxisstep1)
|
||||
drawYLine(ctx, -y*yaxisstep1)
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void LogGraphCanvas::drawAxises(var ctx)
|
||||
Draws the graph axises using 2D \c ctx.
|
||||
*/
|
||||
function drawAxises(ctx) {
|
||||
ctx.strokeStyle = "#000000"
|
||||
var axisypos = logscalex ? 1 : 0
|
||||
drawXLine(ctx, axisypos)
|
||||
drawYLine(ctx, 0)
|
||||
var axisypx = x2px(axisypos) // X coordinate of Y axis
|
||||
var axisxpx = y2px(0) // Y coordinate of X axis
|
||||
// Drawing arrows
|
||||
drawLine(ctx, axisypx, 0, axisypx-10, 10)
|
||||
drawLine(ctx, axisypx, 0, axisypx+10, 10)
|
||||
drawLine(ctx, canvasSize.width, axisxpx, canvasSize.width-10, axisxpx-10)
|
||||
drawLine(ctx, canvasSize.width, axisxpx, canvasSize.width-10, axisxpx+10)
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void LogGraphCanvas::drawLabels(var ctx)
|
||||
Draws all labels (graduation & axises labels) using 2D \c ctx.
|
||||
*/
|
||||
function drawLabels(ctx) {
|
||||
var axisypx = x2px(logscalex ? 1 : 0) // X coordinate of Y axis
|
||||
var axisxpx = y2px(0) // Y coordinate of X axis
|
||||
// Labels
|
||||
ctx.fillStyle = "#000000"
|
||||
ctx.font = `${canvas.textsize+2}px sans-serif`
|
||||
ctx.fillText(ylabel, axisypx+10, 24)
|
||||
var textSize = ctx.measureText(xlabel).width
|
||||
ctx.fillText(xlabel, canvasSize.width-14-textSize, axisxpx-5)
|
||||
// Axis graduation labels
|
||||
ctx.font = `${canvas.textsize-2}px sans-serif`
|
||||
|
||||
var txtMinus = ctx.measureText('-').width
|
||||
if(showxgrad) {
|
||||
if(logscalex) {
|
||||
for(var xpow = -maxgradx; xpow <= maxgradx; xpow+=1) {
|
||||
var textSize = ctx.measureText("10"+Utils.textsup(xpow)).width
|
||||
if(xpow != 0)
|
||||
drawVisibleText(ctx, "10"+Utils.textsup(xpow), x2px(Math.pow(10,xpow))-textSize/2, axisxpx+16+(6*(y==0)))
|
||||
}
|
||||
} else {
|
||||
for(var x = 1; x < drawMaxX; x += 1) {
|
||||
var drawX = x*xaxisstep1
|
||||
var txtX = xaxisstepExpr.simplify(x)
|
||||
var textSize = measureText(ctx, txtX, 6).height
|
||||
drawVisibleText(ctx, txtX, x2px(drawX)-4, axisxpx+textsize/2+textSize)
|
||||
drawVisibleText(ctx, '-'+txtX, x2px(-drawX)-4, axisxpx+textsize/2+textSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
if(showygrad) {
|
||||
for(var y = 0; y < drawMaxY; y += 1) {
|
||||
var drawY = y*yaxisstep1
|
||||
var txtY = yaxisstepExpr.simplify(y)
|
||||
var textSize = ctx.measureText(txtY).width
|
||||
drawVisibleText(ctx, txtY, axisypx-6-textSize, y2px(drawY)+4+(10*(y==0)))
|
||||
if(y != 0)
|
||||
drawVisibleText(ctx, '-'+txtY, axisypx-6-textSize-txtMinus, y2px(-drawY)+4)
|
||||
}
|
||||
}
|
||||
ctx.fillStyle = "#FFFFFF"
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void LogGraphCanvas::drawXLine(var ctx, double x)
|
||||
Draws an horizontal line at \c x plot coordinate using 2D \c ctx.
|
||||
*/
|
||||
function drawXLine(ctx, x) {
|
||||
if(visible(x, ymax)) {
|
||||
drawLine(ctx, x2px(x), 0, x2px(x), canvasSize.height)
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void LogGraphCanvas::drawXLine(var ctx, double x)
|
||||
Draws an vertical line at \c y plot coordinate using 2D \c ctx.
|
||||
*/
|
||||
function drawYLine(ctx, y) {
|
||||
if(visible(xmin, y)) {
|
||||
drawLine(ctx, 0, y2px(y), canvasSize.width, y2px(y))
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void LogGraphCanvas::drawVisibleText(var ctx, string text, double x, double y)
|
||||
Writes multline \c text onto the canvas using 2D \c ctx.
|
||||
\note The \c x and \c y properties here are relative to the canvas, not the plot.
|
||||
*/
|
||||
function drawVisibleText(ctx, text, x, y) {
|
||||
if(x > 0 && x < canvasSize.width && y > 0 && y < canvasSize.height) {
|
||||
text.toString().split("\n").forEach(function(txt, i){
|
||||
ctx.fillText(txt, x, y+(canvas.textsize*i))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void LogGraphCanvas::drawVisibleImage(var ctx, var image, double x, double y)
|
||||
Draws an \c image onto the canvas using 2D \c ctx.
|
||||
\note The \c x, \c y \c width and \c height properties here are relative to the canvas, not the plot.
|
||||
*/
|
||||
function drawVisibleImage(ctx, image, x, y, width, height) {
|
||||
console.log("Drawing image", isImageLoaded(image), isImageError(image))
|
||||
markDirty(Qt.rect(x, y, width, height));
|
||||
ctx.drawImage(image, x, y, width, height)
|
||||
/*if(true || (x > 0 && x < canvasSize.width && y > 0 && y < canvasSize.height)) {
|
||||
}*/
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod var LogGraphCanvas::measureText(var ctx, string text)
|
||||
Measures the wicth and height of a multiline \c text that would be drawn onto the canvas using 2D \c ctx.
|
||||
Return format: dictionary {"width": width, "height": height}
|
||||
*/
|
||||
function measureText(ctx, text) {
|
||||
var theight = 0
|
||||
var twidth = 0
|
||||
text.split("\n").forEach(function(txt, i){
|
||||
theight += canvas.textsize
|
||||
if(ctx.measureText(txt).width > twidth) twidth = ctx.measureText(txt).width
|
||||
})
|
||||
return {'width': twidth, 'height': theight}
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod double LogGraphCanvas::x2px(double x)
|
||||
Converts an \c x coordinate to it's relative position on the canvas.
|
||||
It supports both logarithmic and non logarithmic scale depending on the currently selected mode.
|
||||
*/
|
||||
function x2px(x) {
|
||||
if(logscalex) {
|
||||
var logxmin = Math.log(xmin)
|
||||
return (Math.log(x)-logxmin)*xzoom
|
||||
} else return (x - xmin)*xzoom
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod double LogGraphCanvas::y2px(double y)
|
||||
Converts an \c y coordinate to it's relative position on the canvas.
|
||||
The y axis not supporting logarithmic scale, it only support linear convertion.
|
||||
*/
|
||||
function y2px(y) {
|
||||
return (ymax-y)*yzoom
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod double LogGraphCanvas::px2x(double px)
|
||||
Converts an x \c px position on the canvas to it's corresponding coordinate on the plot.
|
||||
It supports both logarithmic and non logarithmic scale depending on the currently selected mode.
|
||||
*/
|
||||
function px2x(px) {
|
||||
if(logscalex) {
|
||||
return Math.exp(px/xzoom+Math.log(xmin))
|
||||
} else return (px/xzoom+xmin)
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod double LogGraphCanvas::px2x(double px)
|
||||
Converts an x \c px position on the canvas to it's corresponding coordinate on the plot.
|
||||
It supports both logarithmic and non logarithmic scale depending on the currently selected mode.
|
||||
*/
|
||||
function px2y(px) {
|
||||
return -(px/yzoom-ymax)
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod bool LogGraphCanvas::visible(double x, double y)
|
||||
Checks whether a plot point (\c x, \c y) is visible or not on the canvas.
|
||||
*/
|
||||
function visible(x, y) {
|
||||
return (x2px(x) >= 0 && x2px(x) <= canvasSize.width) && (y2px(y) >= 0 && y2px(y) <= canvasSize.height)
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod bool LogGraphCanvas::drawLine(var ctx, double x1, double y1, double x2, double y2)
|
||||
Draws a line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx.
|
||||
*/
|
||||
function drawLine(ctx, x1, y1, x2, y2) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.lineTo(x2, y2);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod bool LogGraphCanvas::drawDashedLine2(var ctx, double x1, double y1, double x2, double y2)
|
||||
Draws a dashed line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx.
|
||||
*/
|
||||
function drawDashedLine2(ctx, x1, y1, x2, y2, dashPxSize = 5) {
|
||||
ctx.setLineDash([dashPxSize, dashPxSize]);
|
||||
drawLine(ctx, x1, y1, x2, y2)
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod bool LogGraphCanvas::drawDashedLine(var ctx, double x1, double y1, double x2, double y2)
|
||||
Draws a dashed line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx.
|
||||
(Legacy slower method)
|
||||
*/
|
||||
function drawDashedLine(ctx, x1, y1, x2, y2, dashPxSize = 10) {
|
||||
var distance = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))
|
||||
var progPerc = dashPxSize/distance
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1, y1);
|
||||
for(var i = 0; i < 1; i += progPerc) {
|
||||
ctx.lineTo(x1-(x1-x2)*i, y1-(y1-y2)*i)
|
||||
ctx.moveTo(x1-(x1-x2)*(i+progPerc/2), y1-(y1-y2)*(i+progPerc/2))
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod var LogGraphCanvas::renderLatexImage(string ltxText, color)
|
||||
Renders latex markup \c ltxText to an image and loads it. Returns a dictionary with three values: source, width and height.
|
||||
*/
|
||||
function renderLatexImage(ltxText, color, callback) {
|
||||
let [ltxSrc, ltxWidth, ltxHeight] = Latex.render(ltxText, textsize, color).split(",")
|
||||
let imgData = {
|
||||
"source": ltxSrc,
|
||||
"width": parseFloat(ltxWidth),
|
||||
"height": parseFloat(ltxHeight)
|
||||
};
|
||||
if(!isImageLoaded(ltxSrc) && !isImageLoading(ltxSrc)){
|
||||
// Wait until the image is loaded to callback.
|
||||
loadImage(ltxSrc)
|
||||
imageLoaders[ltxSrc] = [callback, imgData]
|
||||
} else {
|
||||
// Callback directly
|
||||
callback(canvas, ctx, imgData)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,309 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Dialogs 1.3 as D
|
||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||
import "../js/objects.js" as Objects
|
||||
import "../js/objs/common.js" as ObjectsCommons
|
||||
import "../js/historylib.js" as HistoryLib
|
||||
import "../js/utils.js" as Utils
|
||||
import "../js/mathlib.js" as MathLib
|
||||
|
||||
/*!
|
||||
\qmltype EditorDialog
|
||||
\inqmlmodule eu.ad5001.LogarithmPlotter.ObjectLists
|
||||
\brief Dialog used to edit properties of objects.
|
||||
|
||||
This class contains the dialog that allows to edit all properties of objects.
|
||||
\todo In the future, this class should be optimized so that each property doesn't instanciate one instance of each setting type.
|
||||
|
||||
\sa LogarithmPlotter, ObjectLists
|
||||
*/
|
||||
D.Dialog {
|
||||
id: objEditor
|
||||
/*!
|
||||
\qmlproperty string EditorDialog::objType
|
||||
Type of object being edited by the dialog.
|
||||
*/
|
||||
property string objType: 'Point'
|
||||
/*!
|
||||
\qmlproperty int EditorDialog::objIndex
|
||||
Index of the objects amongst the ones of it's type.
|
||||
*/
|
||||
property int objIndex: 0
|
||||
/*!
|
||||
\qmlproperty var EditorDialog::obj
|
||||
Instance of the object being edited.
|
||||
*/
|
||||
property var obj: Objects.currentObjects[objType][objIndex]
|
||||
|
||||
title: "LogarithmPlotter"
|
||||
width: 350
|
||||
height: 400
|
||||
|
||||
Label {
|
||||
id: dlgTitle
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
text: qsTr("Edit properties of %1 %2").arg(Objects.types[objEditor.objType].displayType()).arg(objEditor.obj.name)
|
||||
font.pixelSize: 20
|
||||
color: sysPalette.windowText
|
||||
}
|
||||
|
||||
Column {
|
||||
id: dlgProperties
|
||||
anchors.top: dlgTitle.bottom
|
||||
width: objEditor.width - 20
|
||||
spacing: 10
|
||||
|
||||
Setting.TextSetting {
|
||||
id: nameProperty
|
||||
height: 30
|
||||
label: qsTr("Name")
|
||||
icon: "common/label.svg"
|
||||
min: 1
|
||||
width: dlgProperties.width
|
||||
value: objEditor.obj.name
|
||||
onChanged: function(newValue) {
|
||||
var newName = Utils.parseName(newValue)
|
||||
if(newName != '' && objEditor.obj.name != newName) {
|
||||
if(Objects.getObjectByName(newName) != null) {
|
||||
newName = ObjectsCommons.getNewName(newName)
|
||||
}
|
||||
history.addToHistory(new HistoryLib.NameChanged(
|
||||
objEditor.obj.name, objEditor.objType, newName
|
||||
))
|
||||
Objects.currentObjects[objEditor.objType][objEditor.objIndex].name = newName
|
||||
objEditor.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex]
|
||||
objectListList.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Setting.ComboBoxSetting {
|
||||
id: labelContentProperty
|
||||
height: 30
|
||||
width: dlgProperties.width
|
||||
label: qsTr("Label content")
|
||||
model: [qsTr("null"), qsTr("name"), qsTr("name + value")]
|
||||
property var idModel: ["null", "name", "name + value"]
|
||||
icon: "common/label.svg"
|
||||
currentIndex: idModel.indexOf(objEditor.obj.labelContent)
|
||||
onActivated: function(newIndex) {
|
||||
if(idModel[newIndex] != objEditor.obj.labelContent) {
|
||||
objEditor.obj.labelContent = idModel[newIndex]
|
||||
objectListList.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic properties
|
||||
Repeater {
|
||||
id: dlgCustomProperties
|
||||
|
||||
Item {
|
||||
height: customPropComment.height + customPropText.height + customPropCheckBox.height + customPropCombo.height + customPropListDict.height
|
||||
width: dlgProperties.width
|
||||
property string label: qsTranslate('prop',modelData[0])
|
||||
property string icon: Utils.camelCase2readable(modelData[0])
|
||||
|
||||
// Item for comments
|
||||
Label {
|
||||
id: customPropComment
|
||||
width: parent.width
|
||||
height: visible ? implicitHeight : 0
|
||||
visible: modelData[0].startsWith('comment')
|
||||
// Translated text with object name.
|
||||
property string trText: visible ? qsTranslate('comment', modelData[1]).toString() : ''
|
||||
text: (visible && trText.includes("%1") ? trText.arg(objEditor.obj.name) : trText).toString()
|
||||
//color: sysPalette.windowText
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
// Setting for text & number settings as well as domains & expressions
|
||||
Setting.TextSetting {
|
||||
id: customPropText
|
||||
height: visible ? 30 : 0
|
||||
width: parent.width
|
||||
label: parent.label
|
||||
icon: `settings/custom/${parent.icon}.svg`
|
||||
isDouble: modelData[1] == 'number'
|
||||
visible: paramTypeIn(modelData[1], ['Expression', 'Domain', 'string', 'number'])
|
||||
defValue: visible ? {
|
||||
'Expression': () => Utils.simplifyExpression(objEditor.obj[modelData[0]].toEditableString()),
|
||||
'Domain': () => objEditor.obj[modelData[0]].toString(),
|
||||
'string': () => objEditor.obj[modelData[0]],
|
||||
'number': () => objEditor.obj[modelData[0]]
|
||||
}[modelData[1]]() : ""
|
||||
onChanged: function(newValue) {
|
||||
var newValue = {
|
||||
'Expression': () => new MathLib.Expression(newValue),
|
||||
'Domain': () => MathLib.parseDomain(newValue),
|
||||
'string': () => newValue,
|
||||
'number': () => parseFloat(newValue)
|
||||
}[modelData[1]]()
|
||||
// Ensuring old and new values are different to prevent useless adding to history.
|
||||
if(objEditor.obj[modelData[0]] != newValue) {
|
||||
history.addToHistory(new HistoryLib.EditedProperty(
|
||||
objEditor.obj.name, objEditor.objType, modelData[0],
|
||||
objEditor.obj[modelData[0]], newValue
|
||||
))
|
||||
objEditor.obj[modelData[0]] = newValue
|
||||
Objects.currentObjects[objEditor.objType][objEditor.objIndex].update()
|
||||
objectListList.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setting for boolean
|
||||
CheckBox {
|
||||
id: customPropCheckBox
|
||||
visible: modelData[1] == 'boolean'
|
||||
height: visible ? 20 : 0
|
||||
width: parent.width
|
||||
text: parent.label
|
||||
//icon: visible ? `settings/custom/${parent.icon}.svg` : ''
|
||||
|
||||
checked: visible ? objEditor.obj[modelData[0]] : false
|
||||
onClicked: {
|
||||
history.addToHistory(new HistoryLib.EditedProperty(
|
||||
objEditor.obj.name, objEditor.objType, modelData[0],
|
||||
objEditor.obj[modelData[0]], this.checked
|
||||
))
|
||||
objEditor.obj[modelData[0]] = this.checked
|
||||
Objects.currentObjects[objEditor.objType][objEditor.objIndex].update()
|
||||
objectListList.update()
|
||||
}
|
||||
}
|
||||
|
||||
// Setting when selecting data from an enum, or an object of a certain type.
|
||||
Setting.ComboBoxSetting {
|
||||
id: customPropCombo
|
||||
width: dlgProperties.width
|
||||
height: visible ? 30 : 0
|
||||
label: parent.label
|
||||
icon: visible ? `settings/custom/${parent.icon}.svg` : ''
|
||||
// True to select an object of type, false for enums.
|
||||
property bool selectObjMode: paramTypeIn(modelData[1], ['ObjectType'])
|
||||
property bool isRealObject: !selectObjMode || (modelData[1].objType != "ExecutableObject" && modelData[1].objType != "DrawableObject")
|
||||
|
||||
// Base, untranslated version of the model.
|
||||
property var baseModel: visible ?
|
||||
(selectObjMode ?
|
||||
Objects.getObjectsName(modelData[1].objType).concat(
|
||||
isRealObject ? [qsTr("+ Create new %1").arg(Objects.types[modelData[1].objType].displayType())] :
|
||||
[]) :
|
||||
modelData[1].values)
|
||||
: []
|
||||
// Translated verison of the model.
|
||||
model: selectObjMode ? baseModel : modelData[1].translatedValues
|
||||
visible: paramTypeIn(modelData[1], ['ObjectType', 'Enum'])
|
||||
currentIndex: baseModel.indexOf(selectObjMode ? objEditor.obj[modelData[0]].name : objEditor.obj[modelData[0]])
|
||||
|
||||
onActivated: function(newIndex) {
|
||||
if(selectObjMode) {
|
||||
// This is only done when what we're selecting are Objects.
|
||||
// Setting object property.
|
||||
var selectedObj = Objects.getObjectByName(baseModel[newIndex], modelData[1].objType)
|
||||
if(newIndex != 0) {
|
||||
// Make sure we don't set the object to null.
|
||||
if(selectedObj == null) {
|
||||
// Creating new object.
|
||||
selectedObj = Objects.createNewRegisteredObject(modelData[1].objType)
|
||||
history.addToHistory(new HistoryLib.CreateNewObject(selectedObj.name, modelData[1].objType, selectedObj.export()))
|
||||
baseModel = Objects.getObjectsName(modelData[1].objType).concat(
|
||||
isRealObject ? [qsTr("+ Create new %1").arg(Objects.types[modelData[1].objType].displayType())] :
|
||||
[])
|
||||
currentIndex = baseModel.indexOf(selectedObj.name)
|
||||
}
|
||||
selectedObj.requiredBy.push(Objects.currentObjects[objEditor.objType][objEditor.objIndex])
|
||||
//Objects.currentObjects[objEditor.objType][objEditor.objIndex].requiredBy = objEditor.obj[modelData[0]].filter((obj) => objEditor.obj.name != obj.name)
|
||||
}
|
||||
objEditor.obj.requiredBy = objEditor.obj.requiredBy.filter((obj) => objEditor.obj.name != obj.name)
|
||||
history.addToHistory(new HistoryLib.EditedProperty(
|
||||
objEditor.obj.name, objEditor.objType, modelData[0],
|
||||
objEditor.obj[modelData[0]], selectedObj
|
||||
))
|
||||
objEditor.obj[modelData[0]] = selectedObj
|
||||
} else if(baseModel[newIndex] != objEditor.obj[modelData[0]]) {
|
||||
// Ensuring new property is different to not add useless history entries.
|
||||
history.addToHistory(new HistoryLib.EditedProperty(
|
||||
objEditor.obj.name, objEditor.objType, modelData[0],
|
||||
objEditor.obj[modelData[0]], baseModel[newIndex]
|
||||
))
|
||||
objEditor.obj[modelData[0]] = baseModel[newIndex]
|
||||
}
|
||||
// Refreshing
|
||||
Objects.currentObjects[objEditor.objType][objEditor.objIndex].update()
|
||||
objectListList.update()
|
||||
}
|
||||
}
|
||||
|
||||
// Setting to edit lists or dictionaries (e.g sequences & repartition function values)
|
||||
Setting.ListSetting {
|
||||
id: customPropListDict
|
||||
width: parent.width
|
||||
height: visible ? implicitHeight : 0
|
||||
|
||||
visible: paramTypeIn(modelData[1], ['List', 'Dict'])
|
||||
label: parent.label
|
||||
//icon: `settings/custom/${parent.icon}.svg`
|
||||
dictionaryMode: paramTypeIn(modelData[1], ['Dict'])
|
||||
keyType: dictionaryMode ? modelData[1].keyType : 'string'
|
||||
valueType: visible ? modelData[1].valueType : 'string'
|
||||
preKeyLabel: visible ? (dictionaryMode ? modelData[1].preKeyLabel : modelData[1].label).replace(/\{name\}/g, objEditor.obj.name) : ''
|
||||
postKeyLabel: visible ? (dictionaryMode ? modelData[1].postKeyLabel : '').replace(/\{name\}/g, objEditor.obj.name) : ''
|
||||
keyRegexp: dictionaryMode ? modelData[1].keyFormat : /^.+$/
|
||||
valueRegexp: visible ? modelData[1].format : /^.+$/
|
||||
forbidAdding: visible ? modelData[1].forbidAdding : false
|
||||
|
||||
onChanged: {
|
||||
var exported = exportModel()
|
||||
history.addToHistory(new HistoryLib.EditedProperty(
|
||||
objEditor.obj.name, objEditor.objType, modelData[0],
|
||||
objEditor.obj[modelData[0]], exported
|
||||
))
|
||||
//Objects.currentObjects[objEditor.objType][objEditor.objIndex][modelData[0]] = exported
|
||||
objEditor.obj[modelData[0]] = exported
|
||||
//Objects.currentObjects[objEditor.objType][objEditor.objIndex].update()
|
||||
objEditor.obj.update()
|
||||
objectListList.update()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if(visible) importModel(objEditor.obj[modelData[0]])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void EditorDialog::show()
|
||||
Shows the editor after the object to be edited is set.
|
||||
*/
|
||||
function show() {
|
||||
dlgCustomProperties.model = [] // Reset
|
||||
let objProps = Objects.types[objEditor.objType].properties()
|
||||
dlgCustomProperties.model = Object.keys(objProps).map(prop => [prop, objProps[prop]]) // Converted to 2-dimentional array.
|
||||
objEditor.open()
|
||||
}
|
||||
}
|
|
@ -1,272 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Dialogs 1.3 as D
|
||||
import QtQuick.Controls 2.12
|
||||
import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting
|
||||
import "../js/objects.js" as Objects
|
||||
import "../js/historylib.js" as HistoryLib
|
||||
|
||||
/*!
|
||||
\qmltype ObjectLists
|
||||
\inqmlmodule eu.ad5001.LogarithmPlotter
|
||||
\brief Tab of the drawer that allows the user to manage the objects.
|
||||
|
||||
This item allows the user to syntheticly see all objects, while giving the user the ability
|
||||
to show, hide, delete, change the location and color, as well as opening the editor dialog
|
||||
for each object.
|
||||
|
||||
\sa LogarithmPlotter, ObjectCreationGrid, ObjectLists
|
||||
*/
|
||||
ScrollView {
|
||||
id: objectListList
|
||||
|
||||
signal changed()
|
||||
|
||||
property var listViews: {'':''} // Needs to be initialized or will be undefined -_-
|
||||
|
||||
|
||||
ScrollBar.horizontal.visible: false
|
||||
ScrollBar.vertical.visible: true
|
||||
|
||||
ListView {
|
||||
id: objectsListView
|
||||
model: Object.keys(Objects.types)
|
||||
width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0)
|
||||
implicitHeight: contentItem.childrenRect.height + footer.height + 10
|
||||
|
||||
delegate: ListView {
|
||||
id: objTypeList
|
||||
property string objType: objectsListView.model[index]
|
||||
property var editingRows: []
|
||||
model: Objects.currentObjects[objType]
|
||||
width: objectsListView.width
|
||||
implicitHeight: contentItem.childrenRect.height
|
||||
visible: model != undefined && model.length > 0
|
||||
interactive: false
|
||||
|
||||
Component.onCompleted: objectListList.listViews[objType] = objTypeList // Listing in order to be refreshed
|
||||
|
||||
header: Row {
|
||||
width: typeHeaderText.width + typeVisibilityCheckBox.visible
|
||||
height: visible ? 20 : 0
|
||||
visible: objTypeList.visible
|
||||
|
||||
CheckBox {
|
||||
id: typeVisibilityCheckBox
|
||||
checked: Objects.currentObjects[objType] != undefined ? Objects.currentObjects[objType].every(obj => obj.visible) : true
|
||||
onClicked: {
|
||||
for(var obj of Objects.currentObjects[objType]) obj.visible = this.checked
|
||||
for(var obj of objTypeList.editingRows) obj.objVisible = this.checked
|
||||
objectListList.changed()
|
||||
}
|
||||
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: checked ? qsTr("Hide all %1").arg(Objects.types[objType].displayTypeMultiple()) : qsTr("Show all %1").arg(Objects.types[objType].displayTypeMultiple())
|
||||
}
|
||||
|
||||
Label {
|
||||
id: typeHeaderText
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
text: qsTranslate("control", "%1: ").arg(Objects.types[objType].displayTypeMultiple())
|
||||
font.pixelSize: 20
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
id: controlRow
|
||||
property var obj: Objects.currentObjects[objType][index]
|
||||
property alias objVisible: objVisibilityCheckBox.checked
|
||||
height: 40
|
||||
width: objTypeList.width
|
||||
|
||||
Component.onCompleted: objTypeList.editingRows.push(controlRow)
|
||||
|
||||
CheckBox {
|
||||
id: objVisibilityCheckBox
|
||||
checked: Objects.currentObjects[objType][index].visible
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 5
|
||||
onClicked: {
|
||||
history.addToHistory(new HistoryLib.EditedVisibility(
|
||||
Objects.currentObjects[objType][index].name, objType, this.checked
|
||||
))
|
||||
Objects.currentObjects[objType][index].visible = this.checked
|
||||
objectListList.changed()
|
||||
controlRow.obj = Objects.currentObjects[objType][index]
|
||||
}
|
||||
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: checked ?
|
||||
qsTr("Hide %1 %2").arg(Objects.types[objType].displayType()).arg(obj.name) :
|
||||
qsTr("Show %1 %2").arg(Objects.types[objType].displayType()).arg(obj.name)
|
||||
}
|
||||
|
||||
Label {
|
||||
id: objDescription
|
||||
anchors.left: objVisibilityCheckBox.right
|
||||
anchors.right: deleteButton.left
|
||||
height: parent.height
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
text: obj.getReadableString()
|
||||
font.pixelSize: 14
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
objEditor.obj = Objects.currentObjects[objType][index]
|
||||
objEditor.objType = objType
|
||||
objEditor.objIndex = index
|
||||
//objEditor.editingRow = controlRow
|
||||
objEditor.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: pointerButton
|
||||
width: parent.height - 10
|
||||
height: width
|
||||
anchors.right: deleteButton.left
|
||||
anchors.rightMargin: 5
|
||||
anchors.topMargin: 5
|
||||
|
||||
Setting.Icon {
|
||||
id: icon
|
||||
width: 18
|
||||
height: 18
|
||||
anchors.centerIn: parent
|
||||
|
||||
color: sysPalette.windowText
|
||||
source: '../icons/common/position.svg'
|
||||
}
|
||||
|
||||
property bool hasXProp: Objects.types[objType].properties().hasOwnProperty('x')
|
||||
property bool hasYProp: Objects.types[objType].properties().hasOwnProperty('y')
|
||||
visible: hasXProp || hasYProp
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Set %1 %2 position").arg(Objects.types[objType].displayType()).arg(obj.name)
|
||||
|
||||
onClicked: {
|
||||
positionPicker.objType = objType
|
||||
positionPicker.objName = obj.name
|
||||
positionPicker.pickX = hasXProp
|
||||
positionPicker.pickY = hasYProp
|
||||
positionPicker.propertyX = 'x'
|
||||
positionPicker.propertyY = 'y'
|
||||
positionPicker.visible = true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: deleteButton
|
||||
width: parent.height - 10
|
||||
height: width
|
||||
anchors.right: colorPickRect.left
|
||||
anchors.rightMargin: 5
|
||||
anchors.topMargin: 5
|
||||
icon.name: 'delete'
|
||||
icon.source: '../icons/common/delete.svg'
|
||||
icon.color: sysPalette.buttonText
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Delete %1 %2").arg(Objects.types[objType].displayType()).arg(obj.name)
|
||||
|
||||
onClicked: {
|
||||
history.addToHistory(new HistoryLib.DeleteObject(
|
||||
obj.name, objType, obj.export()
|
||||
))
|
||||
Objects.currentObjects[objType][index].delete()
|
||||
Objects.currentObjects[objType].splice(index, 1)
|
||||
objectListList.update()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: colorPickRect
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 5
|
||||
anchors.topMargin: 5
|
||||
color: obj.color
|
||||
width: parent.height - 10
|
||||
height: width
|
||||
radius: Math.min(width, height)
|
||||
border.width: 2
|
||||
border.color: sysPalette.windowText
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: pickColor.open()
|
||||
}
|
||||
}
|
||||
|
||||
D.ColorDialog {
|
||||
id: pickColor
|
||||
color: obj.color
|
||||
title: qsTr("Pick new color for %1 %2").arg(Objects.types[objType].displayType()).arg(obj.name)
|
||||
onAccepted: {
|
||||
history.addToHistory(new HistoryLib.ColorChanged(
|
||||
obj.name, objType, obj.color, color.toString()
|
||||
))
|
||||
obj.color = color.toString()
|
||||
controlRow.obj = Objects.currentObjects[objType][index]
|
||||
objectListList.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create items
|
||||
footer: ObjectCreationGrid {
|
||||
id: createRow
|
||||
width: objectsListView.width
|
||||
objectEditor: objEditor
|
||||
objectLists: objectListList
|
||||
}
|
||||
}
|
||||
|
||||
// Object editor
|
||||
EditorDialog {
|
||||
id: objEditor
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void ObjectLists::update()
|
||||
Updates the view of the ObjectLists.
|
||||
*/
|
||||
function update() {
|
||||
objectListList.changed()
|
||||
for(var objType in objectListList.listViews) {
|
||||
objectListList.listViews[objType].model = Objects.currentObjects[objType]
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlmethod void ObjectLists::paramTypeIn(var parameter, var types)
|
||||
Checks if the type of the provided \c parameter is in \c types.
|
||||
\note The type can be normal string types ('boolean', 'string', 'number'...) or object types (Enum, Dictionay, Object types...). If the latter, only the type of object type should be provided in \c types. E.g: if you want to check if the parameter is an enum, add "Enum" to types.
|
||||
*/
|
||||
function paramTypeIn(parameter, types = []) {
|
||||
if(types.includes(parameter.toString())) return true
|
||||
if(typeof parameter == 'object' && 'type' in parameter)
|
||||
return types.includes(parameter.type)
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -1,217 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import "js/objects.js" as Objects
|
||||
import "js/mathlib.js" as MathLib
|
||||
import "js/historylib.js" as HistoryLib
|
||||
|
||||
/*!
|
||||
\qmltype PickLocationOverlay
|
||||
\inqmlmodule eu.ad5001.LogarithmPlotter
|
||||
\brief Overlay used to pick a new location for an object.
|
||||
|
||||
Provides an overlay over the canvas that can be shown when the user clicks the "Set position" button
|
||||
on a specific object. It allows the user to pick a new location on the canvas to place the object at.
|
||||
This overlay allows to set the precision of the pick as well as whether the pick should be on the plot grid.
|
||||
|
||||
\sa LogarithmPlotter, LogGraphCanvas
|
||||
*/
|
||||
Item {
|
||||
id: pickerRoot
|
||||
visible: false
|
||||
|
||||
/*!
|
||||
\qmlproperty var PickLocationOverlay::canvas
|
||||
logGraphCanvas instance.
|
||||
*/
|
||||
property var canvas
|
||||
/*!
|
||||
\qmlproperty string PickLocationOverlay::objType
|
||||
Type of object whose position the user is picking.
|
||||
*/
|
||||
property string objType: 'Point'
|
||||
/*!
|
||||
\qmlproperty string PickLocationOverlay::objType
|
||||
Name of the object whose position the user is picking.
|
||||
*/
|
||||
property string objName: 'A'
|
||||
/*!
|
||||
\qmlproperty bool PickLocationOverlay::pickX
|
||||
true if the user should be picking a position on the x axis.
|
||||
*/
|
||||
property bool pickX: true
|
||||
/*!
|
||||
\qmlproperty bool PickLocationOverlay::pickY
|
||||
true if the user should be picking a position on the y axis.
|
||||
*/
|
||||
property bool pickY: true
|
||||
/*!
|
||||
\qmlproperty string PickLocationOverlay::propertyX
|
||||
Name of the object's property whose x value is being changed.
|
||||
*/
|
||||
property string propertyX: 'x'
|
||||
/*!
|
||||
\qmlproperty string PickLocationOverlay::propertyY
|
||||
Name of the object's property whose y value is being changed.
|
||||
*/
|
||||
property string propertyY: 'y'
|
||||
/*!
|
||||
\qmlproperty int PickLocationOverlay::precision
|
||||
Precision of the picked value (post-dot precision).
|
||||
*/
|
||||
property alias precision: precisionSlider.value
|
||||
|
||||
Rectangle {
|
||||
color: sysPalette.window
|
||||
opacity: 0.35
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: picker
|
||||
anchors.fill: parent
|
||||
hoverEnabled: parent.visible
|
||||
cursorShape: Qt.CrossCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: {
|
||||
if(mouse.button == Qt.LeftButton) { // Validate
|
||||
if(parent.pickX) {
|
||||
let newValue = picked.mouseX.toString()
|
||||
newValue = {
|
||||
'Expression': () => new MathLib.Expression(newValue),
|
||||
'number': () => parseFloat(newValue)
|
||||
}[Objects.types[objType].properties()[propertyX]]()
|
||||
let obj = Objects.getObjectByName(objName, objType)
|
||||
history.addToHistory(new HistoryLib.EditedProperty(
|
||||
objName, objType, propertyX, obj[propertyX], newValue
|
||||
))
|
||||
obj[propertyX] = newValue
|
||||
obj.update()
|
||||
objectLists.update()
|
||||
}
|
||||
if(parent.pickY) {
|
||||
let newValue = picked.mouseY.toString()
|
||||
newValue = {
|
||||
'Expression': () => new MathLib.Expression(newValue),
|
||||
'number': () => parseFloat(newValue)
|
||||
}[Objects.types[objType].properties()[propertyY]]()
|
||||
let obj = Objects.getObjectByName(objName, objType)
|
||||
history.addToHistory(new HistoryLib.EditedProperty(
|
||||
objName, objType, propertyY, obj[propertyY], newValue
|
||||
))
|
||||
obj[propertyY] = newValue
|
||||
obj.update()
|
||||
objectLists.update()
|
||||
}
|
||||
}
|
||||
pickerRoot.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
height: precisionSlider.height
|
||||
Text {
|
||||
text: " "+ qsTr("Pointer precision:") + " "
|
||||
color: 'black'
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Slider {
|
||||
id: precisionSlider
|
||||
from: 0
|
||||
value: 2
|
||||
to: 10
|
||||
stepSize: 1
|
||||
ToolTip {
|
||||
parent: precisionSlider.handle
|
||||
visible: precisionSlider.pressed
|
||||
text: precisionSlider.value.toFixed(0)
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: snapToGridCheckbox
|
||||
text: qsTr("Snap to grid")
|
||||
checked: false
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: xCursor
|
||||
width: 1
|
||||
height: parent.height
|
||||
color: 'black'
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: canvas.x2px(picked.mouseX)
|
||||
visible: parent.pickX
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: yCursor
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: 'black'
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: canvas.y2px(picked.mouseY)
|
||||
visible: parent.pickY
|
||||
}
|
||||
|
||||
Text {
|
||||
id: picked
|
||||
x: picker.mouseX - width - 5
|
||||
y: picker.mouseY - height - 5
|
||||
property double axisX: canvas.xaxisstep1
|
||||
property double mouseX: {
|
||||
let xpos = canvas.px2x(picker.mouseX)
|
||||
if(snapToGridCheckbox.checked) {
|
||||
if(canvas.logscalex) {
|
||||
// Calculate the logged power
|
||||
let pow = Math.pow(10, Math.floor(Math.log10(xpos)))
|
||||
return pow*Math.round(xpos/pow)
|
||||
} else {
|
||||
return canvas.xaxisstep1*Math.round(xpos/canvas.xaxisstep1)
|
||||
}
|
||||
} else {
|
||||
return xpos.toFixed(parent.precision)
|
||||
}
|
||||
}
|
||||
property double mouseY: {
|
||||
let ypos = canvas.px2y(picker.mouseY)
|
||||
if(snapToGridCheckbox.checked) {
|
||||
return canvas.yaxisstep1*Math.round(ypos/canvas.yaxisstep1)
|
||||
} else {
|
||||
return ypos.toFixed(parent.precision)
|
||||
}
|
||||
}
|
||||
color: 'black'
|
||||
text: {
|
||||
if(parent.pickX && parent.pickY)
|
||||
return `(${mouseX}, ${mouseY})`
|
||||
if(parent.pickX)
|
||||
return `X = ${mouseX}`
|
||||
if(parent.pickY)
|
||||
return `Y = ${mouseY}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Dialogs 1.3 as D
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
/*!
|
||||
\qmltype About
|
||||
\inqmlmodule eu.ad5001.LogarithmPlotter.Popup
|
||||
\brief About popup of LogarithmPlotter.
|
||||
|
||||
\sa LogarithmPlotter
|
||||
*/
|
||||
D.Dialog {
|
||||
id: about
|
||||
title: qsTr("About LogarithmPlotter")
|
||||
width: 400
|
||||
height: 600
|
||||
|
||||
Image {
|
||||
id: logo
|
||||
source: "../icons/logarithmplotter.svg"
|
||||
sourceSize.width: 64
|
||||
sourceSize.height: 64
|
||||
width: 64
|
||||
height: 64
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.rightMargin: width/2
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
}
|
||||
|
||||
Label {
|
||||
id: appName
|
||||
anchors.top: logo.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: 10
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: 25
|
||||
text: qsTr("LogarithmPlotter v%1").arg(Helper.getVersion())
|
||||
}
|
||||
|
||||
Label {
|
||||
id: description
|
||||
anchors.top: appName.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: 10
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: 18
|
||||
text: qsTr("2D plotter software to make BODE plots, sequences and repartition functions.")
|
||||
}
|
||||
|
||||
Label {
|
||||
id: debugInfos
|
||||
anchors.top: description.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: 10
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: 14
|
||||
text: Helper.getDebugInfos()
|
||||
}
|
||||
|
||||
Label {
|
||||
id: copyrightInfos
|
||||
anchors.top: debugInfos.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
width: Math.min(410, parent.width)
|
||||
wrapMode: Text.WordWrap
|
||||
textFormat: Text.RichText
|
||||
font.pixelSize: 13
|
||||
text: "Copyright © 2022 Ad5001 <mail@ad5001.eu><br>
|
||||
<br>
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.<br>
|
||||
<br>
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.<br>
|
||||
<br>
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>."
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.top: copyrightInfos.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
spacing: 5
|
||||
|
||||
Button {
|
||||
id: openIssueButton
|
||||
text: qsTr('Report a bug')
|
||||
icon.name: 'tools-report-bug'
|
||||
onClicked: Qt.openUrlExternally('https://git.ad5001.eu/Ad5001/LogarithmPlotter')
|
||||
}
|
||||
|
||||
Button {
|
||||
id: officialWebsiteButton
|
||||
text: qsTr('Official website')
|
||||
icon.name: 'web-browser'
|
||||
onClicked: Qt.openUrlExternally('https://apps.ad5001.eu/logarithmplotter/')
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
/*!
|
||||
\qmltype GreetScreen
|
||||
\inqmlmodule eu.ad5001.LogarithmPlotter.Popup
|
||||
\brief Overlay displayed when LogarithmPlotter is launched for the first time or when it was just updated.
|
||||
|
||||
It contains several settings as well as an easy access to the changelog
|
||||
|
||||
\sa LogarithmPlotter, Settings, AppMenuBar, Changelog
|
||||
*/
|
||||
Popup {
|
||||
id: greetingPopup
|
||||
x: (parent.width-width)/2
|
||||
y: Math.max(20, (parent.height-height)/2)
|
||||
width: Math.max(welcome.width+70, checkForUpdatesSetting.width, resetRedoStackSetting.width)+20
|
||||
height: Math.min(parent.height-40, 500)
|
||||
modal: true
|
||||
focus: true
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
|
||||
Item {
|
||||
id: welcome
|
||||
height: logo.height
|
||||
width: logo.width + 10 + welcomeText.width
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: (parent.width-width)/2
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Image {
|
||||
id: logo
|
||||
source: "../icons/logarithmplotter.svg"
|
||||
sourceSize.width: 48
|
||||
sourceSize.height: 48
|
||||
width: 48
|
||||
height: 48
|
||||
}
|
||||
|
||||
Label {
|
||||
id: welcomeText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: logo.right
|
||||
anchors.leftMargin: 10
|
||||
//width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: 32
|
||||
text: qsTr("Welcome to LogarithmPlotter")
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: versionText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: welcome.bottom
|
||||
anchors.topMargin: 10
|
||||
//width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
width: implicitWidth
|
||||
font.pixelSize: 18
|
||||
font.italic: true
|
||||
text: qsTr("Version %1").arg(Helper.getVersion())
|
||||
}
|
||||
|
||||
Label {
|
||||
id: helpText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: versionText.bottom
|
||||
anchors.topMargin: 40
|
||||
//width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: 14
|
||||
width: parent.width - 50
|
||||
text: qsTr("Take a few seconds to configure LogarithmPlotter.\nThese settings can be changed at any time from the \"Settings\" menu.")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: checkForUpdatesSetting
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: helpText.bottom
|
||||
anchors.topMargin: 10
|
||||
checked: Helper.getSettingBool("check_for_updates")
|
||||
text: qsTr('Check for updates on startup (requires online connectivity)')
|
||||
onClicked: {
|
||||
Helper.setSettingBool("check_for_updates", checked)
|
||||
checkForUpdatesMenuSetting.checked = checked
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: resetRedoStackSetting
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: checkForUpdatesSetting.bottom
|
||||
checked: Helper.getSettingBool("reset_redo_stack")
|
||||
text: qsTr('Reset redo stack when a new action is added to history')
|
||||
onClicked: {
|
||||
Helper.setSettingBool("reset_redo_stack", checked)
|
||||
resetRedoStackMenuSetting.checked = checked
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 10
|
||||
spacing: 10
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Button {
|
||||
id: userManualBtn
|
||||
text: qsTr("User manual")
|
||||
font.pixelSize: 18
|
||||
onClicked: Qt.openUrlExternally("https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/_Sidebar")
|
||||
}
|
||||
|
||||
Button {
|
||||
id: changelogBtn
|
||||
text: qsTr("Changelog")
|
||||
font.pixelSize: 18
|
||||
onClicked: changelog.open()
|
||||
}
|
||||
|
||||
Button {
|
||||
id: doneBtn
|
||||
text: qsTr("Done")
|
||||
font.pixelSize: 18
|
||||
onClicked: greetingPopup.close()
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: if(Helper.getSetting("last_install_greet") != Helper.getVersion()) {
|
||||
greetingPopup.open()
|
||||
}
|
||||
|
||||
onClosed: Helper.setSetting("last_install_greet", Helper.getVersion())
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24.0px"
|
||||
height="24.0px"
|
||||
viewBox="0 0 24.0 24.0"
|
||||
version="1.1"
|
||||
id="SVGRoot"
|
||||
sodipodi:docname="Function.svg"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
||||
<defs
|
||||
id="defs833" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="31.678384"
|
||||
inkscape:cx="15.268708"
|
||||
inkscape:cy="12.238724"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid1403" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata10">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
<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 - Licensed under CC4.0-BY-NC-SA</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-nc-nd/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-nc-nd/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:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
id="rect1415"
|
||||
style="fill:#000000;fill-rule:evenodd"
|
||||
d="M 2,9 C 2,5 6,5 6,5 H 7 V 7 H 6 C 6,7 4,7 4,9 v 2 h 2 v 2 H 4 v 5 H 2 V 13 H 0 v -2 h 2 z"
|
||||
sodipodi:nodetypes="ccccccccccccccccc" />
|
||||
<path
|
||||
id="rect839"
|
||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
||||
d="m 12,3 v 2 c 0,0 -2,0 -2,7 0,7 2,7 2,7 v 2 C 8,21 8,12 8,12 8,12 8,3 12,3 Z"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
id="rect857"
|
||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
||||
d="m 12,10 h 2 l 6,8 h -2 z"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
id="rect857-7"
|
||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
||||
d="m 12,18 h 2 l 6,-8 h -2 z"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
id="rect839-3"
|
||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
||||
d="m 20,3 v 2 c 0,0 2,0 2,7 0,7 -2,7 -2,7 v 2 c 4,0 4,-9 4,-9 0,0 0,-9 -4,-9 z"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.6 KiB |
|
@ -1,92 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24.0px"
|
||||
height="24.0px"
|
||||
viewBox="0 0 24.0 24.0"
|
||||
version="1.1"
|
||||
id="SVGRoot"
|
||||
sodipodi:docname="Gain Bode.svg"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
||||
<defs
|
||||
id="defs1469" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="15.763196"
|
||||
inkscape:cy="7.8365971"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2039" />
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2058" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata1472">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:14.257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
|
||||
id="rect835"
|
||||
width="14"
|
||||
height="2"
|
||||
x="0"
|
||||
y="17" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:17.3333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
||||
x="-0.12666447"
|
||||
y="12.134649"
|
||||
id="text839"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan837"
|
||||
x="-0.12666447"
|
||||
y="12.134649"
|
||||
style="font-size:17.3333px">ω</tspan></text>
|
||||
<circle
|
||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
||||
id="path837"
|
||||
cx="13"
|
||||
cy="18"
|
||||
r="4" />
|
||||
<path
|
||||
id="rect837"
|
||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2.10035"
|
||||
transform="rotate(30)"
|
||||
d="m 20.686533,-6.169873 2.232051,-0.1339746 -0.06218,13.8923049 -2.23205,0.1339746 z"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.9 KiB |
|
@ -1,88 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24.0px"
|
||||
height="24.0px"
|
||||
viewBox="0 0 24.0 24.0"
|
||||
version="1.1"
|
||||
id="SVGRoot"
|
||||
sodipodi:docname="Phase Bode.svg"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
||||
<defs
|
||||
id="defs10" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.4"
|
||||
inkscape:cx="15.347905"
|
||||
inkscape:cy="8.3727678"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid19" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata13">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#000000;stroke-width:1.1547"
|
||||
id="rect26"
|
||||
width="8"
|
||||
height="2"
|
||||
x="5"
|
||||
y="18" />
|
||||
<path
|
||||
id="rect26-3"
|
||||
style="fill:#000000;stroke-width:1.22474"
|
||||
d="m 15,2 v 14 h 2 V 4 h 7 V 2 Z"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<circle
|
||||
style="fill:#000000;stroke-width:2.09999"
|
||||
id="path45"
|
||||
cx="16"
|
||||
cy="19"
|
||||
r="4" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:18px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;text-anchor:middle;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
x="6.4359932"
|
||||
y="11.702"
|
||||
id="text49"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan47"
|
||||
x="6.4359932"
|
||||
y="11.702"
|
||||
style="font-size:18px;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none">φ</tspan></text>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path style="opacity:1;vector-effect:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0" d="M9.98 18.12a3.5 3.5 0 0 1-3.064 3.855 3.5 3.5 0 0 1-3.887-3.022 3.5 3.5 0 0 1 2.982-3.919 3.5 3.5 0 0 1 3.95 2.94"/><text xml:space="preserve" style="font-style:normal;font-weight:400;font-size:17.3373px;line-height:1.25;font-family:sans-serif;letter-spacing:0;word-spacing:0;fill-opacity:1;stroke:none;stroke-width:1.00023" x="12.067" y="13.923" transform="scale(.99447 1.00556)"><tspan x="12.067" y="13.923" style="font-size:17.3373px;stroke-width:1.00023">A</tspan></text></svg>
|
Before Width: | Height: | Size: 772 B |
|
@ -1,103 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24.0px"
|
||||
height="24.0px"
|
||||
viewBox="0 0 24.0 24.0"
|
||||
version="1.1"
|
||||
id="SVGRoot"
|
||||
sodipodi:docname="Sequence.svg"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
||||
<defs
|
||||
id="defs10" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.4"
|
||||
inkscape:cx="-1.4929284"
|
||||
inkscape:cy="9.7261905"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="true"
|
||||
showguides="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid19" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata13">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:17.3333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;text-anchor:middle"
|
||||
x="7.483983"
|
||||
y="16.134649"
|
||||
id="text908"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan906"
|
||||
x="7.483983"
|
||||
y="16.134649"
|
||||
style="font-size:17.3333px">u</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:17px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;text-anchor:middle;stroke-width:1.00003"
|
||||
x="16.365566"
|
||||
y="18.663307"
|
||||
id="text912"
|
||||
transform="scale(1.0000324,0.9999676)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan910"
|
||||
x="16.365566"
|
||||
y="18.663307"
|
||||
style="font-size:17px;stroke-width:1.00003">n</tspan></text>
|
||||
<g
|
||||
aria-label="("
|
||||
id="text852"
|
||||
style="font-style:normal;font-weight:normal;font-size:12px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.4396;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(1.0022756,0,0,1.2616817,-0.26079098,-9.0560687)">
|
||||
<path
|
||||
d="M 2.712,9.86 C 1.536,11.528 0.48,12.956 0.48,15.8 c 0,2.844 1.056,4.272 2.232,5.94 L 3.408,21.26 C 2.328,19.664 1.632,18.368 1.632,15.8 c 0,-2.58 0.696,-3.864 1.776,-5.46 z"
|
||||
style="font-size:12px;stroke:#000000;stroke-width:0.4396;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path854" />
|
||||
</g>
|
||||
<g
|
||||
aria-label="("
|
||||
id="text852-3"
|
||||
style="font-style:normal;font-weight:normal;font-size:12px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.4396;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(-1.0030304,0,0,1.2658306,24.414952,-9.1000412)">
|
||||
<path
|
||||
d="M 2.712,9.86 C 1.536,11.528 0.48,12.956 0.48,15.8 c 0,2.844 1.056,4.272 2.232,5.94 L 3.408,21.26 C 2.328,19.664 1.632,18.368 1.632,15.8 c 0,-2.58 0.696,-3.864 1.776,-5.46 z"
|
||||
style="font-size:12px;stroke:#000000;stroke-width:0.4396;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path854-6" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.9 KiB |
|
@ -1,96 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24.0px"
|
||||
height="24.0px"
|
||||
viewBox="0 0 24.0 24.0"
|
||||
version="1.1"
|
||||
id="SVGRoot"
|
||||
sodipodi:docname="Somme gains Bode.svg"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
||||
<defs
|
||||
id="defs1469" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="63.356768"
|
||||
inkscape:cx="15.947723"
|
||||
inkscape:cy="5.6917309"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2039" />
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2058" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata1472">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
id="rect838"
|
||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
||||
d="M 2,2 H 8 V 3 H 4 L 7,6 4,9 h 4 v 1 H 2 V 9 L 5,6 2,3 Z"
|
||||
sodipodi:nodetypes="ccccccccccccc" />
|
||||
<rect
|
||||
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:14.257;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
|
||||
id="rect835"
|
||||
width="14"
|
||||
height="2"
|
||||
x="0"
|
||||
y="17" />
|
||||
<circle
|
||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
||||
id="path837"
|
||||
cx="13"
|
||||
cy="18"
|
||||
r="4" />
|
||||
<path
|
||||
id="rect837"
|
||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2.10035"
|
||||
transform="rotate(30)"
|
||||
d="m 20.686533,-6.169873 2.232051,-0.1339746 -0.06218,13.8923049 -2.23205,0.1339746 z"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<g
|
||||
aria-label="G"
|
||||
id="text846"
|
||||
style="font-style:normal;font-weight:normal;font-size:12px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none">
|
||||
<path
|
||||
d="m 13,2 c 0,0 -3,0 -3,3 0,3 0,5 3,5 2,0 3,0 3,-2 V 6 h -3 v 1 h 2 v 1 c 0,1 -1,1 -2,1 -1,0 -2,0 -2,-4 0,-1 1,-2 2,-2 2,0 2,1 2,1 h 1 c 0,0 0,-2 -3,-2 z"
|
||||
style="font-size:12px"
|
||||
id="path848"
|
||||
sodipodi:nodetypes="sssccccccsssccs" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.1 KiB |
|
@ -1,93 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24.0px"
|
||||
height="24.0px"
|
||||
viewBox="0 0 24.0 24.0"
|
||||
version="1.1"
|
||||
id="SVGRoot"
|
||||
sodipodi:docname="Somme gains Bode.svg"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
||||
<defs
|
||||
id="defs1469" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.4"
|
||||
inkscape:cx="22.985246"
|
||||
inkscape:cy="9.8906279"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2039" />
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2058" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata1472">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
id="path1414"
|
||||
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
|
||||
d="m 19.979376,19.120606 a 3.5,3.5 0 0 1 -3.06333,3.854578 3.5,3.5 0 0 1 -3.886652,-3.022533 3.5,3.5 0 0 1 2.9814,-3.918293 3.5,3.5 0 0 1 3.9495,2.939936" />
|
||||
<rect
|
||||
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:16.166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
|
||||
id="rect835"
|
||||
width="18"
|
||||
height="2"
|
||||
x="0"
|
||||
y="18.5" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:16px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
||||
x="-0.5703125"
|
||||
y="11.875"
|
||||
id="text839"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan837"
|
||||
x="-0.5703125"
|
||||
y="11.875"
|
||||
style="font-size:16px">ΣG</tspan></text>
|
||||
<rect
|
||||
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:19.0663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0"
|
||||
id="rect835-3"
|
||||
width="25.038315"
|
||||
height="2"
|
||||
x="-10.17229"
|
||||
y="23.748709"
|
||||
ry="0"
|
||||
transform="rotate(-60)" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.3 KiB |
|
@ -1,92 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24.0px"
|
||||
height="24.0px"
|
||||
viewBox="0 0 24.0 24.0"
|
||||
version="1.1"
|
||||
id="SVGRoot"
|
||||
sodipodi:docname="Somme phases Bode.svg"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
||||
<defs
|
||||
id="defs10" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.4"
|
||||
inkscape:cx="15.347905"
|
||||
inkscape:cy="8.3727678"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid19" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata13">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#000000;stroke-width:1.41421"
|
||||
id="rect26"
|
||||
width="12"
|
||||
height="2"
|
||||
x="9"
|
||||
y="18" />
|
||||
<rect
|
||||
style="fill:#000000;stroke-width:0.912867"
|
||||
id="rect26-3"
|
||||
width="5"
|
||||
height="2"
|
||||
x="19"
|
||||
y="2" />
|
||||
<rect
|
||||
style="fill:#000000;stroke-width:1.5"
|
||||
id="rect43"
|
||||
width="2"
|
||||
height="16"
|
||||
x="19"
|
||||
y="4" />
|
||||
<circle
|
||||
style="fill:#000000;stroke-width:2.09999"
|
||||
id="path45"
|
||||
cx="20"
|
||||
cy="19"
|
||||
r="3.5" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;text-anchor:middle"
|
||||
x="8.7617188"
|
||||
y="11.664062"
|
||||
id="text49"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan47"
|
||||
x="8.7617188"
|
||||
y="11.664062"
|
||||
style="font-size:16px">Σφ</tspan></text>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.4 KiB |
|
@ -1,77 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24.0px"
|
||||
height="24.0px"
|
||||
viewBox="0 0 24.0 24.0"
|
||||
version="1.1"
|
||||
id="SVGRoot"
|
||||
sodipodi:docname="Text.svg"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
||||
<defs
|
||||
id="defs1469" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.4"
|
||||
inkscape:cx="13.763421"
|
||||
inkscape:cy="16.975675"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2039" />
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2058" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata1472">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
aria-label="X"
|
||||
transform="matrix(0.99446808,0,0,1.0055627,2,0.50000001)"
|
||||
id="text14"
|
||||
style="font-style:normal;font-weight:normal;font-size:17.3373px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.00023" />
|
||||
<path
|
||||
id="rect12"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.670999;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 4,3 h 8 V 5 H 9 v 14 h 3 v 2 H 4 V 19 H 7 V 5 H 4 Z"
|
||||
sodipodi:nodetypes="ccccccccccccc" />
|
||||
<path
|
||||
id="rect837"
|
||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
||||
d="m 16,5 h 2 v 3 h 2 v 2 h -2 v 5 c 0,2 2,2 2,2 v 2 c 0,0 -4,0 -4,-4 V 10 H 14 V 8 h 2 z"
|
||||
sodipodi:nodetypes="ccccccccccccccc" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
|
@ -1,85 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24.0px"
|
||||
height="24.0px"
|
||||
viewBox="0 0 24.0 24.0"
|
||||
version="1.1"
|
||||
id="SVGRoot"
|
||||
sodipodi:docname="X Cursor.svg"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2021-09-07)">
|
||||
<defs
|
||||
id="defs1469" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.4"
|
||||
inkscape:cx="19.545462"
|
||||
inkscape:cy="13.163586"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="true"
|
||||
inkscape:window-width="1829"
|
||||
inkscape:window-height="916"
|
||||
inkscape:window-x="19"
|
||||
inkscape:window-y="31"
|
||||
inkscape:window-maximized="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2039" />
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2058" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata1472">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
aria-label="X"
|
||||
transform="matrix(0.99446808,0,0,1.0055627,-2.2765848e-8,0.50000001)"
|
||||
id="text14"
|
||||
style="font-style:normal;font-weight:normal;font-size:17.3373px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.00023" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.774803;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect12"
|
||||
width="2"
|
||||
height="24"
|
||||
x="17"
|
||||
y="0"
|
||||
ry="2.14841e-13" />
|
||||
<path
|
||||
id="rect835"
|
||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
||||
d="m 5,2 h 2 l 8,14 h -2 z"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
id="rect835-6"
|
||||
style="fill:#000000;fill-rule:evenodd;stroke-width:2"
|
||||
d="M 15,2 H 13 L 5,16 h 2 z"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.6 KiB |
|
@ -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/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
|
|
@ -1 +0,0 @@
|
|||
../../common/angle.svg
|
|
@ -1,58 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
var themeTextColor;
|
||||
|
||||
|
||||
class Action {
|
||||
// Type of the action done.
|
||||
type(){return 'Unknown'}
|
||||
|
||||
// Icon associated with the item
|
||||
|
||||
// TargetName is the name of the object that's targeted by the event.
|
||||
constructor(targetName = "", targetType = "Point") {
|
||||
this.targetName = targetName
|
||||
this.targetType = targetType
|
||||
}
|
||||
|
||||
undo() {}
|
||||
|
||||
redo() {}
|
||||
|
||||
export() {
|
||||
return [this.targetName, this.targetType]
|
||||
}
|
||||
|
||||
// String used in the toolkit
|
||||
getReadableString() {
|
||||
return 'Unknown action'
|
||||
}
|
||||
|
||||
// Returns an HTML tag containing the icon of a type
|
||||
getIconRichText(type) {
|
||||
return `<img source="../icons/objects/${type}.svg" style="color: ${themeTextColor};" width=18 height=18></img>`
|
||||
}
|
||||
|
||||
// String used in the preview
|
||||
getHTMLString() {
|
||||
return this.getReadableString()
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "../objects.js" as Objects
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../objs/common.js" as Common
|
||||
.import "common.js" as C
|
||||
|
||||
class EditedProperty extends C.Action {
|
||||
// Action used everytime an object's property has been changed
|
||||
type(){return 'EditedProperty'}
|
||||
|
||||
icon(){return 'modify'}
|
||||
|
||||
color(darkVer=false){
|
||||
return darkVer ? 'darkslateblue' : 'cyan';
|
||||
}
|
||||
|
||||
constructor(targetName = "", targetType = "Point", targetProperty = "visible", previousValue = false, newValue = true, valueIsExpressionNeedingImport = false) {
|
||||
super(targetName, targetType)
|
||||
this.targetProperty = targetProperty
|
||||
this.targetPropertyReadable = qsTranslate("prop", this.targetProperty)
|
||||
this.previousValue = previousValue
|
||||
this.newValue = newValue
|
||||
this.propertyType = Objects.types[targetType].properties()[targetProperty]
|
||||
if(valueIsExpressionNeedingImport) {
|
||||
if(this.propertyType == "Expression") {
|
||||
this.previousValue = new MathLib.Expression(this.previousValue);
|
||||
this.newValue = new MathLib.Expression(this.newValue);
|
||||
} else if(this.propertyType == "Domain") {
|
||||
this.previousValue = MathLib.parseDomain(this.previousValue);
|
||||
this.newValue = MathLib.parseDomain(this.newValue);
|
||||
} else {
|
||||
// Objects
|
||||
this.previousValue = Objects.getObjectByName(this.previousValue);
|
||||
this.newValue = Objects.getObjectByName(this.newValue);
|
||||
}
|
||||
}
|
||||
this.setReadableValues()
|
||||
}
|
||||
|
||||
undo() {
|
||||
Objects.getObjectByName(this.targetName, this.targetType)[this.targetProperty] = this.previousValue
|
||||
}
|
||||
|
||||
redo() {
|
||||
Objects.getObjectByName(this.targetName, this.targetType)[this.targetProperty] = this.newValue
|
||||
}
|
||||
|
||||
export() {
|
||||
if(this.previousValue instanceof MathLib.Expression) {
|
||||
return [this.targetName, this.targetType, this.targetProperty, this.previousValue.toEditableString(), this.newValue.toEditableString(), true]
|
||||
} else if(this.previousValue instanceof Common.DrawableObject) {
|
||||
return [this.targetName, this.targetType, this.targetProperty, this.previousValue.name, this.newValue.name, true]
|
||||
} else {
|
||||
return [this.targetName, this.targetType, this.targetProperty, this.previousValue, this.newValue, false]
|
||||
}
|
||||
}
|
||||
|
||||
setReadableValues() {
|
||||
this.prev = "";
|
||||
this.next = "";
|
||||
if(this.propertyType instanceof Object) {
|
||||
switch(this.propertyType.type) {
|
||||
case "Enum":
|
||||
this.prev = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.previousValue)]
|
||||
this.next = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.newValue)]
|
||||
break;
|
||||
case "ObjectType":
|
||||
this.prev = this.previousValue == null ? "null" : this.previousValue.name
|
||||
this.next = this.newValue == null ? "null" : this.newValue.name
|
||||
break;
|
||||
case "List":
|
||||
this.prev = this.previousValue.join(",")
|
||||
this.next = this.newValue.name.join(",")
|
||||
break;
|
||||
case "Dict":
|
||||
this.prev = JSON.stringify(this.previousValue).replace("'", "\\'").replace('"', "'")
|
||||
this.next = JSON.stringify(this.newValue).replace("'", "\\'").replace('"', "'")
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this.prev = this.previousValue == null ? "null" : this.previousValue.toString()
|
||||
this.next = this.newValue == null ? "null" : this.newValue.toString()
|
||||
}
|
||||
}
|
||||
|
||||
getReadableString() {
|
||||
return qsTr('%1 of %2 %3 changed from "%4" to "%5".')
|
||||
.arg(this.targetPropertyReadable)
|
||||
.arg(Objects.types[this.targetType].displayType())
|
||||
.arg(this.targetName).arg(this.prev).arg(this.next)
|
||||
}
|
||||
|
||||
getHTMLString() {
|
||||
return qsTr('%1 of %2 changed from %3 to %4.')
|
||||
.arg(this.targetPropertyReadable)
|
||||
.arg('<b style="font-size: 15px;"> ' + this.targetName + ' </b>')
|
||||
.arg('<tt style="background: rgba(128,128,128,0.1);"> '+this.prev+' </tt>')
|
||||
.arg('<tt style="background: rgba(128,128,128,0.1);"> '+this.next+'</tt>')
|
||||
// .arg('<b style="font-size: 15px;">' + Objects.types[this.targetType].displayType())
|
||||
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "../expr-eval.js" as ExprEval
|
||||
.import "../utils.js" as Utils
|
||||
.import "latex.js" as Latex
|
||||
|
||||
var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manualy
|
||||
"pi": Math.PI,
|
||||
"π": Math.PI,
|
||||
"inf": Infinity,
|
||||
"Infinity": Infinity,
|
||||
"∞": Infinity,
|
||||
"e": Math.E,
|
||||
"E": Math.E
|
||||
}
|
||||
|
||||
var currentVars = {}
|
||||
|
||||
const parser = new ExprEval.Parser()
|
||||
parser.functions.integral = function(a, b, f, variable) {
|
||||
// https://en.wikipedia.org/wiki/Simpson%27s_rule
|
||||
f = parser.parse(f).toJSFunction(variable, currentVars)
|
||||
return (b-a)/6*(f(a)+4*f((a+b)/2)+f(b))
|
||||
}
|
||||
|
||||
const DERIVATION_PRECISION = 0.1
|
||||
|
||||
parser.functions.derivative = function(f, variable, x) {
|
||||
f = parser.parse(f).toJSFunction(variable, currentVars)
|
||||
return (f(x+DERIVATION_PRECISION/2)-f(x-DERIVATION_PRECISION/2))/DERIVATION_PRECISION
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as C
|
||||
.import "latex.js" as Latex
|
||||
.import "../utils.js" as Utils
|
||||
|
||||
/**
|
||||
* Represents any kind of x-based or non variable based expression.
|
||||
*/
|
||||
class Expression {
|
||||
constructor(expr) {
|
||||
this.expr = expr
|
||||
this.calc = C.parser.parse(expr).simplify()
|
||||
this.cached = this.isConstant()
|
||||
this.cachedValue = this.cached ? this.calc.evaluate(C.evalVariables) : null
|
||||
this.latexMarkup = Latex.expressionToLatex(this.calc.tokens)
|
||||
}
|
||||
|
||||
isConstant() {
|
||||
return !this.expr.includes("x") && !this.expr.includes("n")
|
||||
}
|
||||
|
||||
execute(x = 1) {
|
||||
if(this.cached) return this.cachedValue
|
||||
C.currentVars = Object.assign({'x': x}, C.evalVariables)
|
||||
return this.calc.evaluate(C.currentVars)
|
||||
}
|
||||
|
||||
simplify(x) {
|
||||
var expr = this.calc.substitute('x', x).simplify()
|
||||
if(expr.evaluate(C.evalVariables) == 0) return '0'
|
||||
var str = Utils.makeExpressionReadable(expr.toString());
|
||||
if(str != undefined && str.match(/^\d*\.\d+$/)) {
|
||||
if(str.split('.')[1].split('0').length > 7) {
|
||||
// Likely rounding error
|
||||
str = parseFloat(str.substring(0, str.length-1)).toString();
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
duplicate() {
|
||||
return new Expression(this.toEditableString())
|
||||
}
|
||||
|
||||
toEditableString() {
|
||||
return this.calc.toString()
|
||||
}
|
||||
|
||||
toString(forceSign=false) {
|
||||
var str = Utils.makeExpressionReadable(this.calc.toString())
|
||||
if(str[0] != '-' && forceSign) str = '+' + str
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
function executeExpression(expr){
|
||||
return (new Expression(expr.toString())).execute()
|
||||
}
|
|
@ -1,246 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "../expr-eval.js" as ExprEval
|
||||
|
||||
/**
|
||||
* Puts element within parenthesis.
|
||||
*
|
||||
* @param {string} elem - element to put within parenthesis.
|
||||
* @returns {string}
|
||||
*/
|
||||
function par(elem) {
|
||||
return '(' + elem + ')'
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the element contains at least one of the elements of
|
||||
* the string array contents , and returns the parenthesis version if so.
|
||||
*
|
||||
* @param {string} elem - element to put within parenthesis.
|
||||
* @param {Array} contents - Array of elements to put within parenthesis.
|
||||
* @returns {string}
|
||||
*/
|
||||
function parif(elem, contents) {
|
||||
return contents.some(x => elem.toString().includes(x)) ? par(elem) : elem
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a latex expression for a function.
|
||||
*
|
||||
* @param {string} f - Function to convert
|
||||
* @param {Array} args - Arguments of the function
|
||||
* @returns {string}
|
||||
*/
|
||||
function functionToLatex(f, args) {
|
||||
switch(f) {
|
||||
case "derivative":
|
||||
return '\\frac{d' + args[0].substr(1, args[0].length-2).replace(new RegExp(by, 'g'), 'x') + '}{dx}';
|
||||
break;
|
||||
case "integral":
|
||||
return '\\int\\limits^{' + args[0] + '}_{' + args[1] + '}' + args[2].substr(1, args[2].length-2) + ' d' + args[3];
|
||||
break;
|
||||
case "sqrt":
|
||||
return '\\sqrt\\left(' + args.join(', ') + '\\right)';
|
||||
break;
|
||||
case "abs":
|
||||
return '\\left|' + args.join(', ') + '\\right|';
|
||||
break;
|
||||
case "floor":
|
||||
return '\\left\\lfloor' + args.join(', ') + '\\right\\rfloor';
|
||||
break;
|
||||
case "ceil":
|
||||
return '\\left\\lceil' + args.join(', ') + '\\right\\rceil';
|
||||
break;
|
||||
default:
|
||||
return '\\mathrm{' + f + '}\\left(' + args.join(', ') + '\\right)';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a latex variable from a variable.
|
||||
*
|
||||
* @param {string} vari - variable to convert
|
||||
* @returns {string}
|
||||
*/
|
||||
function variableToLatex(vari) {
|
||||
unicodechars = ["α","β","γ","δ","ε","ζ","η",
|
||||
"π","θ","κ","λ","μ","ξ","ρ",
|
||||
"ς","σ","τ","φ","χ","ψ","ω",
|
||||
"Γ","Δ","Θ","Λ","Ξ","Π","Σ",
|
||||
"Φ","Ψ","Ω","ₐ","ₑ","ₒ","ₓ",
|
||||
"ₕ","ₖ","ₗ","ₘ","ₙ","ₚ","ₛ",
|
||||
"ₜ","¹","²","³","⁴","⁵","⁶",
|
||||
"⁷","⁸","⁹","⁰","₁","₂","₃",
|
||||
"₄","₅","₆","₇","₈","₉","₀"]
|
||||
equivalchars = ["alpha","beta","gamma","delta","epsilon","zeta","eta",
|
||||
"pi","theta","kappa","lambda","mu","xi","rho",
|
||||
"sigma","sigma","tau","phi","chi","psi","omega",
|
||||
"Gamma","Delta","Theta","Lambda","Xi","Pi","Sigma",
|
||||
"Phy","Psi","Omega","{}_{a}","{}_{e}","{}_{o}","{}_{x}",
|
||||
"{}_{h}","{}_{k}","{}_{l}","{}_{m}","{}_{n}","{}_{p}","{}_{s}",
|
||||
"{}_{t}","{}^{1}","{}^{2}","{}^{3}","{}^{4}","{}^{5}","{}^{6}",
|
||||
"{}^{7}","{}^{8}","{}^{9}","{}^{0}","{}_{1}","{}_{2}","{}_{3}",
|
||||
"{}_{4}","{}_{5}","{}_{6}","{}_{7}","{}_{8}","{}_{9}","{}_{0}"]
|
||||
for(int i = 0; i < unicodechars.length; i++) {
|
||||
if(vari.includes(unicodechars[i]))
|
||||
vari = vari.replaceAll(unicodechars[i], equivalchars[i])
|
||||
}
|
||||
return vari;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts expr-eval tokens to a latex string.
|
||||
*
|
||||
* @param {Array} tokens - expr-eval tokens list
|
||||
* @returns {string}
|
||||
*/
|
||||
function expressionToLatex(tokens) {
|
||||
var nstack = [];
|
||||
var n1, n2, n3;
|
||||
var f, args, argCount;
|
||||
for (var i = 0; i < tokens.length; i++) {
|
||||
var item = tokens[i];
|
||||
var type = item.type;
|
||||
|
||||
switch(type) {
|
||||
case ExprEval.INUMBER:
|
||||
if (typeof item.value === 'number' && item.value < 0) {
|
||||
nstack.push(par(item.value));
|
||||
} else if (Array.isArray(item.value)) {
|
||||
nstack.push('[' + item.value.map(ExprEval.escapeValue).join(', ') + ']');
|
||||
} else {
|
||||
nstack.push(ExprEval.escapeValue(item.value));
|
||||
}
|
||||
break;
|
||||
case ExprEval.IOP2:
|
||||
n2 = nstack.pop();
|
||||
n1 = nstack.pop();
|
||||
f = item.value;
|
||||
switch(f) {
|
||||
case '-':
|
||||
case '+':
|
||||
nstack.push(n1 + this.ope + n2);
|
||||
break;
|
||||
case '||':
|
||||
case 'or':
|
||||
case '&&':
|
||||
case 'and':
|
||||
case '==':
|
||||
case '!=':
|
||||
nstack.push(par(n1) + this.ope + par(n2));
|
||||
break;
|
||||
case '*':
|
||||
nstack.push(parif(n1,['+','-']) + " \\times " + parif(n2,['+','-']));
|
||||
break;
|
||||
case '/':
|
||||
nstack.push("\\frac{" + n1 + "}{" + n2 + "}");
|
||||
break;
|
||||
case '^':
|
||||
nstack.push(parif(n1,['+','-','*','/','!']) + "^{" + n2 + "}");
|
||||
break;
|
||||
case '%':
|
||||
nstack.push(parif(n1,['+','-','*','/','!','^']) + " \\mathrm{mod} " + parif(n2,['+','-','*','/','!','^']));
|
||||
break;
|
||||
case '[':
|
||||
nstack.push(n1 + '[' + n2 + ']');
|
||||
break;
|
||||
default:
|
||||
throw new EvalError("Unknown operator " + ope + ".");
|
||||
}
|
||||
break;
|
||||
case ExprEval.IOP3: // Thirdiary operator
|
||||
n3 = nstack.pop();
|
||||
n2 = nstack.pop();
|
||||
n1 = nstack.pop();
|
||||
f = item.value;
|
||||
if (f === '?') {
|
||||
nstack.push('(' + n1 + ' ? ' + n2 + ' : ' + n3 + ')');
|
||||
} else {
|
||||
throw new EvalError('Unknown operator ' + ope + '.');
|
||||
}
|
||||
break;
|
||||
case ExprEval.IVAR:
|
||||
case ExprEval.IVARNAME:
|
||||
nstack.push(variableToLatex(item.value));
|
||||
break;
|
||||
case ExprEval.IOP1: // Unary operator
|
||||
n1 = nstack.pop();
|
||||
f = item.value;
|
||||
switch(f) {
|
||||
case '-':
|
||||
case '+':
|
||||
nstack.push(par(f + n1));
|
||||
break;
|
||||
case '!':
|
||||
nstack.push(parif(n1,['+','-','*','/','^']) + '!');
|
||||
break;
|
||||
default:
|
||||
nstack.push(f + parif(n1,['+','-','*','/','^']));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ExprEval.IFUNCALL:
|
||||
argCount = item.value;
|
||||
args = [];
|
||||
while (argCount-- > 0) {
|
||||
args.unshift(nstack.pop());
|
||||
}
|
||||
f = nstack.pop();
|
||||
// Handling various functions
|
||||
nstack.push(functionToLatex(f, args))
|
||||
case ExprEval.IFUNDEF:
|
||||
nstack.push(par(n1 + '(' + args.join(', ') + ') = ' + n2));
|
||||
break;
|
||||
case ExprEval.IMEMBER:
|
||||
n1 = nstack.pop();
|
||||
nstack.push(n1 + '.' + item.value);
|
||||
break;
|
||||
case ExprEval.IARRAY:
|
||||
argCount = item.value;
|
||||
args = [];
|
||||
while (argCount-- > 0) {
|
||||
args.unshift(nstack.pop());
|
||||
}
|
||||
nstack.push('[' + args.join(', ') + ']');
|
||||
break;
|
||||
case ExprEval.IEXPR:
|
||||
nstack.push('(' + expressionToLatex(item.value) + ')');
|
||||
break;
|
||||
case ExprEval.IENDSTATEMENT:
|
||||
break;
|
||||
default:
|
||||
throw new EvalError('invalid Expression');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nstack.length > 1) {
|
||||
if (toJS) {
|
||||
nstack = [ nstack.join(',') ];
|
||||
} else {
|
||||
nstack = [ nstack.join(';') ];
|
||||
}
|
||||
}
|
||||
console.log(nstack[0]);
|
||||
return String(nstack[0]);
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as C
|
||||
.import "expression.js" as Expr
|
||||
.import "../utils.js" as Utils
|
||||
|
||||
/**
|
||||
* Represents mathematical object for sequences.
|
||||
*/
|
||||
class Sequence extends Expr.Expression {
|
||||
constructor(name, baseValues = {}, valuePlus = 1, expr = "") {
|
||||
// u[n+valuePlus] = expr
|
||||
super(expr)
|
||||
this.name = name
|
||||
this.baseValues = baseValues
|
||||
this.calcValues = Object.assign({}, baseValues)
|
||||
for(var n in this.calcValues)
|
||||
if(['string', 'number'].includes(typeof this.calcValues[n]))
|
||||
this.calcValues[n] = parser.parse(this.calcValues[n].toString()).simplify().evaluate(C.evalVariables)
|
||||
this.valuePlus = parseInt(valuePlus)
|
||||
}
|
||||
|
||||
isConstant() {
|
||||
return this.expr.indexOf("n") == -1
|
||||
}
|
||||
|
||||
execute(n = 1) {
|
||||
if(n in this.calcValues)
|
||||
return this.calcValues[n]
|
||||
this.cache(n)
|
||||
return this.calcValues[n]
|
||||
}
|
||||
|
||||
simplify(n = 1) {
|
||||
if(n in this.calcValues)
|
||||
return Utils.makeExpressionReadable(this.calcValues[n].toString())
|
||||
this.cache(n)
|
||||
return Utils.makeExpressionReadable(this.calcValues[n].toString())
|
||||
}
|
||||
|
||||
cache(n = 1) {
|
||||
var str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString())
|
||||
var expr = parser.parse(str).simplify()
|
||||
var l = {'n': n-this.valuePlus} // Just in case, add n (for custom functions)
|
||||
l[this.name] = this.calcValues
|
||||
currentVars = Object.assign(l, C.evalVariables)
|
||||
this.calcValues[n] = expr.evaluate(currentVars)
|
||||
}
|
||||
|
||||
toString(forceSign=false) {
|
||||
var str = Utils.makeExpressionReadable(this.calc.toString())
|
||||
if(str[0] != '-' && forceSign) str = '+' + str
|
||||
var subtxt = this.valuePlus == 0 ? 'ₙ' : Utils.textsub('n+' + this.valuePlus)
|
||||
var ret = `${this.name}${subtxt} = ${str}${this.baseValues.length == 0 ? '' : "\n"}`
|
||||
ret += Object.keys(this.baseValues).map(
|
||||
n => `${this.name}${Utils.textsub(n)} = ${this.baseValues[n]}`
|
||||
).join('; ')
|
||||
return ret
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "utils.js" as Utils
|
||||
.import "mathlib.js" as MathLib
|
||||
.import "parameters.js" as P
|
||||
|
||||
var types = {}
|
||||
|
||||
var currentObjects = {}
|
||||
|
||||
function getObjectByName(objName, objType = null) {
|
||||
var objectTypes = Object.keys(currentObjects)
|
||||
if(typeof objType == 'string' && objType != "") {
|
||||
if(objType == "ExecutableObject") {
|
||||
objectTypes = getExecutableTypes()
|
||||
} else if(currentObjects[objType] != undefined) {
|
||||
objectTypes = [objType]
|
||||
}
|
||||
}
|
||||
if(Array.isArray(objType)) objectTypes = objType
|
||||
var retObj = null
|
||||
if(objName != "" && objName != null) {
|
||||
objectTypes.forEach(function(objType){
|
||||
if(currentObjects[objType] == undefined) return null
|
||||
currentObjects[objType].forEach(function(obj){
|
||||
if(obj.name == objName) retObj = obj
|
||||
})
|
||||
})
|
||||
}
|
||||
return retObj
|
||||
}
|
||||
|
||||
function getObjectsName(objType) {
|
||||
if(objType == "ExecutableObject") {
|
||||
var types = getExecutableTypes()
|
||||
var elementNames = ['']
|
||||
types.forEach(function(elemType){
|
||||
elementNames = elementNames.concat(currentObjects[elemType].map(obj => obj.name))
|
||||
})
|
||||
return elementNames
|
||||
}
|
||||
if(currentObjects[objType] == undefined) return []
|
||||
return currentObjects[objType].map(obj => obj.name)
|
||||
}
|
||||
|
||||
function getExecutableTypes() {
|
||||
return Object.keys(currentObjects).filter(objType => types[objType].executable())
|
||||
}
|
||||
|
||||
function createNewRegisteredObject(objType, args=[]) {
|
||||
if(Object.keys(types).indexOf(objType) == -1) return null // Object type does not exist.
|
||||
var newobj = new types[objType](...args)
|
||||
if(Object.keys(currentObjects).indexOf(objType) == -1) {
|
||||
currentObjects[objType] = []
|
||||
}
|
||||
currentObjects[objType].push(newobj)
|
||||
return newobj
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "../utils.js" as Utils
|
||||
.import "../objects.js" as Objects
|
||||
|
||||
// This file contains the default data to be imported from all other objects
|
||||
|
||||
|
||||
function getNewName(allowedLetters) {
|
||||
// Allows to get a new name, based on the allowed letters,
|
||||
// as well as adding a sub number when needs be.
|
||||
var newid = 0
|
||||
var ret
|
||||
do {
|
||||
var letter = allowedLetters[newid % allowedLetters.length]
|
||||
var num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length)
|
||||
ret = letter + (num > 0 ? Utils.textsub(num-1) : '')
|
||||
newid += 1
|
||||
} while(Objects.getObjectByName(ret) != null)
|
||||
return ret
|
||||
}
|
||||
|
||||
class DrawableObject {
|
||||
// Class to extend for every type of object that
|
||||
// can be drawn on the canvas.
|
||||
|
||||
// Base name of the object. Needs to be constant over time.
|
||||
static type(){return 'Unknown'}
|
||||
|
||||
// (Potentially translated) name of the object to be shown to the user.
|
||||
static displayType(){return 'Unknown'}
|
||||
|
||||
// Label used for the list on the ObjectsList sidebar.
|
||||
static displayTypeMultiple(){return 'Unknowns'}
|
||||
|
||||
// Whether this object can be created by the user
|
||||
// or are instantiated by other objects.
|
||||
static createable() {return true}
|
||||
// Properties are set with key as property name and
|
||||
// value as it's type name (e.g 'Expression', 'string'...),
|
||||
// an Enum for enumerations, an ObjectType for DrawableObjects
|
||||
// with a specific type, a List instance for lists, a
|
||||
// Dictionary instance for dictionaries...
|
||||
// Used for property modifier in the sidebar.
|
||||
static properties() {return {}}
|
||||
|
||||
// Whether the object can be executed (instance of ExecutableObject)
|
||||
static executable() {return false}
|
||||
|
||||
constructor(name, visible = true, color = null, labelContent = 'name + value') {
|
||||
if(color == null) color = Utils.getRandomColor()
|
||||
this.type = 'Unknown'
|
||||
this.name = name
|
||||
this.visible = visible
|
||||
this.color = color
|
||||
this.labelContent = labelContent // "null", "name", "name + value"
|
||||
this.requiredBy = []
|
||||
}
|
||||
|
||||
export() {
|
||||
// Should return what will be inputed as arguments when a file is loaded (serializable form)
|
||||
return [this.name, this.visible, this.color.toString(), this.labelContent]
|
||||
}
|
||||
|
||||
getReadableString() {
|
||||
return `${this.name} = Unknown`
|
||||
}
|
||||
|
||||
toLatexString() {
|
||||
return this.getReadableString()
|
||||
}
|
||||
|
||||
getLabel() {
|
||||
switch(this.labelContent) {
|
||||
case 'name':
|
||||
return this.name
|
||||
case 'name + value':
|
||||
return this.toLatexString()
|
||||
case 'null':
|
||||
return ''
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
update() {
|
||||
for(var req of this.requiredBy) {
|
||||
req.update()
|
||||
}
|
||||
}
|
||||
|
||||
delete() {
|
||||
for(var toRemove of this.requiredBy) {
|
||||
toRemove.delete()
|
||||
Objects.currentObjects[toRemove.type] = Objects.currentObjects[toRemove.type].filter(obj => obj.name != toRemove.name)
|
||||
}
|
||||
}
|
||||
|
||||
draw(canvas, ctx) {}
|
||||
|
||||
toString() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
class ExecutableObject extends DrawableObject {
|
||||
// Class to be extended for every class upon which we
|
||||
// calculate an y for a x with the execute function.
|
||||
// If a value cannot be found during execute, it should
|
||||
// return null. However, theses values should
|
||||
// return false when passed to canExecute.
|
||||
execute(x = 1) {return 0}
|
||||
canExecute(x = 1) {return true}
|
||||
// Simplify returns the simplified string of the expression.
|
||||
simplify(x = 1) {return '0'}
|
||||
|
||||
// Whether the object can be executed (instance of ExecutableObject)
|
||||
static executable() {return true}
|
||||
}
|
||||
|
||||
function registerObject(obj) {
|
||||
// Registers an object to be used in LogarithmPlotter.
|
||||
// This function is called from autoload.js
|
||||
if(obj.prototype instanceof DrawableObject) {
|
||||
Objects.types[obj.type()] = obj
|
||||
} else {
|
||||
console.error("Could not register object " + (obj.type()) + ", as it isn't a DrawableObject.")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,197 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as Common
|
||||
.import "../utils.js" as Utils
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
|
||||
|
||||
class Function extends Common.ExecutableObject {
|
||||
static type(){return 'Function'}
|
||||
static displayType(){return qsTr('Function')}
|
||||
static displayTypeMultiple(){return qsTr('Functions')}
|
||||
/*static properties() {return {
|
||||
'expression': 'Expression',
|
||||
'definitionDomain': 'Domain',
|
||||
'destinationDomain': 'Domain',
|
||||
'comment1': 'Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5}',
|
||||
'labelPosition': P.Enum.Position,
|
||||
'displayMode': new P.Enum('application', 'function'),
|
||||
'labelX': 'number',
|
||||
'comment2': 'The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...)',
|
||||
'drawPoints': 'boolean',
|
||||
'drawDashedLines': 'boolean'
|
||||
}}*/
|
||||
static properties() {return {
|
||||
[QT_TRANSLATE_NOOP('prop','expression')]: 'Expression',
|
||||
[QT_TRANSLATE_NOOP('prop','definitionDomain')]: 'Domain',
|
||||
[QT_TRANSLATE_NOOP('prop','destinationDomain')]: 'Domain',
|
||||
'comment1': QT_TRANSLATE_NOOP(
|
||||
'comment',
|
||||
'Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5}'
|
||||
),
|
||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||
[QT_TRANSLATE_NOOP('prop','displayMode')]: P.Enum.FunctionDisplayType,
|
||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||
'comment2': QT_TRANSLATE_NOOP(
|
||||
'comment',
|
||||
'The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...)'
|
||||
),
|
||||
[QT_TRANSLATE_NOOP('prop','drawPoints')]: 'boolean',
|
||||
[QT_TRANSLATE_NOOP('prop','drawDashedLines')]: 'boolean'
|
||||
}}
|
||||
|
||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||
expression = 'x', definitionDomain = 'RPE', destinationDomain = 'R',
|
||||
displayMode = 'application', labelPosition = 'above', labelX = 1,
|
||||
drawPoints = true, drawDashedLines = true) {
|
||||
if(name == null) name = Common.getNewName('fghjqlmnopqrstuvwabcde')
|
||||
super(name, visible, color, labelContent)
|
||||
this.type = 'Function'
|
||||
if(typeof expression == 'number' || typeof expression == 'string') expression = new MathLib.Expression(expression.toString())
|
||||
this.expression = expression
|
||||
if(typeof definitionDomain == 'string') definitionDomain = MathLib.parseDomain(definitionDomain)
|
||||
this.definitionDomain = definitionDomain
|
||||
if(typeof destinationDomain == 'string') destinationDomain = MathLib.parseDomain(destinationDomain)
|
||||
this.destinationDomain = destinationDomain
|
||||
this.displayMode = displayMode
|
||||
this.labelPosition = labelPosition
|
||||
this.labelX = labelX
|
||||
this.drawPoints = drawPoints
|
||||
this.drawDashedLines = drawDashedLines
|
||||
}
|
||||
|
||||
getReadableString() {
|
||||
if(this.displayMode == 'application') {
|
||||
return `${this.name}: ${this.definitionDomain} ⟶ ${this.destinationDomain}\n ${' '.repeat(this.name.length)}x ⟼ ${this.expression.toString()}`
|
||||
} else {
|
||||
return `${this.name}(x) = ${this.expression.toString()}\nD${Utils.textsub(this.name)} = ${this.definitionDomain}`
|
||||
}
|
||||
}
|
||||
|
||||
export() {
|
||||
return [this.name, this.visible, this.color.toString(), this.labelContent,
|
||||
this.expression.toEditableString(), this.definitionDomain.toString(), this.destinationDomain.toString(),
|
||||
this.displayMode, this.labelPosition, this.labelX, this.drawPoints, this.drawDashedLines]
|
||||
}
|
||||
|
||||
execute(x = 1) {
|
||||
if(this.definitionDomain.includes(x))
|
||||
return this.expression.execute(x)
|
||||
return null
|
||||
}
|
||||
|
||||
canExecute(x = 1) {
|
||||
return this.definitionDomain.includes(x)
|
||||
}
|
||||
|
||||
simplify(x = 1) {
|
||||
if(this.definitionDomain.includes(x))
|
||||
return this.expression.simplify(x)
|
||||
return ''
|
||||
}
|
||||
|
||||
draw(canvas, ctx) {
|
||||
Function.drawFunction(canvas, ctx, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines)
|
||||
// Label
|
||||
var text = this.getLabel()
|
||||
ctx.font = `${canvas.textsize}px sans-serif`
|
||||
var textSize = canvas.measureText(ctx, text)
|
||||
var posX = canvas.x2px(this.labelX)
|
||||
var posY = canvas.y2px(this.execute(this.labelX))
|
||||
switch(this.labelPosition) {
|
||||
case 'above':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
|
||||
break;
|
||||
case 'below':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
|
||||
break;
|
||||
case 'left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
|
||||
break;
|
||||
case 'right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
|
||||
break;
|
||||
case 'above-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
|
||||
break;
|
||||
case 'above-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
|
||||
break;
|
||||
case 'below-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
|
||||
break;
|
||||
case 'below-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static drawFunction(canvas, ctx, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) {
|
||||
// Reusable in other objects.
|
||||
// Drawing small traits every 2px
|
||||
var pxprecision = 0.2
|
||||
var previousX = canvas.px2x(0)
|
||||
var previousY;
|
||||
if(definitionDomain instanceof MathLib.SpecialDomain && definitionDomain.moveSupported) {
|
||||
// Point based functions.
|
||||
previousX = definitionDomain.next(previousX)
|
||||
if(previousX === null) previousX = definitionDomain.next(canvas.px2x(0))
|
||||
previousY = expr.execute(previousX)
|
||||
if(!drawPoints && !drawDash) return
|
||||
while(previousX !== null && canvas.x2px(previousX) < canvas.canvasSize.width) {
|
||||
// Reconverted for canvas to fix for logarithmic scales.
|
||||
var currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX)+pxprecision));
|
||||
var currentY = expr.execute(currentX)
|
||||
if(currentX === null) break;
|
||||
if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) &&
|
||||
(destinationDomain.includes(currentY) || destinationDomain.includes(previousY))) {
|
||||
if(drawDash)
|
||||
canvas.drawDashedLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
|
||||
if(drawPoints) {
|
||||
ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
|
||||
ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
|
||||
}
|
||||
}
|
||||
previousX = currentX
|
||||
previousY = currentY
|
||||
}
|
||||
if(drawPoints) {
|
||||
// Drawing the last cross
|
||||
ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
|
||||
ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
|
||||
}
|
||||
} else {
|
||||
previousY = expr.execute(previousX)
|
||||
for(var px = pxprecision; px < canvas.canvasSize.width; px += pxprecision) {
|
||||
var currentX = canvas.px2x(px)
|
||||
var currentY = expr.execute(currentX)
|
||||
if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) &&
|
||||
(destinationDomain.includes(currentY) || destinationDomain.includes(previousY)) &&
|
||||
Math.abs(previousY-currentY)<100) {
|
||||
canvas.drawLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
|
||||
}
|
||||
previousX = currentX
|
||||
previousY = currentY
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as Common
|
||||
.import "function.js" as F
|
||||
.import "../objects.js" as Objects
|
||||
.import "../utils.js" as Utils
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../historylib.js" as HistoryLib
|
||||
.import "../parameters.js" as P
|
||||
|
||||
class GainBode extends Common.ExecutableObject {
|
||||
static type(){return 'Gain Bode'}
|
||||
static displayType(){return qsTr('Bode Magnitude')}
|
||||
static displayTypeMultiple(){return qsTr('Bode Magnitudes')}
|
||||
/*static properties() {return {
|
||||
'om_0': new P.ObjectType('Point'),
|
||||
'pass': new P.Enum('high', 'low'),
|
||||
'gain': 'Expression',
|
||||
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
|
||||
'labelX': 'number',
|
||||
'omGraduation': 'boolean'
|
||||
}}*/
|
||||
static properties() {return {
|
||||
[QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'),
|
||||
[QT_TRANSLATE_NOOP('prop','pass')]: P.Enum.BodePass,
|
||||
[QT_TRANSLATE_NOOP('prop','gain')]: 'Expression',
|
||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||
[QT_TRANSLATE_NOOP('prop','omGraduation')]: 'boolean'
|
||||
}}
|
||||
|
||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||
om_0 = '', pass = 'high', gain = '20', labelPosition = 'above', labelX = 1, omGraduation = false) {
|
||||
if(name == null) name = Common.getNewName('G')
|
||||
if(name == 'G') name = 'G₀' // G is reserved for sum of BODE magnitudes (Somme gains Bode).
|
||||
super(name, visible, color, labelContent)
|
||||
this.type = 'Gain Bode'
|
||||
if(typeof om_0 == "string") {
|
||||
// Point name or create one
|
||||
om_0 = Objects.getObjectByName(om_0, 'Point')
|
||||
if(om_0 == null) {
|
||||
// Create new point
|
||||
om_0 = Objects.createNewRegisteredObject('Point')
|
||||
om_0.name = Common.getNewName('ω')
|
||||
om_0.labelContent = 'name'
|
||||
om_0.color = this.color
|
||||
HistoryLib.history.addToHistory(new HistoryLib.CreateNewObject(om_0.name, 'Point', om_0.export()))
|
||||
labelPosition = 'below'
|
||||
}
|
||||
om_0.requiredBy.push(this)
|
||||
}
|
||||
this.om_0 = om_0
|
||||
this.pass = pass
|
||||
if(typeof gain == 'number' || typeof gain == 'string') gain = new MathLib.Expression(gain.toString())
|
||||
this.gain = gain
|
||||
this.labelPosition = labelPosition
|
||||
this.labelX = labelX
|
||||
this.omGraduation = omGraduation
|
||||
}
|
||||
|
||||
getReadableString() {
|
||||
let pass = this.pass == "low" ? qsTr("low-pass") : qsTr("high-pass");
|
||||
return `${this.name}: ${pass}; ${this.om_0.name} = ${this.om_0.x}\n ${' '.repeat(this.name.length)}${this.gain.toString(true)} dB/dec`
|
||||
}
|
||||
|
||||
export() {
|
||||
return [this.name, this.visible, this.color.toString(), this.labelContent,
|
||||
this.om_0.name, this.pass.toString(), this.gain.toEditableString(), this.labelPosition, this.labelX, this.omGraduation]
|
||||
}
|
||||
|
||||
execute(x=1) {
|
||||
if(typeof x == 'string') x = MathLib.executeExpression(x)
|
||||
if((this.pass == 'high' && x < this.om_0.x) || (this.pass == 'low' && x > this.om_0.x)) {
|
||||
var dbfn = new MathLib.Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
|
||||
return dbfn.execute(x)
|
||||
} else {
|
||||
return this.om_0.y.execute()
|
||||
}
|
||||
}
|
||||
|
||||
simplify(x = 1) {
|
||||
var xval = x
|
||||
if(typeof x == 'string') xval = MathLib.executeExpression(x)
|
||||
if((this.pass == 'high' && xval < this.om_0.x) || (this.pass == 'low' && xval > this.om_0.x)) {
|
||||
var dbfn = new MathLib.Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
|
||||
return dbfn.simplify(x)
|
||||
} else {
|
||||
return this.om_0.y.toString()
|
||||
}
|
||||
}
|
||||
|
||||
canExecute(x = 1) {
|
||||
return true
|
||||
}
|
||||
|
||||
draw(canvas, ctx) {
|
||||
var base = [canvas.x2px(this.om_0.x), canvas.y2px(this.om_0.y)]
|
||||
var dbfn = new MathLib.Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
|
||||
var inDrawDom = new MathLib.EmptySet()
|
||||
|
||||
if(this.pass == 'high') {
|
||||
// High pass, linear line from begining, then constant to the end.
|
||||
canvas.drawLine(ctx, base[0], base[1], canvas.canvasSize.width, base[1])
|
||||
inDrawDom = MathLib.parseDomain(`]-inf;${this.om_0.x}[`)
|
||||
} else {
|
||||
// Low pass, constant from the beginning, linear line to the end.
|
||||
canvas.drawLine(ctx, base[0], base[1], 0, base[1])
|
||||
inDrawDom = MathLib.parseDomain(`]${this.om_0.x};+inf[`)
|
||||
}
|
||||
F.Function.drawFunction(canvas, ctx, dbfn, inDrawDom, MathLib.Domain.R)
|
||||
// Dashed line representing break in function
|
||||
var xpos = canvas.x2px(this.om_0.x.execute())
|
||||
var dashPxSize = 10
|
||||
for(var i = 0; i < canvas.canvasSize.height && this.omGraduation; i += dashPxSize*2)
|
||||
canvas.drawLine(ctx, xpos, i, xpos, i+dashPxSize)
|
||||
// Label
|
||||
var text = this.getLabel()
|
||||
ctx.font = `${canvas.textsize}px sans-serif`
|
||||
var textSize = canvas.measureText(ctx, text)
|
||||
var posX = canvas.x2px(this.labelX)
|
||||
var posY = canvas.y2px(this.execute(this.labelX))
|
||||
switch(this.labelPosition) {
|
||||
case 'above':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
|
||||
break;
|
||||
case 'below':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
|
||||
break;
|
||||
case 'left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
|
||||
break;
|
||||
case 'right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
|
||||
break;
|
||||
case 'above-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
|
||||
break;
|
||||
case 'above-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
|
||||
break;
|
||||
case 'below-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
|
||||
break;
|
||||
case 'below-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
update() {
|
||||
super.update()
|
||||
if(Objects.currentObjects['Somme gains Bode'] != undefined && Objects.currentObjects['Somme gains Bode'].length > 0) {
|
||||
Objects.currentObjects['Somme gains Bode'][0].recalculateCache()
|
||||
} else {
|
||||
Objects.createNewRegisteredObject('Somme gains Bode')
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as Common
|
||||
.import "../objects.js" as Objects
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../historylib.js" as HistoryLib
|
||||
.import "../parameters.js" as P
|
||||
|
||||
|
||||
class PhaseBode extends Common.ExecutableObject {
|
||||
static type(){return 'Phase Bode'}
|
||||
static displayType(){return qsTr('Bode Phase')}
|
||||
static displayTypeMultiple(){return qsTr('Bode Phases')}
|
||||
/*static properties() {return {
|
||||
'om_0': new P.ObjectType('Point'),
|
||||
'phase': 'Expression',
|
||||
'unit': new P.Enum('°', 'deg', 'rad'),
|
||||
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
|
||||
'labelX': 'number'
|
||||
}}*/
|
||||
static properties() {return {
|
||||
[QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'),
|
||||
[QT_TRANSLATE_NOOP('prop','phase')]: 'Expression',
|
||||
[QT_TRANSLATE_NOOP('prop','unit')]: new P.Enum('°', 'deg', 'rad'),
|
||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number'
|
||||
}}
|
||||
|
||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||
om_0 = '', phase = 90, unit = '°', labelPosition = 'above', labelX = 1) {
|
||||
if(name == null) name = Common.getNewName('φ')
|
||||
if(name == 'φ') name = 'φ₀' // φ is reserved for sum of BODE phases (Somme phases Bode).
|
||||
super(name, visible, color, labelContent)
|
||||
this.type = 'Phase Bode'
|
||||
if(typeof phase == 'number' || typeof phase == 'string') phase = new MathLib.Expression(phase.toString())
|
||||
this.phase = phase
|
||||
if(typeof om_0 == "string") {
|
||||
// Point name or create one
|
||||
om_0 = Objects.getObjectByName(om_0, 'Point')
|
||||
if(om_0 == null) {
|
||||
// Create new point
|
||||
om_0 = Objects.createNewRegisteredObject('Point')
|
||||
om_0.name = Common.getNewName('ω')
|
||||
om_0.color = this.color
|
||||
om_0.labelContent = 'name'
|
||||
om_0.labelPosition = this.phase.execute() >= 0 ? 'bottom' : 'top'
|
||||
HistoryLib.history.addToHistory(new HistoryLib.CreateNewObject(om_0.name, 'Point', om_0.export()))
|
||||
labelPosition = 'below'
|
||||
}
|
||||
om_0.requiredBy.push(this)
|
||||
}
|
||||
this.om_0 = om_0
|
||||
this.unit = unit
|
||||
this.labelPosition = labelPosition
|
||||
this.labelX = labelX
|
||||
}
|
||||
|
||||
export() {
|
||||
return [this.name, this.visible, this.color.toString(), this.labelContent,
|
||||
this.om_0.name, this.phase.toEditableString(), this.unit, this.labelPosition, this.labelX]
|
||||
}
|
||||
|
||||
getReadableString() {
|
||||
return `${this.name}: ${this.phase.toString(true)}${this.unit} at ${this.om_0.name} = ${this.om_0.x}`
|
||||
}
|
||||
|
||||
execute(x=1) {
|
||||
if(typeof x == 'string') x = MathLib.executeExpression(x)
|
||||
if(x < this.om_0.x) {
|
||||
return this.om_0.y.execute()
|
||||
} else {
|
||||
return this.om_0.y.execute() + this.phase.execute()
|
||||
}
|
||||
}
|
||||
|
||||
simplify(x = 1) {
|
||||
var xval = x
|
||||
if(typeof x == 'string') xval = MathLib.executeExpression(x)
|
||||
if(xval < this.om_0.x) {
|
||||
return this.om_0.y.toString()
|
||||
} else {
|
||||
var newExp = this.om_0.y.toEditableString() + ' + ' + this.phase.toEditableString()
|
||||
return (new MathLib.Expression(newExp)).toString()
|
||||
}
|
||||
}
|
||||
|
||||
canExecute(x = 1) {
|
||||
return true
|
||||
}
|
||||
|
||||
draw(canvas, ctx) {
|
||||
var baseX = canvas.x2px(this.om_0.x.execute())
|
||||
var omy = this.om_0.y.execute()
|
||||
var augmt = this.phase.execute()
|
||||
var baseY = canvas.y2px(omy)
|
||||
var augmtY = canvas.y2px(omy+augmt)
|
||||
// Before change line.
|
||||
canvas.drawLine(ctx, 0, baseY, Math.min(baseX, canvas.canvasSize.height), baseY)
|
||||
// Transition line.
|
||||
canvas.drawLine(ctx, baseX, baseY, baseX, augmtY)
|
||||
// After change line
|
||||
canvas.drawLine(ctx, Math.max(0, baseX), augmtY, canvas.canvasSize.width, augmtY)
|
||||
|
||||
// Label
|
||||
var text = this.getLabel()
|
||||
ctx.font = `${canvas.textsize}px sans-serif`
|
||||
var textSize = canvas.measureText(ctx, text)
|
||||
var posX = canvas.x2px(this.labelX)
|
||||
var posY = canvas.y2px(this.execute(this.labelX))
|
||||
switch(this.labelPosition) {
|
||||
case 'above':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
|
||||
break;
|
||||
case 'below':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
|
||||
break;
|
||||
case 'left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
|
||||
break;
|
||||
case 'right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
|
||||
break;
|
||||
case 'above-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
|
||||
break;
|
||||
case 'above-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
|
||||
break;
|
||||
case 'below-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
|
||||
break;
|
||||
case 'below-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
update() {
|
||||
if(Objects.currentObjects['Somme phases Bode'] != undefined && Objects.currentObjects['Somme phases Bode'].length > 0) {
|
||||
Objects.currentObjects['Somme phases Bode'][0].recalculateCache()
|
||||
} else {
|
||||
Objects.createNewRegisteredObject('Somme phases Bode')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as Common
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
|
||||
|
||||
class Point extends Common.DrawableObject {
|
||||
static type(){return 'Point'}
|
||||
static displayType(){return qsTr('Point')}
|
||||
static displayTypeMultiple(){return qsTr('Points')}
|
||||
|
||||
/*static properties() {return {
|
||||
'x': 'Expression',
|
||||
'y': 'Expression',
|
||||
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
|
||||
'pointStyle': new P.Enum('●', '✕', '+'),
|
||||
}}*/
|
||||
static properties() {return {
|
||||
[QT_TRANSLATE_NOOP('prop','x')]: 'Expression',
|
||||
[QT_TRANSLATE_NOOP('prop','y')]: 'Expression',
|
||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||
[QT_TRANSLATE_NOOP('prop','pointStyle')]: new P.Enum('●', '✕', '+')
|
||||
}}
|
||||
|
||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||
x = 1, y = 0, labelPosition = 'above', pointStyle = '●') {
|
||||
if(name == null) name = Common.getNewName('ABCDEFJKLMNOPQRSTUVW')
|
||||
super(name, visible, color, labelContent)
|
||||
this.type = 'Point'
|
||||
if(typeof x == 'number' || typeof x == 'string') x = new MathLib.Expression(x.toString())
|
||||
this.x = x
|
||||
if(typeof y == 'number' || typeof y == 'string') y = new MathLib.Expression(y.toString())
|
||||
this.y = y
|
||||
this.labelPosition = labelPosition
|
||||
this.pointStyle = pointStyle
|
||||
}
|
||||
|
||||
getReadableString() {
|
||||
return `${this.name} = (${this.x}, ${this.y})`
|
||||
}
|
||||
|
||||
toLatexString() {
|
||||
return `${this.name} = \\left(${this.x.latexMarkup}, ${this.y.latexMarkup}\\right)`
|
||||
}
|
||||
|
||||
export() {
|
||||
return [this.name, this.visible, this.color.toString(), this.labelContent, this.x.toEditableString(), this.y.toEditableString(), this.labelPosition, this.pointStyle]
|
||||
}
|
||||
|
||||
draw(canvas, ctx) {
|
||||
var [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())]
|
||||
var pointSize = 8+(ctx.lineWidth*2)
|
||||
switch(this.pointStyle) {
|
||||
case '●':
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(canvasX-pointSize/2, canvasY-pointSize/2, pointSize, pointSize)
|
||||
ctx.fill();
|
||||
break;
|
||||
case '✕':
|
||||
canvas.drawLine(ctx, canvasX-pointSize/2, canvasY-pointSize/2, canvasX+pointSize/2, canvasY+pointSize/2)
|
||||
canvas.drawLine(ctx, canvasX-pointSize/2, canvasY+pointSize/2, canvasX+pointSize/2, canvasY-pointSize/2)
|
||||
break;
|
||||
case '+':
|
||||
ctx.fillRect(canvasX-pointSize/2, canvasY-1, pointSize, 2)
|
||||
ctx.fillRect(canvasX-1, canvasY-pointSize/2, 2, pointSize)
|
||||
break;
|
||||
}
|
||||
|
||||
let drawLabel = function(canvas, ctx, ltxImg) {
|
||||
//console.log(JSON.stringify(ltxImg), canvas.isImageLoaded(ltxImg.source), this, this.labelPosition)
|
||||
switch(this.labelPosition) {
|
||||
case 'top':
|
||||
case 'above':
|
||||
canvas.drawVisibleImage(ctx, ltxImg.source, canvasX-ltxImg.width/2, canvasY-(ltxImg.height+4), ltxImg.width, ltxImg.height)
|
||||
break;
|
||||
case 'bottom':
|
||||
case 'below':
|
||||
canvas.drawVisibleImage(ctx, ltxImg.source, canvasX-ltxImg.width/2, canvasY+4, ltxImg.width, ltxImg.height)
|
||||
break;
|
||||
case 'left':
|
||||
canvas.drawVisibleImage(ctx, ltxImg.source, canvasX-(ltxImg.width+4), canvasY+4, ltxImg.width, ltxImg.height)
|
||||
break;
|
||||
case 'right':
|
||||
canvas.drawVisibleImage(ctx, ltxImg.source, canvasX+4, canvasY+4, ltxImg.width, ltxImg.height)
|
||||
break;
|
||||
case 'top-left':
|
||||
case 'above-left':
|
||||
canvas.drawVisibleImage(ctx, ltxImg.source, canvasX-(ltxImg.width+4), canvasY-(ltxImg.height+4), ltxImg.width, ltxImg.height)
|
||||
break;
|
||||
case 'top-right':
|
||||
case 'above-right':
|
||||
canvas.drawVisibleImage(ctx, ltxImg.source, canvasX+4, canvasY-(ltxImg.height+4), ltxImg.width, ltxImg.height)
|
||||
break;
|
||||
case 'bottom-left':
|
||||
case 'below-left':
|
||||
canvas.drawVisibleImage(ctx, ltxImg.source, canvasX-(ltxImg.width+4), canvasY+4, ltxImg.width, ltxImg.height)
|
||||
break;
|
||||
case 'bottom-right':
|
||||
case 'below-right':
|
||||
canvas.drawVisibleImage(ctx, ltxImg.source, canvasX+4, canvasY+4, ltxImg.width, ltxImg.height)
|
||||
break;
|
||||
}
|
||||
}
|
||||
canvas.renderLatexImage(this.getLabel(), this.color, drawLabel.bind(this))
|
||||
//canvas.drawVisibleImage(ctx, ltxImg.source, canvasX, canvasY)
|
||||
}
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as Common
|
||||
.import "../parameters.js" as P
|
||||
|
||||
class RepartitionFunction extends Common.ExecutableObject {
|
||||
static type(){return 'Repartition'}
|
||||
static displayType(){return qsTr('Repartition')}
|
||||
static displayTypeMultiple(){return qsTr('Repartition functions')}
|
||||
/*static properties() {return {
|
||||
'beginIncluded': 'boolean',
|
||||
'drawLineEnds': 'boolean',
|
||||
'comment1': 'Note: Specify the properties for each potential result.',
|
||||
'probabilities': new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d\.,]+$/, 'P({name} = ', ') = '),
|
||||
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
|
||||
'labelX': 'number'
|
||||
}}*/
|
||||
static properties() {return {
|
||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||
'comment1': QT_TRANSLATE_NOOP(
|
||||
'comment',
|
||||
'Note: Specify the probability for each value.'
|
||||
),
|
||||
[QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d\.,]+$/, 'P({name} = ', ') = '),
|
||||
}}
|
||||
|
||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||
beginIncluded = true, drawLineEnds = true, probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) {
|
||||
if(name == null) name = Common.getNewName('XYZUVW')
|
||||
super(name, visible, color, labelContent)
|
||||
this.beginIncluded = beginIncluded
|
||||
this.drawLineEnds = drawLineEnds
|
||||
this.probabilities = probabilities
|
||||
this.labelPosition = labelPosition
|
||||
this.labelX = labelX
|
||||
this.update()
|
||||
}
|
||||
|
||||
export() {
|
||||
return [this.name, this.visible, this.color.toString(), this.labelContent,
|
||||
this.beginIncluded, this.drawLineEnds, this.probabilities, this.labelPosition, this.labelX]
|
||||
}
|
||||
|
||||
|
||||
getReadableString() {
|
||||
var keys = Object.keys(this.probabilities).sort((a,b) => a-b);
|
||||
return `F_${this.name}(x) = P(${this.name} ≤ x)\n` + keys.map(idx => `P(${this.name}=${idx})=${this.probabilities[idx]}`).join("; ")
|
||||
}
|
||||
|
||||
execute(x = 1) {
|
||||
var ret = 0;
|
||||
Object.keys(this.probabilities).sort((a,b) => a-b).forEach(idx => {
|
||||
if(x >= idx) ret += parseFloat(this.probabilities[idx].replace(/,/g, '.'))
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
canExecute(x = 1) {return true}
|
||||
// Simplify returns the simplified string of the expression.
|
||||
simplify(x = 1) {
|
||||
return this.execute(x)
|
||||
}
|
||||
|
||||
getLabel() {
|
||||
switch(this.labelContent) {
|
||||
case 'name':
|
||||
return `P(${this.name} ≤ x)`
|
||||
case 'name + value':
|
||||
return this.getReadableString()
|
||||
case 'null':
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
draw(canvas, ctx) {
|
||||
var currentY = 0;
|
||||
var keys = Object.keys(this.probabilities).map(idx => parseInt(idx)).sort((a,b) => a-b)
|
||||
if(canvas.visible(keys[0],this.probabilities[keys[0]].replace(/,/g, '.'))) {
|
||||
canvas.drawLine(ctx,
|
||||
0,
|
||||
canvas.y2px(0),
|
||||
canvas.x2px(keys[0]),
|
||||
canvas.y2px(0)
|
||||
)
|
||||
if(canvas.visible(keys[0],0)) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(canvas.x2px(keys[0])+4,canvas.y2px(0), 4, Math.PI / 2, 3 * Math.PI / 2);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < keys.length-1; i++) {
|
||||
var idx = keys[i];
|
||||
currentY += parseFloat(this.probabilities[idx].replace(/,/g, '.'));
|
||||
if(canvas.visible(idx,currentY) || canvas.visible(keys[i+1],currentY)) {
|
||||
canvas.drawLine(ctx,
|
||||
Math.max(0,canvas.x2px(idx)),
|
||||
canvas.y2px(currentY),
|
||||
Math.min(canvas.canvasSize.width,canvas.x2px(keys[i+1])),
|
||||
canvas.y2px(currentY)
|
||||
)
|
||||
if(canvas.visible(idx,currentY)) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(canvas.x2px(idx),canvas.y2px(currentY), 4, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
}
|
||||
if(canvas.visible(keys[i+1],currentY)) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(canvas.x2px(keys[i+1])+4,canvas.y2px(currentY), 4, Math.PI / 2, 3 * Math.PI / 2);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(canvas.visible(keys[keys.length-1],currentY+parseFloat(this.probabilities[keys[keys.length-1]]))) {
|
||||
canvas.drawLine(ctx,
|
||||
Math.max(0,canvas.x2px(keys[keys.length-1])),
|
||||
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))),
|
||||
canvas.canvasSize.width,
|
||||
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.')))
|
||||
)
|
||||
ctx.beginPath();
|
||||
ctx.arc(
|
||||
canvas.x2px(keys[keys.length-1]),
|
||||
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))),
|
||||
4, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// Label
|
||||
var text = this.getLabel()
|
||||
ctx.font = `${canvas.textsize}px sans-serif`
|
||||
var textSize = canvas.measureText(ctx, text)
|
||||
var posX = canvas.x2px(this.labelX)
|
||||
var posY = canvas.y2px(this.execute(this.labelX))
|
||||
switch(this.labelPosition) {
|
||||
case 'above':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
|
||||
break;
|
||||
case 'below':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
|
||||
break;
|
||||
case 'left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
|
||||
break;
|
||||
case 'right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
|
||||
break;
|
||||
case 'above-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
|
||||
break;
|
||||
case 'above-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
|
||||
break;
|
||||
case 'below-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
|
||||
break;
|
||||
case 'below-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as Common
|
||||
.import "function.js" as F
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
|
||||
|
||||
class Sequence extends Common.ExecutableObject {
|
||||
static type(){return 'Sequence'}
|
||||
static displayType(){return qsTr('Sequence')}
|
||||
static displayTypeMultiple(){return qsTr('Sequences')}
|
||||
|
||||
static properties() {return {
|
||||
[QT_TRANSLATE_NOOP('prop','drawPoints')]: 'boolean',
|
||||
[QT_TRANSLATE_NOOP('prop','drawDashedLines')]: 'boolean',
|
||||
[QT_TRANSLATE_NOOP('prop','defaultExpression')]: new P.Dictionary('string', 'int', /^.+$/, /^\d+$/, '{name}[n+', '] = ', true),
|
||||
'comment1': QT_TRANSLATE_NOOP(
|
||||
'comment',
|
||||
'Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁...'
|
||||
),
|
||||
[QT_TRANSLATE_NOOP('prop','baseValues')]: new P.Dictionary('string', 'int', /^.+$/, /^\d+$/, '{name}[', '] = '),
|
||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||
}}
|
||||
|
||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||
drawPoints = true, drawDashedLines = true, defaultExp = {1: "n"},
|
||||
baseValues = {0: 0}, labelPosition = 'above', labelX = 1) {
|
||||
if(name == null) name = Common.getNewName('uvwPSUVWabcde')
|
||||
super(name, visible, color, labelContent)
|
||||
this.drawPoints = drawPoints
|
||||
this.drawDashedLines = drawDashedLines
|
||||
this.defaultExpression = defaultExp
|
||||
this.baseValues = baseValues
|
||||
this.labelPosition = labelPosition
|
||||
this.labelX = labelX
|
||||
this.update()
|
||||
}
|
||||
|
||||
export() {
|
||||
return [this.name, this.visible, this.color.toString(), this.labelContent,
|
||||
this.drawPoints, this.drawDashedLines, this.defaultExpression, this.baseValues, this.labelPosition, this.labelX]
|
||||
}
|
||||
|
||||
update() {
|
||||
super.update()
|
||||
if(
|
||||
this.sequence == null || this.baseValues != this.sequence.baseValues ||
|
||||
this.sequence.name != this.name ||
|
||||
this.sequence.expr != Object.values(this.defaultExpression)[0] ||
|
||||
this.sequence.valuePlus != Object.keys(this.defaultExpression)[0]
|
||||
)
|
||||
this.sequence = new MathLib.Sequence(
|
||||
this.name, this.baseValues,
|
||||
Object.keys(this.defaultExpression)[0],
|
||||
Object.values(this.defaultExpression)[0]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
getReadableString() {
|
||||
return this.sequence.toString()
|
||||
}
|
||||
|
||||
execute(x = 1) {
|
||||
if(x % 1 == 0)
|
||||
return this.sequence.execute(x)
|
||||
return null
|
||||
}
|
||||
canExecute(x = 1) {return x%1 == 0}
|
||||
|
||||
// Simplify returns the simplified string of the expression.
|
||||
simplify(x = 1) {
|
||||
if(x % 1 == 0)
|
||||
return this.sequence.simplify(x)
|
||||
return null
|
||||
}
|
||||
|
||||
getLabel() {
|
||||
switch(this.labelContent) {
|
||||
case 'name':
|
||||
return `(${this.name}ₙ)`
|
||||
case 'name + value':
|
||||
return this.getReadableString()
|
||||
case 'null':
|
||||
return ''
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
draw(canvas, ctx) {
|
||||
F.Function.drawFunction(canvas, ctx, this.sequence, canvas.logscalex ? MathLib.Domain.NE : MathLib.Domain.N, MathLib.Domain.R, this.drawPoints, this.drawDashedLines)
|
||||
|
||||
// Label
|
||||
var text = this.getLabel()
|
||||
ctx.font = `${canvas.textsize}px sans-serif`
|
||||
var textSize = canvas.measureText(ctx, text)
|
||||
var posX = canvas.x2px(this.labelX)
|
||||
var posY = canvas.y2px(this.execute(this.labelX))
|
||||
switch(this.labelPosition) {
|
||||
case 'above':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
|
||||
break;
|
||||
case 'below':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
|
||||
break;
|
||||
case 'left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
|
||||
break;
|
||||
case 'right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
|
||||
break;
|
||||
case 'above-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
|
||||
break;
|
||||
case 'above-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
|
||||
break;
|
||||
case 'below-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
|
||||
break;
|
||||
case 'below-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as Common
|
||||
.import "function.js" as F
|
||||
.import "../objects.js" as Objects
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
|
||||
|
||||
class SommeGainsBode extends Common.DrawableObject {
|
||||
static type(){return 'Somme gains Bode'}
|
||||
static displayType(){return qsTr('Bode Magnitudes Sum')}
|
||||
static displayTypeMultiple(){return qsTr('Bode Magnitudes Sum')}
|
||||
static createable() {return false}
|
||||
/*static properties() {return {
|
||||
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
|
||||
'labelX': 'number'
|
||||
}}*/
|
||||
static properties() {return {
|
||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||
}}
|
||||
|
||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||
labelPosition = 'above', labelX = 1) {
|
||||
if(name == null) name = 'G'
|
||||
super(name, visible, color, labelContent)
|
||||
this.labelPosition = labelPosition
|
||||
this.labelX = labelX
|
||||
this.recalculateCache()
|
||||
}
|
||||
|
||||
export() {
|
||||
return [this.name, this.visible, this.color.toString(), this.labelContent, this.labelPosition, this.labelX]
|
||||
}
|
||||
|
||||
getReadableString() {
|
||||
return `${this.name} = ${Objects.getObjectsName('Gain Bode').join(' + ')}`
|
||||
}
|
||||
|
||||
execute(x = 0) {
|
||||
for(var [dbfn, inDrawDom] of this.cachedParts) {
|
||||
if(inDrawDom.includes(x)) {
|
||||
return dbfn.execute(x)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
canExecute(x = 1) {
|
||||
return true // Should always be true
|
||||
}
|
||||
|
||||
simplify(x = 1) {
|
||||
for(var [dbfn, inDrawDom] of this.cachedParts) {
|
||||
if(inDrawDom.includes(x)) {
|
||||
return dbfn.simplify(x)
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
recalculateCache() {
|
||||
this.cachedParts = []
|
||||
// Calculating this is fairly resource expansive so it's cached.
|
||||
if(Objects.currentObjects['Gain Bode'] != undefined) {
|
||||
console.log('Recalculating cache gain')
|
||||
// Minimum to draw (can be expended if needed, just not infinite or it'll cause issues.
|
||||
var drawMin = 0.001
|
||||
|
||||
var baseY = 0
|
||||
var om0xGains = {1000000000: 0} // To draw the last part
|
||||
var om0xPass = {1000000000: 'high'} // To draw the last part
|
||||
Objects.currentObjects['Gain Bode'].forEach(function(gainObj) { // Sorting by their om_0 position.
|
||||
var om0x = gainObj.om_0.x.execute()
|
||||
if(om0xGains[om0x] == undefined) {
|
||||
om0xGains[om0x] = gainObj.gain.execute()
|
||||
om0xPass[om0x] = gainObj.pass == 'high'
|
||||
} else {
|
||||
om0xGains[om0x+0.001] = gainObj.gain.execute()
|
||||
om0xPass[om0x+0.001] = gainObj.pass == 'high'
|
||||
}
|
||||
baseY += gainObj.execute(drawMin)
|
||||
})
|
||||
// Sorting the om_0x
|
||||
var om0xList = Object.keys(om0xGains).map(x => parseFloat(x)) // THEY WERE CONVERTED TO STRINGS...
|
||||
om0xList.sort((a,b) => a - b)
|
||||
// Adding the total gains.
|
||||
var gainsBeforeP = []
|
||||
var gainsAfterP = []
|
||||
var gainTotal = 0
|
||||
for(var om0x of om0xList){
|
||||
if(om0xPass[om0x]) { // High-pass
|
||||
gainsBeforeP.push(om0xGains[om0x])
|
||||
gainsAfterP.push(0)
|
||||
gainTotal += om0xGains[om0x] // Gain at first
|
||||
} else {
|
||||
gainsBeforeP.push(0)
|
||||
gainsAfterP.push(om0xGains[om0x])
|
||||
}
|
||||
}
|
||||
// Calculating parts
|
||||
var previousPallier = drawMin
|
||||
for(var pallier = 0; pallier < om0xList.length; pallier++) {
|
||||
var dbfn = new MathLib.Expression(`${gainTotal}*(ln(x)-ln(${previousPallier}))/ln(10)+${baseY}`)
|
||||
var inDrawDom = MathLib.parseDomain(`]${previousPallier};${om0xList[pallier]}]`)
|
||||
this.cachedParts.push([dbfn, inDrawDom])
|
||||
previousPallier = om0xList[pallier]
|
||||
baseY = dbfn.execute(om0xList[pallier])
|
||||
gainTotal += gainsAfterP[pallier] - gainsBeforeP[pallier]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draw(canvas, ctx) {
|
||||
if(this.cachedParts.length > 0) {
|
||||
for(var [dbfn, inDrawDom] of this.cachedParts) {
|
||||
F.Function.drawFunction(canvas, ctx, dbfn, inDrawDom, MathLib.Domain.R)
|
||||
if(inDrawDom.includes(this.labelX)) {
|
||||
// Label
|
||||
var text = this.getLabel()
|
||||
ctx.font = `${canvas.textsize}px sans-serif`
|
||||
var textSize = canvas.measureText(ctx, text)
|
||||
var posX = canvas.x2px(this.labelX)
|
||||
var posY = canvas.y2px(dbfn.execute(this.labelX))
|
||||
switch(this.labelPosition) {
|
||||
case 'above':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
|
||||
break;
|
||||
case 'below':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
|
||||
break;
|
||||
case 'left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
|
||||
break;
|
||||
case 'right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
|
||||
break;
|
||||
case 'above-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
|
||||
break;
|
||||
case 'above-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
|
||||
break;
|
||||
case 'below-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
|
||||
break;
|
||||
case 'below-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as Common
|
||||
.import "../objects.js" as Objects
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
|
||||
|
||||
class SommePhasesBode extends Common.ExecutableObject {
|
||||
static type(){return 'Somme phases Bode'}
|
||||
static displayType(){return qsTr('Bode Phases Sum')}
|
||||
static displayTypeMultiple(){return qsTr('Bode Phases Sum')}
|
||||
static createable() {return false}
|
||||
/*static properties() {return {
|
||||
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
|
||||
'labelX': 'number'
|
||||
}}*/
|
||||
static properties() {return {
|
||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||
}}
|
||||
|
||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||
labelPosition = 'above', labelX = 1) {
|
||||
if(name == null) name = 'φ'
|
||||
super(name, visible, color, labelContent)
|
||||
this.labelPosition = labelPosition
|
||||
this.labelX = labelX
|
||||
this.recalculateCache()
|
||||
}
|
||||
|
||||
export() {
|
||||
return [this.name, this.visible, this.color.toString(), this.labelContent, this.labelPosition, this.labelX]
|
||||
}
|
||||
|
||||
getReadableString() {
|
||||
return `${this.name} = ${Objects.getObjectsName('Phase Bode').join(' + ')}`
|
||||
}
|
||||
|
||||
execute(x=1) {
|
||||
if(typeof x == 'string') x = MathLib.executeExpression(x)
|
||||
for(var i = 0; i < this.om0xList.length-1; i++) {
|
||||
if(x >= this.om0xList[i] && x < this.om0xList[i+1]) return this.phasesList[i]
|
||||
}
|
||||
}
|
||||
|
||||
simplify(x = 1) {
|
||||
var xval = x
|
||||
if(typeof x == 'string') xval = MathLib.executeExpression(x)
|
||||
for(var i = 0; i < this.om0xList.length-1; i++) {
|
||||
if(xval >= this.om0xList[i] && xval < this.om0xList[i+1]) {
|
||||
return (new MathLib.Expression(this.phasesExprList[i])).simplify()
|
||||
}
|
||||
}
|
||||
return '0'
|
||||
}
|
||||
|
||||
canExecute(x = 1) {
|
||||
return true
|
||||
}
|
||||
|
||||
recalculateCache() {
|
||||
// Minimum to draw (can be expended if needed, just not infinite or it'll cause issues.
|
||||
var drawMin = 0.001
|
||||
var drawMax = 100000
|
||||
this.om0xList = [drawMin, drawMax]
|
||||
this.phasesList = [0]
|
||||
this.phasesExprList = ['0']
|
||||
var phasesDict = {}
|
||||
var phasesExprDict = {}
|
||||
phasesDict[drawMax] = 0
|
||||
|
||||
if(Objects.currentObjects['Phase Bode'] != undefined) {
|
||||
console.log('Recalculating cache phase')
|
||||
for(var obj of Objects.currentObjects['Phase Bode']) {
|
||||
this.om0xList.push(obj.om_0.x.execute())
|
||||
if(phasesDict[obj.om_0.x.execute()] == undefined) {
|
||||
phasesDict[obj.om_0.x.execute()] = obj.phase.execute()
|
||||
phasesExprDict[obj.om_0.x.execute()] = obj.phase.toEditableString()
|
||||
} else {
|
||||
phasesDict[obj.om_0.x.execute()] += obj.phase.execute()
|
||||
phasesExprDict[obj.om_0.x.execute()] += '+' + obj.phase.toEditableString()
|
||||
}
|
||||
this.phasesList[0] += obj.om_0.y.execute()
|
||||
this.phasesExprList[0] += '+' + obj.om_0.y.toEditableString()
|
||||
}
|
||||
this.om0xList.sort((a,b) => a - b)
|
||||
var totalAdded = this.phasesList[0]
|
||||
for(var i = 1; i < this.om0xList.length; i++) {
|
||||
this.phasesList[i] = this.phasesList[i-1] + phasesDict[this.om0xList[i]]
|
||||
this.phasesExprList[i] = this.phasesExprList[i-1] + '+' + phasesDict[this.om0xList[i]]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draw(canvas, ctx) {
|
||||
for(var i = 0; i < this.om0xList.length-1; i++) {
|
||||
var om0xBegin = canvas.x2px(this.om0xList[i])
|
||||
var om0xEnd = canvas.x2px(this.om0xList[i+1])
|
||||
var baseY = canvas.y2px(this.phasesList[i])
|
||||
var nextY = canvas.y2px(this.phasesList[i+1])
|
||||
canvas.drawLine(ctx, om0xBegin, baseY, om0xEnd, baseY)
|
||||
canvas.drawLine(ctx, om0xEnd, baseY, om0xEnd, nextY)
|
||||
}
|
||||
|
||||
// Label
|
||||
var text = this.getLabel()
|
||||
ctx.font = `${canvas.textsize}px sans-serif`
|
||||
var textSize = canvas.measureText(ctx, text)
|
||||
var posX = canvas.x2px(this.labelX)
|
||||
var posY = canvas.y2px(this.execute(this.labelX))
|
||||
switch(this.labelPosition) {
|
||||
case 'above':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
|
||||
break;
|
||||
case 'below':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
|
||||
break;
|
||||
case 'left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
|
||||
break;
|
||||
case 'right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
|
||||
break;
|
||||
case 'above-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
|
||||
break;
|
||||
case 'above-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
|
||||
break;
|
||||
case 'below-left':
|
||||
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
|
||||
break;
|
||||
case 'below-right':
|
||||
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as Common
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
|
||||
|
||||
class Text extends Common.DrawableObject {
|
||||
static type(){return 'Text'}
|
||||
static displayType(){return qsTr('Text')}
|
||||
static displayTypeMultiple(){return qsTr('Texts')}
|
||||
/*static properties() {return {
|
||||
'x': 'Expression',
|
||||
'y': 'Expression',
|
||||
'labelPosition': new P.Enum('center', 'above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
|
||||
'text': 'string',
|
||||
}}*/
|
||||
static properties() {return {
|
||||
[QT_TRANSLATE_NOOP('prop','x')]: 'Expression',
|
||||
[QT_TRANSLATE_NOOP('prop','y')]: 'Expression',
|
||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning,
|
||||
[QT_TRANSLATE_NOOP('prop','text')]: 'string'
|
||||
}}
|
||||
|
||||
constructor(name = null, visible = true, color = null, labelContent = 'null',
|
||||
x = 1, y = 0, labelPosition = 'center', text = 'New text') {
|
||||
if(name == null) name = Common.getNewName('t')
|
||||
super(name, visible, color, labelContent)
|
||||
this.type = 'Point'
|
||||
if(typeof x == 'number' || typeof x == 'string') x = new MathLib.Expression(x.toString())
|
||||
this.x = x
|
||||
if(typeof y == 'number' || typeof y == 'string') y = new MathLib.Expression(y.toString())
|
||||
this.y = y
|
||||
this.labelPosition = labelPosition
|
||||
this.text = text
|
||||
}
|
||||
|
||||
getReadableString() {
|
||||
return `${this.name} = "${this.text}"`
|
||||
}
|
||||
|
||||
export() {
|
||||
return [this.name, this.visible, this.color.toString(), this.labelContent, this.x.toEditableString(), this.y.toEditableString(), this.labelPosition, this.text]
|
||||
}
|
||||
|
||||
draw(canvas, ctx) {
|
||||
var [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())]
|
||||
ctx.font = `${canvas.textsize}px sans-serif`
|
||||
var textSize = ctx.measureText(this.text).width
|
||||
switch(this.labelPosition) {
|
||||
case 'center':
|
||||
canvas.drawVisibleText(ctx, this.text, canvasX-textSize/2, canvasY+4)
|
||||
break;
|
||||
case 'top':
|
||||
canvas.drawVisibleText(ctx, this.text, canvasX-textSize/2, canvasY-16)
|
||||
break;
|
||||
case 'bottom':
|
||||
canvas.drawVisibleText(ctx, this.text, canvasX-textSize/2, canvasY+16)
|
||||
break;
|
||||
case 'left':
|
||||
canvas.drawVisibleText(ctx, this.text, canvasX-textSize-5, canvasY+4)
|
||||
break;
|
||||
case 'right':
|
||||
canvas.drawVisibleText(ctx, this.text, canvasX+5, canvasY+4)
|
||||
break;
|
||||
case 'top-left':
|
||||
canvas.drawVisibleText(ctx, this.text, canvasX-textSize-5, canvasY-16)
|
||||
break;
|
||||
case 'top-right':
|
||||
canvas.drawVisibleText(ctx, this.text, canvasX+5, canvasY-16)
|
||||
break;
|
||||
case 'bottom-left':
|
||||
canvas.drawVisibleText(ctx, this.text, canvasX-textSize-5, canvasY+16)
|
||||
break;
|
||||
case 'bottom-right':
|
||||
canvas.drawVisibleText(ctx, this.text, canvasX+5, canvasY+16)
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,190 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "common.js" as Common
|
||||
.import "../objects.js" as Objects
|
||||
.import "../mathlib.js" as MathLib
|
||||
.import "../parameters.js" as P
|
||||
|
||||
|
||||
class XCursor extends Common.DrawableObject {
|
||||
static type(){return 'X Cursor'}
|
||||
static displayType(){return qsTr('X Cursor')}
|
||||
static displayTypeMultiple(){return qsTr('X Cursors')}
|
||||
/*static properties() {
|
||||
return {
|
||||
'x': 'Expression',
|
||||
'targetElement': new P.ObjectType('ExecutableObject'),
|
||||
'labelPosition': new P.Enum('left', 'right'),
|
||||
'approximate': 'boolean',
|
||||
'rounding': 'number',
|
||||
'displayStyle': new P.Enum(
|
||||
'— — — — — — —',
|
||||
'⸺⸺⸺⸺⸺⸺',
|
||||
'• • • • • • • • • •'
|
||||
),
|
||||
'targetValuePosition' : new P.Enum('Next to target', 'With label', 'Hidden')
|
||||
}
|
||||
}*/
|
||||
static properties() {return {
|
||||
[QT_TRANSLATE_NOOP('prop','x')]: 'Expression',
|
||||
[QT_TRANSLATE_NOOP('prop','targetElement')]: new P.ObjectType('ExecutableObject'),
|
||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||
[QT_TRANSLATE_NOOP('prop','approximate')]: 'boolean',
|
||||
[QT_TRANSLATE_NOOP('prop','rounding')]: 'number',
|
||||
[QT_TRANSLATE_NOOP('prop','displayStyle')]: new P.Enum(
|
||||
'— — — — — — —',
|
||||
'⸺⸺⸺⸺⸺⸺',
|
||||
'• • • • • • • • • •'
|
||||
),
|
||||
[QT_TRANSLATE_NOOP('prop','targetValuePosition')]: P.Enum.XCursorValuePosition,
|
||||
}}
|
||||
|
||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||
x = 1, targetElement = null, labelPosition = 'left', approximate = true,
|
||||
rounding = 3, displayStyle = '— — — — — — —', targetValuePosition = 'Next to target') {
|
||||
if(name == null) name = Common.getNewName('X')
|
||||
super(name, visible, color, labelContent)
|
||||
this.type = 'X Cursor'
|
||||
this.approximate = approximate
|
||||
this.rounding = rounding
|
||||
if(typeof x == 'number' || typeof x == 'string') x = new MathLib.Expression(x.toString())
|
||||
this.x = x
|
||||
this.targetElement = targetElement
|
||||
if(typeof targetElement == "string") {
|
||||
this.targetElement = Objects.getObjectByName(targetElement, elementTypes)
|
||||
}
|
||||
this.labelPosition = labelPosition
|
||||
this.displayStyle = displayStyle
|
||||
this.targetValuePosition = targetValuePosition
|
||||
}
|
||||
|
||||
export() {
|
||||
return [this.name, this.visible, this.color.toString(), this.labelContent,
|
||||
this.x.toEditableString(), this.targetElement == null ? null : this.targetElement.name, this.labelPosition,
|
||||
this.approximate, this.rounding, this.displayStyle, this.targetValuePosition]
|
||||
}
|
||||
|
||||
getReadableString() {
|
||||
if(this.targetElement == null) return `${this.name} = ${this.x.toString()}`
|
||||
return `${this.name} = ${this.x.toString()}\n${this.getTargetValueLabel()}`
|
||||
}
|
||||
|
||||
getTargetValueLabel() {
|
||||
var t = this.targetElement
|
||||
var approx = ''
|
||||
if(this.approximate) {
|
||||
approx = t.execute(this.x.execute())
|
||||
approx = approx.toPrecision(this.rounding + Math.round(approx).toString().length)
|
||||
}
|
||||
return `${t.name}(${this.name}) = ${t.simplify(this.x.toEditableString())}` +
|
||||
(this.approximate ? ' ≈ ' + approx : '')
|
||||
}
|
||||
|
||||
getLabel() {
|
||||
switch(this.labelContent) {
|
||||
case 'name':
|
||||
return this.name
|
||||
break;
|
||||
case 'name + value':
|
||||
switch(this.targetValuePosition) {
|
||||
case 'Next to target':
|
||||
case 'Hidden':
|
||||
return `${this.name} = ${this.x.toString()}`
|
||||
break;
|
||||
case 'With label':
|
||||
return this.getReadableString()
|
||||
break;
|
||||
}
|
||||
case 'null':
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
draw(canvas, ctx) {
|
||||
var xpos = canvas.x2px(this.x.execute())
|
||||
switch(this.displayStyle) {
|
||||
case '— — — — — — —':
|
||||
var dashPxSize = 10
|
||||
for(var i = 0; i < canvas.canvasSize.height; i += dashPxSize*2)
|
||||
canvas.drawLine(ctx, xpos, i, xpos, i+dashPxSize)
|
||||
break;
|
||||
case '⸺⸺⸺⸺⸺⸺':
|
||||
canvas.drawXLine(ctx, this.x.execute())
|
||||
break;
|
||||
case '• • • • • • • • • •':
|
||||
var pointDistancePx = 10
|
||||
var pointSize = 2
|
||||
ctx.beginPath();
|
||||
for(var i = 0; i < canvas.canvasSize.height; i += pointDistancePx)
|
||||
ctx.ellipse(xpos-pointSize/2, i-pointSize/2, pointSize, pointSize)
|
||||
ctx.fill();
|
||||
break;
|
||||
}
|
||||
|
||||
// Label
|
||||
var text = this.getLabel()
|
||||
ctx.font = `${canvas.textsize}px sans-serif`
|
||||
var textSize = canvas.measureText(ctx, text)
|
||||
|
||||
switch(this.labelPosition) {
|
||||
case 'left':
|
||||
case 'above-left':
|
||||
case 'below-left':
|
||||
case 'below':
|
||||
case 'above':
|
||||
canvas.drawVisibleText(ctx, text, xpos-textSize.width-5, textSize.height+5)
|
||||
break;
|
||||
case 'right':
|
||||
case 'above-right':
|
||||
case 'below-right':
|
||||
canvas.drawVisibleText(ctx, text, xpos+5, textSize.height+5)
|
||||
break;
|
||||
}
|
||||
|
||||
if(this.targetValuePosition == 'Next to target' && this.targetElement != null) {
|
||||
var text = this.getTargetValueLabel()
|
||||
var textSize = canvas.measureText(ctx, text)
|
||||
var ypox = canvas.y2px(this.targetElement.execute(this.x.execute()))
|
||||
switch(this.labelPosition) {
|
||||
case 'left':
|
||||
case 'below':
|
||||
case 'above':
|
||||
canvas.drawVisibleText(ctx, text, xpos-textSize.width-5, ypox+textSize.height)
|
||||
break;
|
||||
case 'above-left':
|
||||
canvas.drawVisibleText(ctx, text, xpos-textSize.width-5, ypox+textSize.height+12)
|
||||
break;
|
||||
case 'below-left':
|
||||
canvas.drawVisibleText(ctx, text, xpos-textSize.width-5, ypox+textSize.height-12)
|
||||
break;
|
||||
case 'right':
|
||||
canvas.drawVisibleText(ctx, text, xpos+5, ypox+textSize.height)
|
||||
break;
|
||||
case 'above-right':
|
||||
canvas.drawVisibleText(ctx, text, xpos+5, ypox+textSize.height+12)
|
||||
break;
|
||||
case 'below-right':
|
||||
canvas.drawVisibleText(ctx, text, xpos+5, ypox+textSize.height-12)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
class Enum {
|
||||
constructor(...values) {
|
||||
this.type = 'Enum'
|
||||
this.values = values
|
||||
this.translatedValues = values.map(x => qsTr(x))
|
||||
}
|
||||
}
|
||||
|
||||
class ObjectType {
|
||||
constructor(objType) {
|
||||
this.type = 'ObjectType'
|
||||
this.objType = objType
|
||||
}
|
||||
}
|
||||
|
||||
class List {
|
||||
constructor(type, format = /^.+$/, label = '', forbidAdding = false) {
|
||||
// type can be string, int and double.
|
||||
this.type = 'List'
|
||||
this.valueType = type
|
||||
this.format = format
|
||||
this.label = label
|
||||
this.forbidAdding = forbidAdding
|
||||
}
|
||||
}
|
||||
|
||||
class Dictionary {
|
||||
constructor(type, keyType = 'string', format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = '', postKeyLabel = ': ', forbidAdding = false) {
|
||||
// type & keyType can be string, int and double.
|
||||
this.type = 'Dict'
|
||||
this.valueType = type
|
||||
this.keyType = keyType
|
||||
this.format = format
|
||||
this.keyFormat = keyFormat
|
||||
this.preKeyLabel = preKeyLabel
|
||||
this.postKeyLabel = postKeyLabel
|
||||
this.forbidAdding = forbidAdding
|
||||
}
|
||||
}
|
||||
|
||||
// Common parameters for Enums
|
||||
|
||||
Enum.Position = new Enum(
|
||||
QT_TR_NOOP('above'),
|
||||
QT_TR_NOOP('below'),
|
||||
QT_TR_NOOP('left'),
|
||||
QT_TR_NOOP('right'),
|
||||
QT_TR_NOOP('above-left'),
|
||||
QT_TR_NOOP('above-right'),
|
||||
QT_TR_NOOP('below-left'),
|
||||
QT_TR_NOOP('below-right')
|
||||
)
|
||||
|
||||
Enum.Positioning = new Enum(
|
||||
QT_TR_NOOP('center'),
|
||||
QT_TR_NOOP('top'),
|
||||
QT_TR_NOOP('bottom'),
|
||||
QT_TR_NOOP('left'),
|
||||
QT_TR_NOOP('right'),
|
||||
QT_TR_NOOP('top-left'),
|
||||
QT_TR_NOOP('top-right'),
|
||||
QT_TR_NOOP('bottom-left'),
|
||||
QT_TR_NOOP('bottom-right')
|
||||
)
|
||||
|
||||
Enum.FunctionDisplayType = new Enum(
|
||||
QT_TR_NOOP('application'),
|
||||
QT_TR_NOOP('function')
|
||||
)
|
||||
|
||||
Enum.BodePass = new Enum(
|
||||
QT_TR_NOOP('high'),
|
||||
QT_TR_NOOP('low')
|
||||
)
|
||||
|
||||
|
||||
Enum.XCursorValuePosition = new Enum(
|
||||
QT_TR_NOOP('Next to target'),
|
||||
QT_TR_NOOP('With label'),
|
||||
QT_TR_NOOP('Hidden')
|
||||
)
|
||||
|
||||
|
|
@ -1,663 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "reference.js" as Reference
|
||||
|
||||
const DERIVATION_PRECISION = 0.01
|
||||
|
||||
const OPERATION_PRIORITY = {
|
||||
"+": 10, "-": 10,
|
||||
"*": 20, "/": 20
|
||||
}
|
||||
|
||||
enum ASEType {
|
||||
UNKNOWN,
|
||||
VARIABLE,
|
||||
NUMBER,
|
||||
STRING,
|
||||
FUNCTION,
|
||||
CONSTANT,
|
||||
OPERATION,
|
||||
NEGATION // Example: -x.
|
||||
}
|
||||
|
||||
class AbstractSyntaxElement {
|
||||
type = ASEType.UNKNOWN;
|
||||
|
||||
execute(variables) {
|
||||
return null;
|
||||
}
|
||||
simplify() {
|
||||
return this;
|
||||
}
|
||||
derivate(variable) {
|
||||
return this;
|
||||
}
|
||||
integrate(variable) {
|
||||
return this;
|
||||
}
|
||||
toEditableString() {
|
||||
return "";
|
||||
}
|
||||
toLatex() {
|
||||
return "";
|
||||
}
|
||||
isConstant() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class Variable extends AbstractSyntaxElement {
|
||||
type = ASEType.VARIABLE;
|
||||
|
||||
constructor(variableName) {
|
||||
this.varName = variableName;
|
||||
}
|
||||
|
||||
execute(variables) {
|
||||
if(variables.includes(this.varName)) {
|
||||
return variables[this.varName];
|
||||
} else {
|
||||
throw new EvalError("Unknown variable " + this.varName + ".");
|
||||
}
|
||||
}
|
||||
|
||||
derivate(variable) {
|
||||
if(variable == this.varName)
|
||||
return new NumberElement(1);
|
||||
return this;
|
||||
}
|
||||
|
||||
integrate(variable) {
|
||||
if(variable == this.varName)
|
||||
// <var>^2/2
|
||||
return new Operation(new Operation(this, '^', new NumberElement(2)), '/', new NumberElement(2));
|
||||
return this;
|
||||
}
|
||||
|
||||
toEditableString() {
|
||||
return this.varName;
|
||||
}
|
||||
|
||||
toLatex() {
|
||||
return this.varName;
|
||||
}
|
||||
|
||||
isConstant() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayVariable extends Variable {
|
||||
constructor(arrayName, astIndex) {
|
||||
super(arrayName + "[" + astIndex.toEditableString() + "]")
|
||||
this.arrayName = arrayName;
|
||||
this.astIndex = astIndex;
|
||||
}
|
||||
|
||||
execute(variables) {
|
||||
if(variables.includes(this.arrayName)) {
|
||||
let index = this.astIndex.execute(variables)
|
||||
if(index % 1 != 0 || index < 0) { // Float index.
|
||||
throw new EvalError("Non-integer array index " + index + " used as array index for " + this.varName + ".");
|
||||
} else if(variables[this.arrayName].length <= index) {
|
||||
throw new EvalError("Out-of-range index " + index + " used as array index for " + this.varName + ".");
|
||||
} else {
|
||||
return variables[this.arrayName][index];
|
||||
}
|
||||
} else {
|
||||
throw new EvalError("Unknown variable " + this.varName + ".");
|
||||
}
|
||||
|
||||
toLatex() {
|
||||
return this.varName;
|
||||
}
|
||||
}
|
||||
|
||||
simplify() {
|
||||
return new ArrayVariable(this.arrayName, this.astIndex.simplify());
|
||||
}
|
||||
|
||||
toLatex() {
|
||||
return this.arrayName + '\\left[' + this.astIndex.toLatex() + '\\right]';
|
||||
}
|
||||
|
||||
isConstant() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Constant extends Variable {
|
||||
type = ASEType.CONSTANT;
|
||||
|
||||
constructor(constant) {
|
||||
super(constant)
|
||||
}
|
||||
|
||||
execute(variables) {
|
||||
if(Reference.CONSTANTS_LIST.includes(this.varName)) {
|
||||
return Reference.CONSTANTS[this.varName];
|
||||
} else {
|
||||
throw new EvalError("Unknown constant " + this.varName + ".");
|
||||
}
|
||||
}
|
||||
|
||||
derivate(variable) {
|
||||
if(variable == this.varName)
|
||||
return new NumberElement(0);
|
||||
return this;
|
||||
}
|
||||
|
||||
integrate(variable) {
|
||||
return new Operation(new Variable(variable), '^', this);
|
||||
}
|
||||
|
||||
toEditableString() {
|
||||
return this.varName;
|
||||
}
|
||||
|
||||
toLatex() {
|
||||
return this.varName;
|
||||
}
|
||||
|
||||
isConstant() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class NumberElement extends AbstractSyntaxElement {
|
||||
type = ASEType.NUMBER;
|
||||
|
||||
constructor(number) {
|
||||
this.value = parseFloat(number);
|
||||
}
|
||||
|
||||
derivate(variable) {
|
||||
return new NumberElement(0);
|
||||
}
|
||||
|
||||
integrate(variable) {
|
||||
return new Variable(variable);
|
||||
}
|
||||
|
||||
toEditableString() {
|
||||
return this.value.toString();
|
||||
}
|
||||
|
||||
toLatex() {
|
||||
return this.value.toString();
|
||||
}
|
||||
|
||||
isConstant() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class StringElement extends AbstractSyntaxElement {
|
||||
type = ASEType.STRING;
|
||||
|
||||
constructor(str) {
|
||||
this.str = str;
|
||||
}
|
||||
|
||||
execute(variables) {
|
||||
return this.str
|
||||
}
|
||||
|
||||
derivate(variable) {
|
||||
return this;
|
||||
}
|
||||
|
||||
integrate(variable) {
|
||||
return this;
|
||||
}
|
||||
|
||||
toEditableString() {
|
||||
return '"' + this.str + '"';
|
||||
}
|
||||
|
||||
toLatex() {
|
||||
return this.str;
|
||||
}
|
||||
|
||||
isConstant() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionElement extends AbstractSyntaxElement {
|
||||
type = ASEType.FUNCTION;
|
||||
|
||||
constructor(functionName, astArguments) {
|
||||
this.function = functionName;
|
||||
this.args = astArguments;
|
||||
}
|
||||
|
||||
execute(variables) {
|
||||
if(this.function == "derivate") {
|
||||
return executeDerivative(variables)
|
||||
} else if(this.function == "integrate") {
|
||||
return executeIntegral(variables)
|
||||
} else if(Reference.FUNCTIONS_LIST.includes(this.function)) {
|
||||
let args = this.args.map(arg => arg.execute(variables));
|
||||
return Reference.FUNCTIONS[this.function](...args);
|
||||
} else {
|
||||
throw new EvalError("Unknown function " + this.function + ".");
|
||||
}
|
||||
}
|
||||
|
||||
executeDerivative(variables) {
|
||||
// Calculate derivation.
|
||||
if(this.args.length == 2)
|
||||
if(this.args[1] instanceof Variable) {
|
||||
let d = this.args[1].varName; // derivative variable name.
|
||||
if(Object.keys(variables).includes(d)) {
|
||||
let plus = this.args[0].execute(Object.assign({}, variables, {d: variables[d]+DERIVATION_PRECISION/2}));
|
||||
let min = this.args[0].execute(Object.assign({}, variables, {d: variables[d]-DERIVATION_PRECISION/2}));
|
||||
return (plus-min)/DERIVATION_PRECISION
|
||||
} else
|
||||
throw new EvalError("Undefined variable " + d + ".");
|
||||
} else
|
||||
throw new EvalError(`Argument 1 of function derivate must be a variable.`)
|
||||
else
|
||||
throw new EvalError(`Function 'derivate' can only have 2 arguments. ${this.args.length} provided.`)
|
||||
}
|
||||
|
||||
executeIntegral(variables) {
|
||||
// Calculate integral.
|
||||
// Using simons rule
|
||||
// https://en.wikipedia.org/wiki/Simpson%27s_rule
|
||||
let d, f, a, b;
|
||||
if(this.args.length == 2)
|
||||
// Integral(f,var) integral of f by var.
|
||||
if(this.args[1] instanceof Variable)
|
||||
if(Object.keys(variables).includes(d)) {
|
||||
d = this.args[1].varName; // derivative variable name.
|
||||
if(!Object.keys(variables).includes(d))
|
||||
throw new EvalError("Undefined variable " + d + ".")
|
||||
a = 0;
|
||||
b = variables[d];
|
||||
f = this.args[0].execute;
|
||||
} else
|
||||
else
|
||||
throw new EvalError(`Argument 2 of function derivate must be a variable.`)
|
||||
else if(this.args.length == 4)
|
||||
// Integral(a,b,f,var) integral from a to b of f by var.
|
||||
if(this.args[3] instanceof Variable)
|
||||
if(Object.keys(variables).includes(d)) {
|
||||
a = this.args[0].execute(variables);
|
||||
b = this.args[1].execute(variables);
|
||||
f = this.args[2].execute;
|
||||
d = this.args[3].varName; // derivative variable name.
|
||||
if(!Object.keys(variables).includes(d))
|
||||
throw new EvalError("Undefined variable " + d + ".");
|
||||
}
|
||||
else
|
||||
throw new EvalError(`Argument 4 of function derivate must be a variable.`)
|
||||
else
|
||||
throw new EvalError(`Function 'derivate' can only have 2 or 4 arguments. ${this.args.length} provided.`)
|
||||
|
||||
// (b-a)/6*(f(a)+4*f((a+b)/2)+f(b))
|
||||
let f_a = f(Object.assign({}, variables, {d: a})), f_b = f(Object.assign({}, variables, {d: b}));
|
||||
let f_m = f(Object.assign({}, variables, {d: (a+b)/2}))
|
||||
return (b-a)/6*(f_a+4*f_m+f_b);
|
||||
}
|
||||
|
||||
simplify() {
|
||||
let args = this.args.map(arg => arg.simplify(variables));
|
||||
let newFunc = new FunctionElement(this.function, args);
|
||||
let result;
|
||||
if(newFunc.isConstant() && (result = newFunc.execute({})) % 1 == 0) { // Simplification (e.g. cos(0), sin(π/2)...)
|
||||
return new NumberElement(result);
|
||||
} else {
|
||||
return newFunc;
|
||||
}
|
||||
}
|
||||
|
||||
derivate(variable) {
|
||||
//TODO: Use DERIVATIVES elements in reference.
|
||||
return new FunctionElement("derivate", this, variable);
|
||||
}
|
||||
|
||||
integrate(variable) {
|
||||
//TODO: Use INTEGRALS elements in reference.
|
||||
return new FunctionElement("integrate", this, variable);
|
||||
}
|
||||
|
||||
toEditableString() {
|
||||
return this.function + '(' + this.args.map(arg => arg.toEditableString()).join(', ') + ')';
|
||||
}
|
||||
|
||||
toLatex() {
|
||||
switch(this.function) {
|
||||
case "sqrt":
|
||||
return '\\sqrt{' + this.args.map(arg => arg.toLatex()).join(', ') + '}';
|
||||
case "abs":
|
||||
return '\\left|' + this.args.map(arg => arg.toLatex()).join(', ') + '\\right|';
|
||||
case "floor":
|
||||
return '\\left\\lfloor' + this.args.map(arg => arg.toLatex()).join(', ') + '\\right\\rfloor';
|
||||
case "ceil":
|
||||
return '\\left\\lceil' + this.args.map(arg => arg.toLatex()).join(', ') + '\\right\\rceil';
|
||||
default:
|
||||
return '\\mathrm{' + this.function + '}\\left(' + this.args.map(arg => arg.toLatex()).join(', ') + '\\right)';
|
||||
}
|
||||
}
|
||||
|
||||
isConstant() {
|
||||
if(this.function == "derivate")
|
||||
return this.args[0].isConstant();
|
||||
else if(this.function == "integrate")
|
||||
return this.args.length == 4 && this.args[0].isConstant() && this.args[1].isConstant() && this.args[2].isConstant();
|
||||
else
|
||||
return this.args.every(x => x.isConstant());
|
||||
}
|
||||
}
|
||||
|
||||
class Operation extends AbstractSyntaxElement {
|
||||
type = ASEType.OPERATION;
|
||||
|
||||
constructor(leftHand, operation, rightHand) {
|
||||
this.leftHand = leftHand;
|
||||
this.ope = operation;
|
||||
this.rightHand = rightHand;
|
||||
}
|
||||
|
||||
evaluate(variables) {
|
||||
switch(this.ope) {
|
||||
case '+':
|
||||
return this.leftHand.evaluate(variables) + this.rightHand.evaluate(variables);
|
||||
case '-':
|
||||
return this.leftHand.evaluate(variables) - this.rightHand.evaluate(variables);
|
||||
case '*':
|
||||
return this.leftHand.evaluate(variables) * this.rightHand.evaluate(variables);
|
||||
case '/':
|
||||
return this.leftHand.evaluate(variables) / this.rightHand.evaluate(variables);
|
||||
case '%':
|
||||
return this.leftHand.evaluate(variables) % this.rightHand.evaluate(variables);
|
||||
case '^':
|
||||
return Math.pow(this.leftHand.evaluate(variables), this.rightHand.evaluate(variables));
|
||||
default:
|
||||
throw new EvalError("Unknown operator " + ope + ".");
|
||||
}
|
||||
}
|
||||
|
||||
simplify() {
|
||||
let leftHand = this.leftHand.simplify();
|
||||
let rightHand = this.rightHand.simplify();
|
||||
let newOpe = new Operation(leftHand, this.ope, rightHand);
|
||||
if(leftHand.isConstant() && rightHand.isConstant() && Math.abs(newOpe.execute({})) < 1000000) {
|
||||
// Do not simplify to too big numbers
|
||||
switch(this.ope) {
|
||||
case '+':
|
||||
case '-':
|
||||
case '*':
|
||||
case '^':
|
||||
case '%':
|
||||
return new NumberElement(newOpe.execute({}));
|
||||
case '/':
|
||||
if(result % 1 == 0)
|
||||
return new NumberElement(newOpe.execute({}));
|
||||
else {
|
||||
let simplified = simplifyFraction(leftHand.number, rightHand.number)
|
||||
return new Operation(new NumberElement(simplified[0]), '/', new NumberElement(simplified[1]))
|
||||
}
|
||||
return this.leftHand.evaluate(variables) / this.rightHand.evaluate(variables);
|
||||
return Math.pow(this.leftHand.evaluate(variables), this.rightHand.evaluate(variables));
|
||||
default:
|
||||
throw new EvalError("Unknown operator " + ope + ".");
|
||||
}
|
||||
} else {
|
||||
// Simplifications of +- 0 or *1
|
||||
switch(this.ope) {
|
||||
case '+':
|
||||
case '-':
|
||||
if(leftHand.type == ASEType.NUMBER && leftHand.value == 0)
|
||||
return rightHand;
|
||||
else if(rightHand.type == ASEType.NUMBER && rightHand.value == 0) {
|
||||
if(ope == '-') leftHand.value = -leftHand.value;
|
||||
return leftHand;
|
||||
} else
|
||||
return newOpe
|
||||
case '*':
|
||||
if((leftHand.type == ASEType.NUMBER && leftHand.value == 0) || (rightHand.type == ASEType.NUMBER && rightHand.value == 0))
|
||||
return new NumberElement(0);
|
||||
else if(leftHand.type == ASEType.NUMBER && leftHand.value == 1)
|
||||
return rightHand;
|
||||
else if(rightHand.type == ASEType.NUMBER && rightHand.value == 1)
|
||||
return leftHand;
|
||||
else
|
||||
return newOpe
|
||||
case '^':
|
||||
if(rightHand.type == ASEType.NUMBER && rightHand.value == 0)
|
||||
return new NumberElement(1);
|
||||
else if(rightHand.type == ASEType.NUMBER && rightHand.value == 1)
|
||||
return new NumberElement(leftHand.value);
|
||||
else
|
||||
return newOpe;
|
||||
case '/':
|
||||
if(rightHand.type == ASEType.NUMBER && rightHand.value == 1)
|
||||
return new NumberElement(leftHand.value);
|
||||
else
|
||||
return newOpe;
|
||||
case '%':
|
||||
return newOpe;
|
||||
default:
|
||||
throw new EvalError("Unknown operator " + ope + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
derivate(variable) {
|
||||
switch(this.ope) {
|
||||
case '-':
|
||||
case '+':
|
||||
return new Operation(this.leftHand.derivate(variable), this.ope, this.rightHand.derivate(variable));
|
||||
case '*':
|
||||
return new Operation(
|
||||
new Operation(this.leftHand, '*', this.rightHand.derivate(variable)),
|
||||
'+',
|
||||
new Operation(this.leftHand.derivate(variable), '*', this.rightHand),
|
||||
);
|
||||
case '/':
|
||||
return new Operation(
|
||||
new Operation(
|
||||
new Operation(this.leftHand, '*', this.rightHand.derivate(variable)),
|
||||
'-',
|
||||
new Operation(this.leftHand.derivate(variable), '*', this.rightHand),
|
||||
),
|
||||
'/',
|
||||
new Operation(this.rightHand, '^', new NumberElement(2))
|
||||
);
|
||||
case '^':
|
||||
case '%':
|
||||
return new FunctionElement("derivate", this.toEditableString());
|
||||
default:
|
||||
throw new EvalError("Unknown operator " + ope + ".");
|
||||
}
|
||||
}
|
||||
|
||||
integrate(variable) {
|
||||
switch(this.ope) {
|
||||
case '-':
|
||||
case '+':
|
||||
return new Operation(this.leftHand.integrate(variable), this.ope, this.rightHand.integrate(variable));
|
||||
case '*':
|
||||
return new Operation(
|
||||
new Operation(this.leftHand.derivate(variable), '*', this.rightHand),
|
||||
'+',
|
||||
new Operation(this.leftHand, '*', this.rightHand.derivate(variable))
|
||||
);
|
||||
case '/':
|
||||
return new Operation(
|
||||
new Operation(this.leftHand.derivate(variable), '*', this.rightHand),
|
||||
'+',
|
||||
new Operation(this.leftHand, '*', this.rightHand.derivate(variable))
|
||||
);
|
||||
case '^':
|
||||
case '%':
|
||||
return new FunctionElement("integrate", this.toEditableString());
|
||||
default:
|
||||
throw new EvalError("Unknown operator " + ope + ".");
|
||||
}
|
||||
}
|
||||
|
||||
toEditableString() {
|
||||
let leftString = this.leftHand.toEditableString();
|
||||
let rightString = this.rightHand.toEditableString();
|
||||
if(this.leftHand.type == ASEType.OPERATION && OPERATION_PRIORITY[this.ope] > OPERATION_PRIORITY[this.leftHand.ope])
|
||||
leftString = "(" + leftString + ")"
|
||||
if(this.rightHand.type == ASEType.OPERATION && OPERATION_PRIORITY[this.ope] > OPERATION_PRIORITY[this.rightHand.ope])
|
||||
rightString = "(" + rightString + ")"
|
||||
return leftString + " " + this.ope + " " + rightString;
|
||||
}
|
||||
|
||||
|
||||
toLatex() {
|
||||
switch(this.ope) {
|
||||
case '-':
|
||||
case '+':
|
||||
return this.leftHand.toLatex() + this.ope + this.rightHand.toLatex();
|
||||
case '*':
|
||||
return this.leftHand.toLatex() + " \\times " + this.rightHand.toLatex();
|
||||
case '%':
|
||||
return this.leftHand.toLatex() + " \\mathrm{mod} " + this.rightHand.toLatex();
|
||||
case '/':
|
||||
return "\\frac{" + this.leftHand.toLatex() + "}{" + this.rightHand.toLatex() + "}"
|
||||
case '^':
|
||||
return this.leftHand.toLatex() + "^{" + this.rightHand.toLatex() + "}";
|
||||
default:
|
||||
throw new EvalError("Unknown operator " + ope + ".");
|
||||
}
|
||||
return this.leftHand.toLatex() + ope + this.rightHand.toLatex();
|
||||
}
|
||||
|
||||
isConstant() {
|
||||
return this.leftHand.isConstant() && this.rightHand.isConstant();
|
||||
}
|
||||
}
|
||||
|
||||
function simplifyFraction(num,den) {
|
||||
// More than gcd because it allows decimals fractions.
|
||||
let mult = 1;
|
||||
if(num%1 != 0)
|
||||
mult = Math.max(mult,Math.pow(10,num.toString().split('.')[1].length))
|
||||
else if(den%1 != 0)
|
||||
mult = Math.max(mult,Math.pow(10,den.toString().split('.')[1].length))
|
||||
let a = Math.abs(num*mult);
|
||||
let b = Math.abs(den*mult);
|
||||
let gcd = 0
|
||||
if (b > a) {let temp = a; a = b; b = temp;}
|
||||
while (gcd == 0) {
|
||||
if (b == 0) gcd = a;
|
||||
a %= b;
|
||||
if (a == 0) gcd = b;
|
||||
b %= a;
|
||||
}
|
||||
return [num*mult/gcd, den*mult/gcd]
|
||||
}
|
||||
|
||||
class Negation extends AbstractSyntaxElement {
|
||||
type = ASEType.NEGATION;
|
||||
|
||||
constructor(variableName) {
|
||||
this.varName = variableName;
|
||||
}
|
||||
|
||||
execute(variables) {
|
||||
if(variables.includes(this.varName)) {
|
||||
return variables[this.varName];
|
||||
} else {
|
||||
throw new EvalError("Unknown variable " + this.varName + ".");
|
||||
}
|
||||
}
|
||||
|
||||
derivate(variable) {
|
||||
if(variable == this.varName)
|
||||
return new NumberElement(1);
|
||||
return this;
|
||||
}
|
||||
|
||||
integrate(variable) {
|
||||
if(variable == this.varName)
|
||||
// <var>^2/2
|
||||
return new Operation(new Operation(this, '^', new NumberElement(2)), '/', new NumberElement(2));
|
||||
return this;
|
||||
}
|
||||
|
||||
toEditableString() {
|
||||
return this.varName;
|
||||
}
|
||||
|
||||
toLatex() {
|
||||
return this.varName;
|
||||
}
|
||||
|
||||
isConstant() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class Negation extends AbstractSyntaxElement {
|
||||
type = ASEType.NEGATION;
|
||||
|
||||
constructor(expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
execute(variables) {
|
||||
if(variables.includes(this.arrayName)) {
|
||||
let index = this.astIndex.execute(variables)
|
||||
if(index % 1 != 0 || index < 0) { // Float index.
|
||||
throw new EvalError("Non-integer array index " + index + " used as array index for " + this.varName + ".");
|
||||
} else if(variables[this.arrayName].length <= index) {
|
||||
throw new EvalError("Out-of-range index " + index + " used as array index for " + this.varName + ".");
|
||||
} else {
|
||||
return variables[this.arrayName][index];
|
||||
}
|
||||
} else {
|
||||
throw new EvalError("Unknown variable " + this.varName + ".");
|
||||
}
|
||||
|
||||
toLatex() {
|
||||
return this.varName;
|
||||
}
|
||||
}
|
||||
|
||||
simplify() {
|
||||
return new Negation(this.expression.simplify());
|
||||
}
|
||||
|
||||
derivate(variable) {
|
||||
return new Negation(this.expression.derivate(variable));
|
||||
}
|
||||
|
||||
integrate(variable) {
|
||||
return new Negation(this.expression.integrate(variable));
|
||||
}
|
||||
|
||||
toLatex() {
|
||||
return '-' + this.expression.toLatex();
|
||||
}
|
||||
|
||||
isConstant() {
|
||||
return this.expression.isConstant();
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
import "ast.js" as AST
|
||||
import "tokenizer.js" as TK
|
||||
|
||||
|
||||
class ExpressionBuilder {
|
||||
constructor(tokenizer) {
|
||||
this.tokenizer = tokenizer;
|
||||
}
|
||||
|
||||
parseExpression(delimitors = '') {
|
||||
// Parse a sequence of operations, and orders them based on OPERATION_PRIORITY.
|
||||
let elements = []
|
||||
let operators = []
|
||||
let firstToken = this.tokenizer.peek();
|
||||
if(firstToken.type == TK.TokenType.OPERATOR) // First operations.
|
||||
if(firstToken.value == "-") {
|
||||
// TODO: Set initial argument.
|
||||
this.tokenizer.skip(TK.TokenType.OPERATOR)
|
||||
} else
|
||||
tokenizer.input.raise(`Invalid operator ${firstToken.value} at begining of statement.`)
|
||||
else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
parseOperation()`
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
const CONSTANTS = {
|
||||
"π": Math.PI,
|
||||
"pi": Math.PI,
|
||||
"inf": Infinity,
|
||||
"infinity": Infinity,
|
||||
"∞": Infinity,
|
||||
"e": Infinity
|
||||
};
|
||||
const CONSTANTS_LIST = Object.keys(CONSTANTS);
|
||||
|
||||
const FUNCTIONS = {
|
||||
"abs": Math.abs,
|
||||
"acos": Math.acos,
|
||||
"acosh": Math.acosh,
|
||||
"asin": Math.asin,
|
||||
"asinh": Math.asinh,
|
||||
"atan": Math.atan,
|
||||
"atan2": Math.atan2,
|
||||
"atanh": Math.atanh,
|
||||
"cbrt": Math.cbrt,
|
||||
"ceil": Math.ceil,
|
||||
"clz32": Math.clz32,
|
||||
"cos": Math.cos,
|
||||
"cosh": Math.cosh,
|
||||
"exp": Math.exp,
|
||||
"expm1": Math.expm1,
|
||||
"floor": Math.floor,
|
||||
"fround": Math.fround,
|
||||
"hypot": Math.hypot,
|
||||
"imul": Math.imul,
|
||||
"log": Math.log,
|
||||
"log10": Math.log10,
|
||||
"log1p": Math.log1p,
|
||||
"log2": Math.log2,
|
||||
"max": Math.max,
|
||||
"min": Math.min,
|
||||
"pow": Math.log2,
|
||||
"random": Math.random,
|
||||
"round": Math.round,
|
||||
"sign": Math.sign,
|
||||
"sin": Math.sin,
|
||||
"sinh": Math.sinh,
|
||||
"sqrt": Math.sqrt,
|
||||
"tan": Math.tan,
|
||||
"tanh": Math.tanh,
|
||||
"trunc": Math.trunc,
|
||||
}
|
||||
const FUNCTIONS_LIST = Object.keys(FUNCTIONS);
|
||||
// TODO: Complete
|
||||
const DERIVATIVES = {
|
||||
"abs": "abs(<1>)/<1>",
|
||||
"acos": "-derivate(<1>)/sqrt(1-(<1>)^2)",
|
||||
"acosh": "derivate(<1>)/sqrt((<1>)^2-1)",
|
||||
"asin": "derivate(<1>)/sqrt(1-(<1>)^2)",
|
||||
"asinh": "derivate(<1>)/sqrt((<1>)^2+1)",
|
||||
"atan": "derivate(<1>)/(1+(<1>)^2)",
|
||||
"atan2": "",
|
||||
}
|
||||
const INTEGRALS = {
|
||||
"abs": "integrate(<1>)*sign(<1>)",
|
||||
"acos": "",
|
||||
"acosh": "",
|
||||
"asin": "",
|
||||
"asinh": "",
|
||||
"atan": "",
|
||||
"atan2": "",
|
||||
}
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
.import "reference.js" as Reference
|
||||
|
||||
const WHITESPACES = " \t\n\r"
|
||||
const STRING_LIMITORS = '"\'`';
|
||||
const OPERATORS = "+-*/^%";
|
||||
const PUNCTUTATION = "()[]{},";
|
||||
const NUMBER_CHARS = "0123456789."
|
||||
const IDENTIFIER_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_₀₁₂₃₄₅₆₇₈₉αβγδεζηθκλμξρςστφχψωₐₑₒₓₔₕₖₗₘₙₚₛₜ"
|
||||
|
||||
enum TokenType {
|
||||
// Expression type
|
||||
VARIABLE,
|
||||
CONSTANT,
|
||||
FUNCTION,
|
||||
OPERATOR,
|
||||
PUNCT,
|
||||
NUMBER,
|
||||
STRING
|
||||
}
|
||||
|
||||
class Token {
|
||||
constructor(type, value) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
class ExpressionTokenizer {
|
||||
constructor(input) {
|
||||
this.input = input;
|
||||
this.currentToken = null;
|
||||
}
|
||||
|
||||
skipWhitespaces() {
|
||||
while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek()))
|
||||
this.input.next();
|
||||
}
|
||||
|
||||
readString() {
|
||||
let delimitation = this.input.peek();
|
||||
if(STRING_LIMITORS.includes(delimitation)) {
|
||||
this.input.skip(delimitation)
|
||||
let included = "";
|
||||
let justEscaped = false;
|
||||
while(!this.input.atEnd() && (!STRING_LIMITORS.includes(this.input.peek()) || justEscaped)) {
|
||||
justEscaped = this.input.peek() == "\\"
|
||||
if(!justEscaped)
|
||||
included += this.input.next();
|
||||
}
|
||||
this.input.skip(delimitation)
|
||||
return new Token(TokenType.STRING, included);
|
||||
} else {
|
||||
this.input.raise("Unexpected " + delimitation + ". Expected string delimitator")
|
||||
}
|
||||
}
|
||||
|
||||
readNumber() {
|
||||
let included = "";
|
||||
let hasDot = false;
|
||||
while(!this.input.atEnd() && NUMBER_CHARS.includes(this.input.peek())) {
|
||||
if(this.input.peek() == ".") {
|
||||
if(hasDot) this.input.raise("Unexpected '.'. Expected digit")
|
||||
hasDot = true;
|
||||
}
|
||||
included += this.input.next();
|
||||
}
|
||||
}
|
||||
|
||||
readIdentifier() {
|
||||
let identifier = "";
|
||||
let hasDot = false;
|
||||
while(!this.input.atEnd() && IDENTIFIER_CHARS.includes(this.input.peek())) {
|
||||
identifier += this.input.next();
|
||||
}
|
||||
if(Reference.CONSTANTS_LIST.includes(identifier.toLowerCase())) {
|
||||
return new Token(TokenType.CONSTANT, identifier.toLowerCase())
|
||||
} else if(Reference.FUNCTIONS_LIST.includes(identifier.toLowerCase())) {
|
||||
return new Token(TokenType.FUNCTION, identifier.toLowerCase())
|
||||
} else {
|
||||
return new Token(TokenType.VARIABLE, identifier)
|
||||
}
|
||||
}
|
||||
|
||||
readNextToken() {
|
||||
this.skipWhitespaces()
|
||||
if(input.atEnd()) return null;
|
||||
let c = input.peek();
|
||||
if(STRING_LIMITORS.includes(c)) return this.readString();
|
||||
if(NUMBER_CHARS.includes(c)) return this.readNumber();
|
||||
if(IDENTIFIER_CHARS.includes(c)) return this.readIdentifier();
|
||||
if(Reference.CONSTANTS_LIST.includes(c)) return new Token(TokenType.CONSTANT, c);
|
||||
if(OPERATORS.includes(c)) return new Token(TokenType.OPERATOR, c);
|
||||
if(PUNCTUTATION.includes(c)) return new Token(TokenType.PUNCT, c);
|
||||
this.input.throw("Unknown token character " + c)
|
||||
}
|
||||
|
||||
peek() {
|
||||
if(this.currentToken == null) this.currentToken = this.readNextToken();
|
||||
return this.currentToken;
|
||||
}
|
||||
|
||||
next() {
|
||||
let tmp;
|
||||
if(this.currentToken == null)
|
||||
tmp = this.readNextToken();
|
||||
else
|
||||
tmp = this.currentToken;
|
||||
this.currentToken = null;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
atEnd() {
|
||||
return this.peek() == null;
|
||||
}
|
||||
|
||||
skip(type) {
|
||||
Token next = Next();
|
||||
if(next.type != type)
|
||||
input.raise("Unexpected token " + next.type.oLowerCase() + ' "' + next.value + '". Expected ' + type.toLowerCase());
|
||||
}
|
||||
}
|
|
@ -1,349 +0,0 @@
|
|||
/**
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.pragma library
|
||||
|
||||
var powerpos = {
|
||||
"-": "⁻",
|
||||
"+": "⁺",
|
||||
"=": "⁼",
|
||||
" ": " ",
|
||||
"(": "⁽",
|
||||
")": "⁾",
|
||||
"0": "⁰",
|
||||
"1": "¹",
|
||||
"2": "²",
|
||||
"3": "³",
|
||||
"4": "⁴",
|
||||
"5": "⁵",
|
||||
"6": "⁶",
|
||||
"7": "⁷",
|
||||
"8": "⁸",
|
||||
"9": "⁹",
|
||||
"a": "ᵃ",
|
||||
"b": "ᵇ",
|
||||
"c": "ᶜ",
|
||||
"d": "ᵈ",
|
||||
"e": "ᵉ",
|
||||
"f": "ᶠ",
|
||||
"g": "ᵍ",
|
||||
"h": "ʰ",
|
||||
"i": "ⁱ",
|
||||
"j": "ʲ",
|
||||
"k": "ᵏ",
|
||||
"l": "ˡ",
|
||||
"m": "ᵐ",
|
||||
"n": "ⁿ",
|
||||
"o": "ᵒ",
|
||||
"p": "ᵖ",
|
||||
"r": "ʳ",
|
||||
"s": "ˢ",
|
||||
"t": "ᵗ",
|
||||
"u": "ᵘ",
|
||||
"v": "ᵛ",
|
||||
"w": "ʷ",
|
||||
"x": "ˣ",
|
||||
"y": "ʸ",
|
||||
"z": "ᶻ"
|
||||
}
|
||||
|
||||
var indicepos = {
|
||||
"-": "₋",
|
||||
"+": "₊",
|
||||
"=": "₌",
|
||||
"(": "₍",
|
||||
")": "₎",
|
||||
" ": " ",
|
||||
"0": "₀",
|
||||
"1": "₁",
|
||||
"2": "₂",
|
||||
"3": "₃",
|
||||
"4": "₄",
|
||||
"5": "₅",
|
||||
"6": "₆",
|
||||
"7": "₇",
|
||||
"8": "₈",
|
||||
"9": "₉",
|
||||
"a": "ₐ",
|
||||
"e": "ₑ",
|
||||
"h": "ₕ",
|
||||
"i": "ᵢ",
|
||||
"j": "ⱼ",
|
||||
"k": "ₖ",
|
||||
"l": "ₗ",
|
||||
"m": "ₘ",
|
||||
"n": "ₙ",
|
||||
"o": "ₒ",
|
||||
"p": "ₚ",
|
||||
"r": "ᵣ",
|
||||
"s": "ₛ",
|
||||
"t": "ₜ",
|
||||
"u": "ᵤ",
|
||||
"v": "ᵥ",
|
||||
"x": "ₓ",
|
||||
}
|
||||
// Put a text in sup position
|
||||
function textsup(text) {
|
||||
var ret = ""
|
||||
text = text.toString()
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
if(Object.keys(powerpos).indexOf(text[i]) >= 0) {
|
||||
ret += powerpos[text[i]]
|
||||
} else {
|
||||
ret += text[i]
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Put a text in sub position
|
||||
function textsub(text) {
|
||||
var ret = ""
|
||||
text = text.toString()
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
if(Object.keys(indicepos).indexOf(text[i]) >= 0) {
|
||||
ret += indicepos[text[i]]
|
||||
} else {
|
||||
ret += text[i]
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
function simplifyExpression(str) {
|
||||
var replacements = [
|
||||
// Operations not done by parser.
|
||||
[// Decomposition way 2
|
||||
/(^.?|[+-] |\()([-.\d\w]+) ([*/]) \((([-.\d\w] [*/] )?[-\d\w.]+) ([+\-]) (([-.\d\w] [*/] )?[\d\w.+]+)\)(.?$| [+-]|\))/g,
|
||||
"$1$2 $3 $4 $6 $2 $3 $7$9"
|
||||
],
|
||||
[ // Decomposition way 2
|
||||
/(^.?|[+-] |\()\((([-.\d\w] [*/] )?[-\d\w.]+) ([+\-]) (([-.\d\w] [*/] )?[\d\w.+]+)\) ([*/]) ([-.\d\w]+)(.?$| [+-]|\))/g,
|
||||
"$1$2 $7 $8 $4 $5 $7 $8$9"
|
||||
],
|
||||
[ // Factorisation of π elements.
|
||||
/(([-\d\w.]+ [*/] )*)(pi|π)(( [/*] [-\d\w.]+)*) ([+-]) (([-\d\w.]+ [*/] )*)(pi|π)(( [/*] [-\d\w.]+)*)?/g,
|
||||
function(match, m1, n1, pi1, m2, ope2, n2, opeM, m3, n3, pi2, m4, ope4, n4) {
|
||||
// g1, g2, g3 , g4, g5, g6, g7, g8, g9, g10, g11,g12 , g13
|
||||
// We don't care about mx & pix, ope2 & ope4 are either / or * for n2 & n4.
|
||||
// n1 & n3 are multiplied, opeM is the main operation (- or +).
|
||||
// Putting all n in form of number
|
||||
//n2 = n2 == undefined ? 1 : parseFloat(n)
|
||||
n1 = m1 == undefined ? 1 : eval(m1 + '1')
|
||||
n2 = m2 == undefined ? 1 : eval('1' + m2)
|
||||
n3 = m3 == undefined ? 1 : eval(m3 + '1')
|
||||
n4 = m4 == undefined ? 1 : eval('1' + m4)
|
||||
//var [n1, n2, n3, n4] = [n1, n2, n3, n4].map(n => n == undefined ? 1 : parseFloat(n))
|
||||
// Falling back to * in case it does not exist (the corresponding n would be 1)
|
||||
var [ope2, ope4] = [ope2, ope4].map(ope => ope == '/' ? '/' : '*')
|
||||
var coeff1 = n1*n2
|
||||
var coeff2 = n3*n4
|
||||
var coefficient = coeff1+coeff2-(opeM == '-' ? 2*coeff2 : 0)
|
||||
|
||||
return `${coefficient} * π`
|
||||
}
|
||||
],
|
||||
[ // Removing parenthesis when content is only added from both sides.
|
||||
/(^.?|[+-] |\()\(([^)(]+)\)(.?$| [+-]|\))/g,
|
||||
function(match, b4, middle, after) {return `${b4}${middle}${after}`}
|
||||
],
|
||||
[ // Removing parenthesis when content is only multiplied.
|
||||
/(^.?|[*\/] |\()\(([^)(+-]+)\)(.?$| [*\/+-]|\))/g,
|
||||
function(match, b4, middle, after) {return `${b4}${middle}${after}`}
|
||||
],
|
||||
[ // Removing parenthesis when content is only multiplied.
|
||||
/(^.?|[*\/-+] |\()\(([^)(+-]+)\)(.?$| [*\/]|\))/g,
|
||||
function(match, b4, middle, after) {return `${b4}${middle}${after}`}
|
||||
],
|
||||
[// Simplification additions/substractions.
|
||||
/(^.?|[^*\/] |\()([-.\d]+) (\+|\-) (\([^)(]+\)|[^)(]+) (\+|\-) ([-.\d]+)(.?$| [^*\/]|\))/g,
|
||||
function(match, b4, n1, op1, middle, op2, n2, after) {
|
||||
var total
|
||||
if(op2 == '+') {
|
||||
total = parseFloat(n1) + parseFloat(n2)
|
||||
} else {
|
||||
total = parseFloat(n1) - parseFloat(n2)
|
||||
}
|
||||
return `${b4}${total} ${op1} ${middle}${after}`
|
||||
}
|
||||
],
|
||||
[// Simplification multiplications/divisions.
|
||||
/([-.\d]+) (\*|\/) (\([^)(]+\)|[^)(+-]+) (\*|\/) ([-.\d]+)/g,
|
||||
function(match, n1, op1, middle, op2, n2) {
|
||||
if(parseInt(n1) == n1 && parseInt(n2) == n2 && op2 == '/' &&
|
||||
(parseInt(n1) / parseInt(n2)) % 1 != 0) {
|
||||
// Non int result for int division.
|
||||
return `(${n1} / ${n2}) ${op1} ${middle}`
|
||||
} else {
|
||||
if(op2 == '*') {
|
||||
return `${parseFloat(n1) * parseFloat(n2)} ${op1} ${middle}`
|
||||
} else {
|
||||
return `${parseFloat(n1) / parseFloat(n2)} ${op1} ${middle}`
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[// Starting & ending parenthesis if not needed.
|
||||
/^\s*\((.*)\)\s*$/g,
|
||||
function(match, middle) {
|
||||
var str = middle
|
||||
// Replace all groups
|
||||
while(/\([^)(]+\)/g.test(str))
|
||||
str = str.replace(/\([^)(]+\)/g, '')
|
||||
// There shouldn't be any more parenthesis
|
||||
// If there is, that means the 2 parenthesis are needed.
|
||||
if(!str.includes(')') && !str.includes('(')) {
|
||||
return middle
|
||||
} else {
|
||||
return `(${middle})`
|
||||
}
|
||||
|
||||
}
|
||||
],
|
||||
// Simple simplifications
|
||||
[/(\s|^|\()0(\.0+)? \* (\([^)(]+\))/g, '$10'],
|
||||
[/(\s|^|\()0(\.0+)? \* ([^)(+-]+)/g, '$10'],
|
||||
[/(\([^)(]\)) \* 0(\.0+)?(\s|$|\))/g, '0$3'],
|
||||
[/([^)(+-]) \* 0(\.0+)?(\s|$|\))/g, '0$3'],
|
||||
[/(\s|^|\()1(\.0+)? (\*|\/) /g, '$1'],
|
||||
[/(\s|^|\()0(\.0+)? (\+|\-) /g, '$1'],
|
||||
[/ (\*|\/) 1(\.0+)?(\s|$|\))/g, '$3'],
|
||||
[/ (\+|\-) 0(\.0+)?(\s|$|\))/g, '$3'],
|
||||
[/(^| |\() /g, '$1'],
|
||||
[/ ($|\))/g, '$1'],
|
||||
]
|
||||
|
||||
// Replacements
|
||||
var found
|
||||
do {
|
||||
found = false
|
||||
for(var replacement of replacements)
|
||||
while(replacement[0].test(str)) {
|
||||
found = true
|
||||
str = str.replace(replacement[0], replacement[1])
|
||||
}
|
||||
} while(found)
|
||||
return str
|
||||
}
|
||||
|
||||
|
||||
function makeExpressionReadable(str) {
|
||||
var replacements = [
|
||||
// variables
|
||||
[/pi/g, 'π'],
|
||||
[/Infinity/g, '∞'],
|
||||
[/inf/g, '∞'],
|
||||
// Other
|
||||
[/ \* /g, '×'],
|
||||
[/ \^ /g, '^'],
|
||||
[/\^\(([^\^]+)\)/g, function(match, p1) { return textsup(p1) }],
|
||||
[/\^([^ "]+)/g, function(match, p1) { return textsup(p1) }],
|
||||
[/_\(([^_]+)\)/g, function(match, p1) { return textsub(p1) }],
|
||||
[/_([^ "]+)/g, function(match, p1) { return textsub(p1) }],
|
||||
[/\[([^\[\]]+)\]/g, function(match, p1) { return textsub(p1) }],
|
||||
[/(\d|\))×/g, '$1'],
|
||||
//[/×(\d|\()/g, '$1'],
|
||||
[/\(([^)(+.\/-]+)\)/g, "$1"],
|
||||
[/integral\((.+),\s?(.+),\s?("|')(.+)("|'),\s?("|')(.+)("|')\)/g, function(match, a, b, p1, body, p2, p3, by, p4) {
|
||||
if(a.length < b.length) {
|
||||
return `∫${textsub(a)}${textsup(b)} ${body} d${by}`
|
||||
} else {
|
||||
return `∫${textsup(b)}${textsub(a)} ${body} d${by}`
|
||||
}
|
||||
}],
|
||||
[/derivative\(?("|')(.+)("|'), ?("|')(.+)("|'), ?(.+)\)?/g, function(match, p1, body, p2, p3, by, p4, x) {
|
||||
return `d(${body.replace(new RegExp(by, 'g'), 'x')})/dx`
|
||||
}]
|
||||
]
|
||||
|
||||
str = simplifyExpression(str)
|
||||
// Replacements
|
||||
for(var replacement of replacements)
|
||||
while(replacement[0].test(str))
|
||||
str = str.replace(replacement[0], replacement[1])
|
||||
return str
|
||||
}
|
||||
|
||||
function parseName(str, removeUnallowed = true) {
|
||||
var replacements = [
|
||||
// Greek letters
|
||||
[/([^a-z]|^)al(pha)?([^a-z]|$)/g, '$1α$3'],
|
||||
[/([^a-z]|^)be(ta)?([^a-z]|$)/g, '$1β$3'],
|
||||
[/([^a-z]|^)ga(mma)?([^a-z]|$)/g, '$1γ$3'],
|
||||
[/([^a-z]|^)de(lta)?([^a-z]|$)/g, '$1δ$3'],
|
||||
[/([^a-z]|^)ep(silon)?([^a-z]|$)/g, '$1ε$3'],
|
||||
[/([^a-z]|^)ze(ta)?([^a-z]|$)/g, '$1ζ$3'],
|
||||
[/([^a-z]|^)et(a)?([^a-z]|$)/g, '$1η$3'],
|
||||
[/([^a-z]|^)th(eta)?([^a-z]|$)/g, '$1θ$3'],
|
||||
[/([^a-z]|^)io(ta)?([^a-z]|$)/g, '$1ι$3'],
|
||||
[/([^a-z]|^)ka(ppa)([^a-z]|$)?/g, '$1κ$3'],
|
||||
[/([^a-z]|^)la(mbda)?([^a-z]|$)/g, '$1λ$3'],
|
||||
[/([^a-z]|^)mu([^a-z]|$)/g, '$1μ$2'],
|
||||
[/([^a-z]|^)nu([^a-z]|$)/g, '$1ν$2'],
|
||||
[/([^a-z]|^)xi([^a-z]|$)/g, '$1ξ$2'],
|
||||
[/([^a-z]|^)rh(o)?([^a-z]|$)/g, '$1ρ$3'],
|
||||
[/([^a-z]|^)si(gma)?([^a-z]|$)/g, '$1σ$3'],
|
||||
[/([^a-z]|^)ta(u)?([^a-z]|$)/g, '$1τ$3'],
|
||||
[/([^a-z]|^)up(silon)?([^a-z]|$)/g, '$1υ$3'],
|
||||
[/([^a-z]|^)ph(i)?([^a-z]|$)/g, '$1φ$3'],
|
||||
[/([^a-z]|^)ch(i)?([^a-z]|$)/g, '$1χ$3'],
|
||||
[/([^a-z]|^)ps(i)?([^a-z]|$)/g, '$1ψ$3'],
|
||||
[/([^a-z]|^)om(ega)?([^a-z]|$)/g, '$1ω$3'],
|
||||
// Capital greek letters
|
||||
[/([^a-z]|^)gga(mma)?([^a-z]|$)/g, '$1Γ$3'],
|
||||
[/([^a-z]|^)gde(lta)?([^a-z]|$)/g, '$1Δ$3'],
|
||||
[/([^a-z]|^)gth(eta)?([^a-z]|$)/g, '$1Θ$3'],
|
||||
[/([^a-z]|^)gla(mbda)?([^a-z]|$)/g, '$1Λ$3'],
|
||||
[/([^a-z]|^)gxi([^a-z]|$)/g, '$1Ξ$2'],
|
||||
[/([^a-z]|^)gpi([^a-z]|$)/g, '$1Π$2'],
|
||||
[/([^a-z]|^)gsi(gma)([^a-z]|$)?/g, '$1Σ$3'],
|
||||
[/([^a-z]|^)gph(i)?([^a-z]|$)/g, '$1Φ$3'],
|
||||
[/([^a-z]|^)gps(i)?([^a-z]|$)/g, '$1Ψ$3'],
|
||||
[/([^a-z]|^)gom(ega)?([^a-z]|$)/g, '$1Ω$3'],
|
||||
// Underscores
|
||||
[/_\(([^_]+)\)/g, function(match, p1) { return textsub(p1) }],
|
||||
[/_([^" ]+)/g, function(match, p1) { return textsub(p1) }],
|
||||
// Array elements
|
||||
[/\[([^\]\[]+)\]/g, function(match, p1) { return textsub(p1) }],
|
||||
// Removing
|
||||
[/[xπℝℕ\\∪∩\]\[ ()^/÷*×+=\d-]/g , ''],
|
||||
]
|
||||
if(!removeUnallowed) replacements.pop()
|
||||
// Replacements
|
||||
for(var replacement of replacements)
|
||||
str = str.replace(replacement[0], replacement[1])
|
||||
return str
|
||||
}
|
||||
|
||||
String.prototype.toLatinUppercase = function() {
|
||||
return this.replace(/[a-z]/g, function(match){return match.toUpperCase()})
|
||||
}
|
||||
|
||||
function camelCase2readable(label) {
|
||||
var parsed = parseName(label, false)
|
||||
return parsed.charAt(0).toLatinUppercase() + parsed.slice(1).replace(/([A-Z])/g," $1")
|
||||
}
|
||||
|
||||
function getRandomColor() {
|
||||
var clrs = '0123456789ABCDEF';
|
||||
var color = '#';
|
||||
for(var i = 0; i < 6; i++) {
|
||||
color += clrs[Math.floor(Math.random() * (16-5*(i%2==0)))];
|
||||
}
|
||||
return color;
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
module eu.ad5001.LogarithmPlotter
|
||||
|
||||
Settings 1.0 Settings.qml
|
||||
Alert 1.0 Alert.qml
|
|
@ -1,161 +0,0 @@
|
|||
"""
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from PySide2.QtWidgets import QMessageBox, QApplication
|
||||
from PySide2.QtCore import QRunnable, QThreadPool, QThread, QObject, Signal, Slot, QCoreApplication
|
||||
from PySide2.QtQml import QQmlApplicationEngine
|
||||
from PySide2.QtGui import QImage
|
||||
from PySide2 import __version__ as PySide2_version
|
||||
|
||||
from os import chdir, path
|
||||
from json import loads
|
||||
from sys import version as sys_version
|
||||
from urllib.request import urlopen
|
||||
from urllib.error import HTTPError, URLError
|
||||
|
||||
from LogarithmPlotter import __VERSION__
|
||||
from LogarithmPlotter.util import config
|
||||
|
||||
class ChangelogFetcher(QRunnable):
|
||||
def __init__(self, helper):
|
||||
QRunnable.__init__(self)
|
||||
self.helper = helper
|
||||
|
||||
def run(self):
|
||||
msg_text = "Unknown changelog error."
|
||||
try:
|
||||
# Fetching version
|
||||
r = urlopen("https://api.ad5001.eu/changelog/logarithmplotter/")
|
||||
lines = r.readlines()
|
||||
r.close()
|
||||
msg_text = "".join(map(lambda x: x.decode('utf-8'), lines)).strip()
|
||||
except HTTPError as e:
|
||||
msg_text = QCoreApplication.translate("changelog","Could not fetch changelog: Server error {}.").format(str(e.code))
|
||||
except URLError as e:
|
||||
msg_text = QCoreApplication.translate("changelog","Could not fetch update: {}.").format(str(e.reason))
|
||||
self.helper.gotChangelog.emit(msg_text)
|
||||
|
||||
class Helper(QObject):
|
||||
changelogFetched = Signal(str)
|
||||
gotChangelog = Signal(str)
|
||||
|
||||
def __init__(self, cwd: str, tmpfile: str):
|
||||
QObject.__init__(self)
|
||||
self.cwd = cwd
|
||||
self.tmpfile = tmpfile
|
||||
self.gotChangelog.connect(self.fetched)
|
||||
|
||||
def fetched(self, changelog: str):
|
||||
self.changelogFetched.emit(changelog)
|
||||
|
||||
@Slot(str, str)
|
||||
def write(self, filename, filedata):
|
||||
chdir(self.cwd)
|
||||
if path.exists(path.dirname(path.realpath(filename))):
|
||||
if filename.split(".")[-1] == "lpf":
|
||||
# Add header to file
|
||||
filedata = "LPFv1" + filedata
|
||||
f = open(path.realpath(filename), 'w', -1, 'utf8')
|
||||
f.write(filedata)
|
||||
f.close()
|
||||
chdir(path.dirname(path.realpath(__file__)))
|
||||
|
||||
@Slot(str, result=str)
|
||||
def load(self, filename):
|
||||
chdir(self.cwd)
|
||||
data = '{}'
|
||||
if path.exists(path.realpath(filename)):
|
||||
f = open(path.realpath(filename), 'r', -1, 'utf8')
|
||||
data = f.read()
|
||||
f.close()
|
||||
try:
|
||||
if data[:5] == "LPFv1":
|
||||
# V1 version of the file
|
||||
data = data[5:]
|
||||
elif data[0] == "{" and "type" in loads(data) and loads(data)["type"] == "logplotv1":
|
||||
pass
|
||||
elif data[:3] == "LPF":
|
||||
# More recent version of LogarithmPlotter file, but incompatible with the current format
|
||||
raise Exception(QCoreApplication.translate("This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}.\nPlease update LogarithmPlotter to open this file.".format(__VERSION__)))
|
||||
else:
|
||||
raise Exception("Invalid LogarithmPlotter file.")
|
||||
except Exception as e: # If file can't be loaded
|
||||
QMessageBox.warning(None, 'LogarithmPlotter', QCoreApplication.translate('main','Could not open file "{}":\n{}').format(filename, e), QMessageBox.Ok) # Cannot parse file
|
||||
else:
|
||||
QMessageBox.warning(None, 'LogarithmPlotter', QCoreApplication.translate('main','Could not open file: "{}"\nFile does not exist.').format(filename), QMessageBox.Ok) # Cannot parse file
|
||||
try:
|
||||
chdir(path.dirname(path.realpath(__file__)))
|
||||
except NotADirectoryError as e:
|
||||
# Triggered on bundled versions of MacOS when it shouldn't. Prevents opening files.
|
||||
# See more at https://git.ad5001.eu/Ad5001/LogarithmPlotter/issues/1
|
||||
pass
|
||||
return data
|
||||
|
||||
@Slot(result=str)
|
||||
def gettmpfile(self):
|
||||
return self.tmpfile
|
||||
|
||||
@Slot()
|
||||
def copyImageToClipboard(self):
|
||||
clipboard = QApplication.clipboard()
|
||||
clipboard.setImage(QImage(self.tmpfile))
|
||||
|
||||
@Slot(result=str)
|
||||
def getVersion(self):
|
||||
return __VERSION__
|
||||
|
||||
@Slot(str, result=str)
|
||||
def getSetting(self, namespace):
|
||||
return config.getSetting(namespace)
|
||||
|
||||
@Slot(str, result=bool)
|
||||
def getSettingBool(self, namespace):
|
||||
return config.getSetting(namespace)
|
||||
|
||||
@Slot(str, str)
|
||||
def setSetting(self, namespace, value):
|
||||
return config.setSetting(namespace, value)
|
||||
|
||||
@Slot(str, bool)
|
||||
def setSettingBool(self, namespace, value):
|
||||
return config.setSetting(namespace, value)
|
||||
|
||||
@Slot(str)
|
||||
def setLanguage(self, new_lang):
|
||||
config.setSetting("language", new_lang)
|
||||
|
||||
@Slot(result=str)
|
||||
def getDebugInfos(self):
|
||||
"""
|
||||
Returns the version info about Qt, PySide2 & Python
|
||||
"""
|
||||
return QCoreApplication.translate('main',"Built with PySide2 (Qt) v{} and python v{}").format(PySide2_version, sys_version.split("\n")[0])
|
||||
|
||||
@Slot()
|
||||
def fetchChangelog(self):
|
||||
changelog_cache_path = path.join(path.dirname(path.realpath(__file__)), "CHANGELOG.md")
|
||||
if path.exists(changelog_cache_path):
|
||||
# We have a cached version of the changelog, for env that don't have access to the internet.
|
||||
f = open(changelog_cache_path);
|
||||
self.changelogFetched.emit("".join(f.readlines()).strip())
|
||||
f.close()
|
||||
else:
|
||||
# Fetch it from the internet.
|
||||
runnable = ChangelogFetcher(self)
|
||||
QThreadPool.globalInstance().start(runnable)
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
"""
|
||||
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||
* Copyright (C) 2022 Ad5001
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from PySide2.QtCore import QObject, Slot
|
||||
from PySide2.QtGui import QImage, QColor
|
||||
from PySide2.QtWidgets import QApplication
|
||||
|
||||
from os import path
|
||||
from sympy import preview
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
class Latex(QObject):
|
||||
def __init__(self, tempdir: str, palette):
|
||||
QObject.__init__(self)
|
||||
self.tempdir = tempdir
|
||||
self.palette = palette
|
||||
fg = self.palette.windowText().color().convertTo(QColor.Rgb)
|
||||
|
||||
@Slot(str, float, QColor, result=str)
|
||||
def render(self, latexstring, font_size, color = True):
|
||||
exprpath = path.join(self.tempdir.name, f'{hash(latexstring)}_{font_size}_{color.rgb()}.png')
|
||||
print("Rendering", latexstring, exprpath)
|
||||
if not path.exists(exprpath):
|
||||
fg = color.convertTo(QColor.Rgb)
|
||||
fg = f'rgb {fg.redF()} {fg.greenF()} {fg.blueF()}'
|
||||
preview('$$' + latexstring + '$$', viewer='file', filename=exprpath, dvioptions=[
|
||||
"-T", "tight",
|
||||
"-z", "0",
|
||||
"--truecolor",
|
||||
f"-D {font_size * 72.27 / 10}", # See https://linux.die.net/man/1/dvipng#-D for convertion
|
||||
"-bg", "Transparent",
|
||||
"-fg", fg],
|
||||
euler=True)
|
||||
img = QImage(exprpath);
|
||||
# Small hack, not very optimized since we load the image twice, but you can't pass a QImage to QML and expect it to be loaded
|
||||
return f'{exprpath},{img.width()},{img.height()}'
|
||||
|
||||
@Slot(str)
|
||||
def copyLatexImageToClipboard(self, latexstring):
|
||||
global tempfile
|
||||
clipboard = QApplication.clipboard()
|
||||
clipboard.setImage(self.render(latexstring))
|
97
README.md
|
@ -1,4 +1,5 @@
|
|||
# ![icon](https://git.ad5001.eu/Ad5001/LogarithmPlotter/raw/branch/master/logplotter.svg) LogarithmPlotter
|
||||
# ![icon](https://apps.ad5001.eu/icons/apps/svg/logarithmplotter.svg) LogarithmPlotter
|
||||
|
||||
[![Build Status](https://ci.ad5001.eu/api/badges/Ad5001/LogarithmPlotter/status.svg)](https://ci.ad5001.eu/Ad5001/LogarithmPlotter)
|
||||
[![Translation status](https://hosted.weblate.org/widgets/logarithmplotter/-/logarithmplotter/svg-badge.svg)](https://hosted.weblate.org/engage/logarithmplotter/)
|
||||
[![On flathub](https://img.shields.io/flathub/v/eu.ad5001.LogarithmPlotter?label=on%20flathub&logo=Flathub&logoColor=white&color=4A86CF)](https://flathub.org/apps/details/eu.ad5001.LogarithmPlotter)
|
||||
|
@ -7,56 +8,79 @@
|
|||
2D plotter software to make Bode plots, sequences and distribution functions.
|
||||
|
||||
## Screenshots
|
||||
|
||||
![Magnitude example](https://apps.ad5001.eu/img/full/logarithmplotter.png)
|
||||
![Phase example](https://apps.ad5001.eu/img/en/logarithmplotter/phase.png)
|
||||
![Object settings](https://apps.ad5001.eu/img/en/logarithmplotter/object-settings.webp)
|
||||
|
||||
You can find more screenshots on the [app website](https://apps.ad5001.eu/logarithmplotter/).
|
||||
You can find more screenshots on the [app's website](https://apps.ad5001.eu/logarithmplotter/).
|
||||
|
||||
## Run
|
||||
## Build & Run
|
||||
|
||||
You can simply run LogarithmPlotter using `python3 run.py`.
|
||||
First, you'll need to install all the required dependencies:
|
||||
|
||||
In order to test translations, you can use the `--lang=<lang code>` command line option to force the detected locale of LogarithmPlotter.
|
||||
- [Python 3](https://python.org) with [poetry](https://python-poetry.org/), setup a virtual environment, go to the `runtime-pyside6` directory, and call
|
||||
`poetry install`.
|
||||
- [npm](https://npmjs.com) (or [yarn](https://yarnpkg.com/)), go to the `common` directory, and run `npm install` (or `yarn install`).
|
||||
|
||||
You can simply run LogarithmPlotter using `python3 run.py`. It automatically compiles the language files (requires
|
||||
`pyside6-lrelease` to be installed and in path), and the JavaScript modules.
|
||||
|
||||
If you do not wish do recompile the files again on every run, you can use the build script (`scripts/build.sh`) and run
|
||||
`python3 build/runtime-pyside6/LogarithmPlotter/logarithmplotter.py`.
|
||||
|
||||
In order to test translations, you can use the `--lang=<lang code>` commandline option to force the locale.
|
||||
|
||||
## Install
|
||||
|
||||
### Generate installers:
|
||||
|
||||
All scripts noted here can be found in the `scripts` directory.
|
||||
|
||||
You can generate installers from LogarithmPlotter after installing all the dependencies:
|
||||
For all builds, you need [Python 3](https://python.org) with [PySide2](https://pypi.org/project/PySide2/) installable with `pip install PySide2`.
|
||||
- Windows installer:
|
||||
- You need `pyinstaller`. You can install it using `pip install pyinstaller`.
|
||||
- Run the `build-windows.bat` script (or `build-wine.sh` if you're cross-compiling with wine on Linux) to build an exe for LogarithmPlotter.
|
||||
- You also need [NSIS](https://nsis.sourceforge.io/Main_Page) (Linux users can install the [nsis](https://pkgs.org/download/nsis) package).
|
||||
- Run the `package-windows.bat` script (or `package-wine.sh`if you're cross-compiling on Linux). You will find a logarithmplotter-setup.exe installer in the dist/accountfree/ folder.
|
||||
You can generate installers for LogarithmPlotter after installing all the dependencies.
|
||||
|
||||
- Windows installer (crosscompiling from Linux):
|
||||
- Run `build-wine.sh` (requires wine) to build an exe for LogarithmPlotter in build/runtime-pyside6/dist.
|
||||
- You also need [NSIS](https://nsis.sourceforge.io/Main_Page) (the [nsis](https://pkgs.org/download/nsis) package is available on linux).
|
||||
- Run the `package-wine.sh` script. You will find a logarithmplotter-setup.exe installer in the build/runtime-pyside6/dist/logarithmplotter/ folder.
|
||||
- MacOS Archive creator installer:
|
||||
- You need `pyinstaller`. You can install it using `pip install pyinstaller`.
|
||||
- Run the `build-macosx.sh` script to build an .app for LogarithmPlotter which can be found in the dist directory.
|
||||
- Run the `package-macosx.sh` script. You will find a LogarithmPlotter-v0.1-dev-setup.dmg installer in the dist/ folder.
|
||||
- Run the `build-macosx.sh` script to build an .app for LogarithmPlotter which can be found in the build/runtime-pyside6/dist directory.
|
||||
- Run the `package-macosx.sh` script. You will find a LogarithmPlotter-v<version>-setup.dmg installer in the
|
||||
build/runtime-pyside6/build/pysdist/ folder.
|
||||
- Linux packages:
|
||||
- To build a DEB, you need DPKG and stdeb. You can install the later by using `pip install stdeb`.
|
||||
- To build and install the flatpak, you need [flatpak-builder](https://docs.flatpak.org/en/latest/flatpak-builder.html) installed.
|
||||
- To build the snap, you need [snapcraft](https://snapcraft.io) installed.
|
||||
- Run `package-linux.sh`.
|
||||
|
||||
|
||||
### Linux
|
||||
|
||||
Run `bash linux/install_local.sh`
|
||||
- Run `package-deb.sh`. It will create an DSC and a DEB in build/runtime-pyside6/deb_dist/
|
||||
- Run `scripts/build.sh` followed by `snapcraft`. It .snap file in the root directory.
|
||||
- See [the flatpak repo](https://github.com/Ad5001/eu.ad5001.LogarithmPlotter) for instrutions on how to build the flatpak.
|
||||
|
||||
## Contribute
|
||||
|
||||
There are several ways to contribute to LogarithmPlotter.
|
||||
There are several ways you can contribute to LogarithmPlotter.
|
||||
|
||||
- You can help to translate [the project on Hosted Weblate](https://hosted.weblate.org/engage/logarithmplotter/):
|
||||
[![Translation status](https://hosted.weblate.org/widgets/logarithmplotter/-/logarithmplotter/multi-auto.svg)](https://hosted.weblate.org/engage/logarithmplotter/)
|
||||
|
||||
- You can help the development of LogarithmPlotter. In order to get started, take a look at the [wiki](https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/_pages).
|
||||
- You can help the development of LogarithmPlotter. In order to get started, take a look at
|
||||
the [wiki](https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/_pages).
|
||||
|
||||
## Tests
|
||||
|
||||
To run LogarithmPlotter's tests, follow these steps:
|
||||
|
||||
- Python
|
||||
- Install python3 and [poetry](https://python-poetry.org/)
|
||||
- Create and activate virtual env (recommended)
|
||||
- Go into `runtime-pyside6` and run `poetry install --with test`
|
||||
- ECMAScript
|
||||
- Install node with npm
|
||||
- Go into `common` and run `npm install -D`
|
||||
|
||||
Finally, to actually run the tests:
|
||||
- Run `scripts/run-tests.sh`
|
||||
|
||||
## Legal notice
|
||||
LogarithmPlotter - 2D plotter software to make BODE plots, sequences and repartition functions.
|
||||
Copyright (C) 2022 Ad5001 <mail@ad5001.eu>
|
||||
|
||||
LogarithmPlotter - 2D plotter software to make Bode plots, sequences and repartition functions.
|
||||
Copyright (C) 2021-2024 Ad5001 <mail@ad5001.eu>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -71,6 +95,19 @@ There are several ways to contribute to LogarithmPlotter.
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Language files translations located at LogarithmPlotter/i18n are licensed under GNU GPL3.0+ and are copyrighted by their original authors. See LICENSE.md for more details:
|
||||
- 🇳🇴 Norwegian translation by [Allan Nordhøy](https://github.com/comradekingu)
|
||||
See LICENSE.md for more details. Language files translations located at assets/i18n are licensed under GNU GPL3.0+ and
|
||||
are copyrighted by their original authors:
|
||||
|
||||
- 🇭🇺 Hungarian translation by [Óvári](https://github.com/ovari)
|
||||
- 🇳🇴 Norwegian translation by [Allan Nordhøy](https://github.com/comradekingu)
|
||||
- 🇪🇸 Spanish translation by gallegonovato and [IngrownMink4](https://github.com/IngrownMink4)
|
||||
|
||||
### Libraries used
|
||||
|
||||
LogarithmPlotter includes [expr-eval](https://github.com/silentmatt/expr-eval) a port
|
||||
of [ndef.parser](https://web.archive.org/web/20111023001618/http://www.undefined.ch/mparser/index.html) by Raphael Graf
|
||||
<r@undefined.ch>, ported to javascript by Matthew Crumley
|
||||
<email@matthewcrumley.com> (http://silentmatt.com/), and then to QMLJS by Ad5001.
|
||||
|
||||
All files in (common/src/lib/expr-eval/) except integration.mjs are licensed
|
||||
under the [MIT License](https://raw.githubusercontent.com/silentmatt/expr-eval/master/LICENSE.txt).
|
||||
|
|
1897
assets/i18n/lp_de.ts
Normal file
1897
assets/i18n/lp_en.ts
Normal file
1889
assets/i18n/lp_es.ts
Normal file
1900
assets/i18n/lp_fr.ts
Normal file
1897
assets/i18n/lp_hu.ts
Normal file
1800
assets/i18n/lp_nb_NO.ts
Normal file
1563
assets/i18n/lp_template.ts
Normal file
2
assets/i18n/release.sh
Executable file
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
pyside6-lrelease *.ts
|
65
assets/i18n/update.sh
Executable file
|
@ -0,0 +1,65 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# This file automatically renames .mjs files to js, and (tries) to fix most common ECMAScript
|
||||
# specificities so that lupdate doesn't cry out in pain.
|
||||
# See also: https://bugreports.qt.io/browse/QTBUG-123819
|
||||
#
|
||||
|
||||
escape() {
|
||||
str="$1"
|
||||
str="${str//\//\\/}" # Escape slashes
|
||||
str="${str//\*/\\*}" # Escape asterixes
|
||||
echo "$str"
|
||||
}
|
||||
|
||||
replace() {
|
||||
file="$1"
|
||||
from="$(escape "$2")"
|
||||
to="$(escape "$3")"
|
||||
sed -i "s/${from}/${to}/g" "$file"
|
||||
}
|
||||
|
||||
rm ../qml/eu/ad5001/LogarithmPlotter/js/index.mjs # Remove index which should not be scanned
|
||||
|
||||
files=$(find ../../common/src -name '*.mjs')
|
||||
for file in $files; do
|
||||
echo "Moving '$file' to '${file%.*}.js'..."
|
||||
mv "$file" "${file%.*}.js"
|
||||
# Replacements to make it valid js
|
||||
replace "${file%.*}.js" "^import" "/*import"
|
||||
replace "${file%.*}.js" "^export *" "/*export *"
|
||||
replace "${file%.*}.js" '.mjs"$' '.mjs"*/'
|
||||
replace "${file%.*}.js" "^export default" "/*export default*/"
|
||||
replace "${file%.*}.js" "^export" "/*export*/"
|
||||
replace "${file%.*}.js" "async " "/*async */"
|
||||
replace "${file%.*}.js" "await" "/*await */"
|
||||
replace "${file%.*}.js" " #" "// #"
|
||||
replace "${file%.*}.js" "this.#" "/*this.#*/"
|
||||
done
|
||||
|
||||
echo "----------------------------"
|
||||
echo "| Updating translations... |"
|
||||
echo "----------------------------"
|
||||
pyside6-lupdate -extensions js,qs,qml,py -recursive ../../common/src -recursive ../../runtime-pyside6/LogarithmPlotter -ts lp_*.ts
|
||||
# Updating locations in files
|
||||
for lp in *.ts; do
|
||||
echo "Replacing locations in $lp..."
|
||||
for file in $files; do
|
||||
replace "$lp" "${file%.*}.js" "$file"
|
||||
done
|
||||
done
|
||||
|
||||
for file in $files; do
|
||||
echo "Moving '${file%.*}.js' to '$file'..."
|
||||
mv "${file%.*}.js" "$file"
|
||||
# Resetting changes
|
||||
replace "$file" "/*await */" "await"
|
||||
replace "$file" "/*async */" "async "
|
||||
replace "$file" "^/*export*/" "export"
|
||||
replace "$file" "^/*export default*/" "export default"
|
||||
replace "$file" '.mjs"*/' '.mjs"'
|
||||
replace "$file" "^/*import" "import"
|
||||
replace "$file" "^/*export" "export"
|
||||
replace "$file" "// #" " #"
|
||||
replace "$file" "/*this.#*/" "this.#"
|
||||
done
|
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 198 B |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 289 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 620 B After Width: | Height: | Size: 620 B |
Before Width: | Height: | Size: 261 B After Width: | Height: | Size: 261 B |
1
assets/icons/common/manual.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd"><path d="M14 0v10l2-1.518 2 1.518v-10h4v24h-17c-1.657 0-3-1.343-3-3v-18c0-1.657 1.343-3 3-3h9zm6 20h-14.505c-1.375 0-1.375 2 0 2h14.505v-2z"/></svg>
|
After Width: | Height: | Size: 251 B |
1
assets/icons/common/new.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd"><path d="M12 0l-2.138 2.63-3.068-1.441-.787 3.297-3.389.032.722 3.312-3.039 1.5 2.088 2.671-2.088 2.67 3.039 1.499-.722 3.312 3.389.033.787 3.296 3.068-1.441 2.138 2.63 2.139-2.63 3.068 1.441.786-3.296 3.39-.033-.722-3.312 3.038-1.499-2.087-2.67 2.087-2.671-3.038-1.5.722-3.312-3.39-.032-.786-3.297-3.068 1.441-2.139-2.63zm0 15.5c.69 0 1.25.56 1.25 1.25s-.56 1.25-1.25 1.25-1.25-.56-1.25-1.25.56-1.25 1.25-1.25zm1-1.038v-7.462h-2v7.462h2z"/></svg>
|
After Width: | Height: | Size: 550 B |
Before Width: | Height: | Size: 273 B After Width: | Height: | Size: 273 B |
1
assets/icons/common/settings.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M24 13.616v-3.232l-2.869-1.02c-.198-.687-.472-1.342-.811-1.955l1.308-2.751-2.285-2.285-2.751 1.307c-.613-.339-1.269-.613-1.955-.811l-1.021-2.869h-3.232l-1.021 2.869c-.686.198-1.342.471-1.955.811l-2.751-1.308-2.285 2.285 1.308 2.752c-.339.613-.614 1.268-.811 1.955l-2.869 1.02v3.232l2.869 1.02c.197.687.472 1.342.811 1.955l-1.308 2.751 2.285 2.286 2.751-1.308c.613.339 1.269.613 1.955.811l1.021 2.869h3.232l1.021-2.869c.687-.198 1.342-.472 1.955-.811l2.751 1.308 2.285-2.286-1.308-2.751c.339-.613.613-1.268.811-1.955l2.869-1.02zm-12 2.384c-2.209 0-4-1.791-4-4s1.791-4 4-4 4 1.791 4 4-1.791 4-4 4z"/></svg>
|
After Width: | Height: | Size: 696 B |
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 270 B |
Before Width: | Height: | Size: 370 B After Width: | Height: | Size: 370 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
1
assets/icons/history/delete.svg
Symbolic link
|
@ -0,0 +1 @@
|
|||
../common/close.svg
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |