doctordalek1963 / lintrans Goto Github PK
View Code? Open in Web Editor NEWlintrans is a tool to visualize linear transformations in 2D.
Home Page: https://DoctorDalek1963.github.io/lintrans
License: GNU General Public License v3.0
lintrans is a tool to visualize linear transformations in 2D.
Home Page: https://DoctorDalek1963.github.io/lintrans
License: GNU General Public License v3.0
The session file currently saves the MatrixWrapper
and the polygon, but it should also save:
No response
No response
Display settings should be saved in a dedicated file when the app is closed, and loaded from that file (if it exists) when the app is opened. This would allow the display settings to persist across sessions.
These persistent display settings would be lower priority than the display settings saved in the session file (see #37), so if lintrans is used to open a session file directly, the settings in that file take priority. The session file display settings will also overwrite the current display settings when a session is loaded. If the app is closed with a session file loaded, those display settings get saved to the session file and the global persistence file.
No response
No response
With the way the transformed grid lines are currently drawn, we often end up with grid lines that should be drawn, not getting drawn. This means that we get some grid lines in the centre, but they don't continue out far enough and the corners look pretty sparse. I'm not entirely sure how, but I need to draw all the possible grid lines within the canvas.
I think I could use a technique like this. Use y = mx + c
and find m for the grid lines. Check if this line intersects the line y = max_y
between -max_x
and max_x
. If it does, then draw as many parallel grid lines as possible. For a mostly vertical grid line to fit within the canvas, it must intersect y = max_y
or y = -max_y
between -max_x
and max_x
, so we continue to increment multiples of c until we no longer meet this requirement.
A very similar technique can be used if the grid line doesn't intersect y = max_y
between the limits. Instead, we look at intersections with x = max_x
or x = -max_x
.
When trying to render a matrix, the canvas coords are stored as 32 bit integers. This means that internal overflow errors can occur if a matrix is too big.
The display settings should have an animation speed setting. This number would be used in animating transformations in LintransMainWindow
The app should have a global settings dialog, available under the File
option in the menubar. These settings should include where to save the logs, the logging level, whether to show the "Would you like to read the tutorial?" pop-up at the start, etc.
If a matrix is defined directly in terms of itself, you're not allowed to confirm it. It just won't let you. But if it's indirectly referencing itself, then you can confirm the definition, and you will then have a pair of matrices that form a reference loop. Trying to even enter either of these into the expression input box will crash the program as it tries to see if it's a valid expression.
CRASH REPORT at 2022-08-20 12:09:23
SYSTEM INFO:
lintrans: 0.3.0-alpha
Python: 3.10.6
Qt5: 5.15.2
PyQt5: 5.15.6
Platform: Linux-5.19.0-2.1-liquorix-amd64-x86_64-with-glibc2.31
CRASH ORIGIN:
Exception "maximum recursion depth exceeded in __instancecheck__"
of type RecursionError in call to _compile()
on line 290 of /home/dyson/.local/lib/python3.10/re.py
POST MORTEM:
Matrix wrapper:
A: "B"
B: "2A"
I: [1.0 0.0; 0.0 1.0]
Expression box: "A"
Currently displayed: [1.0 0.0; 0.0 1.0]
Animating (sequence): False (False)
Grid spacing: 85
Window size: 1000 x 750
Viewport size: 782 x 650
Grid corner: (4.6, 3.823529411764706)
Display settings:
draw_background_grid: True
draw_transformed_grid: True
draw_basis_vectors: True
label_basis_vectors: False
smoothen_determinant: True
applicative_animation: True
animation_time: 1200
animation_pause_length: 400
draw_determinant_parallelogram: False
show_determinant_value: True
draw_eigenvectors: False
draw_eigenlines: False
draw_untransformed_polygon: True
draw_transformed_polygon: True
No response
Currently, the I/O vectors can be toggled on and off, but only as a pair, not independently. I think it would be beneficial for these vectors to be toggleable independently in the display settings.
Teachers could show the input vector and ask their students to find the output vector, using visual inspection and matrix multiplication, and then show the output vector to verify their answers.
No response
Currently, all animation moves from the identity to the matrix in the expression box. I propose adding a new method to LintransMainWindow
, something like animate_between_matrices(self, start: MatrixType, end: MatrixType) -> None
which would animate from start
to end
. This would require the introduction of new coord transformation methods in VectorGridPlot
to translate coords in the transformed vector space to the canvas space.
Animations should also start from the currently displayed transformation by default, so animating M
and then animating M
again would be equivalent to rendering M^2
.
Additionally, the expression box should support syntax like A,B
, which would animate transformation B
, and then transformation A
. Pressing render would simply render AB
, but pressing animate will do it in stages.
Python has an extensive stdlib logging
module, which I could definitely use. We could log matrix evaluations and rendering information with different logging levels. The user would have the option to direct this log to a file or stdout, and adjust the logging level (info by default).
As referenced in #12, animation should support comma syntax in the input bar. Animating A,B
would be equivalent to animating to B
, waiting for a moment, then animating to AB
.
Likewise, something like A+2rot(45),B^2,3C
would be animated in stages as 3C
, then (B^{2})(3C)
, then (A+2rot(45))(B^{2})(3C)
, so each section gets multiplied together each time.
When a comma-syntax expression is in the input box, the animation button will be enabled, but the standard render button will be disabled.
The Create polygon
button is currently disabled, but the user should be able to define a custom polygon by just clicking to define points, and then see how it gets transformed by any matrix expression.
When using lintrans on a laptop with a trackpad, scrolling can be an issue when the sensitivity of the trackpad is low, meaning that it's hard to zoom with much precision.
I think an option in the global settings could remedy this issue by allowing zoom sensitivity to be adjusted.
No response
No response
Currently, when a matrix is defined in terms of an expression, that expression is immediately evaluated and the new matrix has that resultant value.
Say we define matrix A as 2B^2
. If the value of B changes, then A does not. It retains the value of evaluation at definition time. I propose that instead, A is defined as the expression itself, which is re-evaluated whenever it's needed. So if B changes, then A is re-evaluated the next time it's used.
Matrix expression should be allowed to parenthesise other matrix expressions inside themselves. For example, 2(AB)^2
would be equivalent to 2 A^{2} B^{2}
, and (2A)^{2} 3B
would be the same as 4A^{2} 3B
. This would also allow for things like (A^T)^2
, which are currently impossible.
Validating and evaluating these expressions would likely be recursive, and parsing them presents a considerable challenge using the current parsing method.
I think that there should be the ability to "undefine" matrices. It's all well and good to overwrite them, but you sometimes want them gone completely. This would be as simple as wrapper['M'] = None
on the backend. In the GUI, there should be a button next to each matrix in the InfoPanelDialog
to "undefine" it.
No response
No response
The parser fails to correctly parse expressions with anonymous matrices in them. It can parse anonymous matrices on their own, or as part of an expression if they're wrapped in parentheses, but won't continue to parse after an anonymous matrix if it occurs outside of its own parentheses. That means that something like [-1 0; 0 1]^2
doesn't actually square the matrix.
Here's a table of expressions:
expression | parse list |
---|---|
3[-1 0; 0 1]^2+A^-1-3M |
[[('3', '[-1 0;0 1]', '')]] |
3([-1 0; 0 1])^2+A^-1-3M |
[[('3', '[-1 0;0 1]', '2')], [('', 'A', '-1')], [('-3', 'M', '')]] |
A^-1-3M+3[-1 0; 0 1]^2 |
[[('', 'A', '-1')], [('-3', 'M', '')], [('3', '[-1 0;0 1]', '')]] |
A^-1-3M+3([-1 0; 0 1])^2 |
[[('', 'A', '-1')], [('-3', 'M', '')], [('3', '[-1 0;0 1]', '2')]] |
No response
No response
On GNOME with X11, lintrans cannot be put at the side of the screen (Super+LeftArrow or Super+RightArrow). I can press the key combination but nothing happens. It works fine with other apps. I have also heard that on Windows, lintrans takes up more than half of the screen, and the expression box falls off the bottom.
I think this is because the minimum width is 1000 pixels, which is more than half of the standard HD screen width of 1920 pixels. Reducing the minimum size (but maybe keeping 1000x750 as the default) should hopefully fix this.
N/A
No response
animated 6 times in a row
D every time
CRASH REPORT at 2022-10-03 10:12:29
SYSTEM INFO:
lintrans: 0.4.0-alpha
Python: 3.10.4
Qt5: 5.15.2
PyQt5: 5.15.6
Platform: Linux-5.15.0-48-generic-x86_64-with-glibc2.31
CRASH ORIGIN:
Exception "argument 1 overflowed: value must be in the range -2147483648 to 2147483647"
of type OverflowError in call to _draw_determinant_text()
on line 637 of /home/dyson/repos/lintrans/src/lintrans/gui/plots/classes.py
POST MORTEM:
Matrix wrapper:
A: [1.0 1.0; 0.0 2.0]
B: [1.0 2.0; 0.0 2.0]
C: [1.0 -1.0; -1.0 -1.0]
D: [2.0 -9.0; 3.0 7.0]
I: [1.0 0.0; 0.0 1.0]
Expression box: "D"
Currently displayed: [-5798404.641761554 -58024780.49779437; 19341593.49926479 26437584.523679763]
Animating (sequence): True (False)
Grid spacing: 35
Window size: 1920 x 1018
Viewport size: 1702 x 918
Grid corner: (24.314285714285713, 13.114285714285714)
Display settings:
draw_background_grid: True
draw_transformed_grid: False
draw_basis_vectors: True
label_basis_vectors: False
smoothen_determinant: True
applicative_animation: True
animation_time: 1200
animation_pause_length: 400
draw_determinant_parallelogram: True
show_determinant_value: True
draw_eigenvectors: False
draw_eigenlines: False
draw_untransformed_polygon: True
draw_transformed_polygon: True
draw_input_vector: True
draw_output_vector: True
No response
To reproduce this bug, and compare "(0.01I)^2" with "(0.01I)^2rot(0.1)" and see that when the basis vectors are horizontal and vertical, way too many lines are drawn. This causes severe lag, especially when zoomed out.
I'm pretty sure this bug is in _draw_parallel_lines()
, although I'm not sure exactly where.
Currently, the mouse cursor has to be less than 5 pixels from the basis vector tip or polygon point that the user is trying to drag. It also has to be less than 0.1 grid units away from an integer coordinate to snap to it. These numbers should both be higher.
I think the best way to handle this would be using an option in the global settings. The user could configure both distances independently and perhaps even toggle snapping to integer coordinates. This would require creating a proper GUI for the global settings, but the require infrastructure is already in place for that. It would also mean integrating the automatic update type into this GUI and removing the config file (which was meant to be temporary anyway).
Most teachers and students will be using lintrans on a laptop. Using the trackpad precisely can be quite finicky, so a larger radius around draggable points and snappable integer coordinates would be useful.
No response
As far as in aware, the only reason we don't support 3.9 is because of the TypeGuard
in src/lintrans/typing_/__init__.py
. Since this is just a typing thing for mypy, could we conditionally import it or maybe extract it out to a .pyi file to allow the program to run with Python 3.9? Type checks would be done with GitHub Actions with the most recent version of Python, but the actual program would run on multiple versions. All these versions would get unit tested.
When installing lintrans
with pip install .
, it installs fine, but then importing it or running python -m lintrans
will result in a circular import error. I don't know why this happens, but it's fine if you use editable mode and do pip install -e .
, which is what I use and what the compile guides use anyway.
This isn't a problem, I'm just logging it with an issue for completeness.
When rendering a matrix with determinant 0, the program crashes, complaining that it can't convert infinity to integer when trying to draw the grid lines. When animating, it crashes near the end of the animation, as everything collapses into a lower dimension. When we're rendering a matrix with determinant 0, we shouldn't draw the grid lines.
There's a button called Change display settings
which is currently disabled. I intend for this button to open a dialog with checkboxes. This dialog will return a DisplaySettings
dataclass object, which will be an attribute of the plot. Then the rendering methods can respect the settings in this object when rendering. Having it as one object would also making saving easier, as we can just have this as an attribute on the SaveObject
.
Proposed display settings:
I need to implement defining matrices visually. This is a key part of the app and I think I've got most of the groundwork done in terms of classes and structures. I just need to create the DefineVisuallyDialog
class and implement dragging the basis vectors.
Office software like Microsoft Word will have a titlebar like *Untitled - Word
and will prompt the user to choose a file and save their work before exiting. lintrans does this, but only if the user has already chosen a file to save to. I propose that they should be prompted even if they haven't chosen a file.
When you've been using lintrans for a while and you've defined several matrices, it's common to want to save them. However, lintrans doesn't currently prompt the user, making it very easy to accidentally lose that work.
No response
Currently, rank 1 matrices of the form [a 0; b 0] or [0 a; 0 b] (a column of 0s) will render the transformed axes. This should not be the case. ALL rank 1 matrices should ONLY render their output column space line.
I think that the background axes should always be visible, but the background and transformed grids (arguably the basis vectors as well) should be toggle-able in the display settings. This will reduce clutter when trying to view things like eigenlines.
Additionally, as a small point here, the order in which we draw things is important, since each thing gets drawn on top of the next. We should be drawing the background grid first, and the transformed grid last.
Currently, when animating a 180° rotation or a reflection, there will be a pause and then the basis vectors will jump to their new positions. This is because we're trying to keep the determinant constant. And for the same reason, rotations close to 180° (roughly 160° - 200°) speed up in the middle of their animation because the components of the basis vectors have to travel significantly different distances but the determinant lock keeps them on the unit circle.
We should be able to save and load sessions. A session would consist of the matrix_wrapper object and the expression currently in the input box. A SaveObject
would be created, pickled, and saved to a binary file. We could then load this binary save file at a different time.
This SaveObject
would be a simple dataclass containing things like the current expression, matrix wrapper, and display settings.
VisualizeTransformationWidget
should draw arrowheads at the tips of the basis vectors. These arrowheads should be two lines, each 45° from the line connecting the tip of the vector to the origin.
When animating a matrix with very large elements, taking the determinant raises an error from NumPy. This results in a program crash. This is easily seen when animating from a det 0 matrix to a very high det matrix, but can also be seen when animating from I
. We'll need a try/except clause somewhere to show an error message when this happens, but I'm not completely sure where.
The 32-bit integer limit of Qt is relevant for all calls to a draw method on a QPainter
object. However, LintransMainWindow._is_matrix_too_big()
only checks the coordinates of the basis vectors. Problems occur when we end up animating towards a large matrix. I'm not entirely sure what the problem is, but we end up trying to draw a line that's way out of bounds for the canvas in _draw_parallel_lines()
.
To reproduce this bug, try to animate "(999I)^2".
To fix this, I propose a signal emitted by VisualizeTransformationWidget
and a slot on LintransMainWindow
. The signal would be emitted when the matrix was too big for the canvas, and then slot would stop animation and create the warning dialog box.
Additionally, the canvas coords methods should probably be bounded.
When using transitional animation and commas in the expression, the plot won't reset if the target matrix is the same as the start matrix.
A,A,A
No response
No response
When defining a matrix, the dropdown should default to the first matrix which has not been previously defined. If all matrices already have a definition, then use A
.
It is a common problem that a user will accidentally overwrite A
instead of defining a new matrix, because they didn't remember to choose a new name.
This would also solve the issue of having to remember which names you'd already used, since the default name would be undefined.
No response
There should be a history of expressions that the user has evaluated. They could scroll through them with the up and down arrow keys, much like a shell prompt. This would make re-using expressions much easier.
The user should be able to zoom in and out when using the scroll wheel over the canvas. This might be as simple as adjusting the grid_spacing
, but there are likely other things to adjust. The reset button should also reset the zoom level.
There should be the option to have a vector in the main transformation view which the user can drag around and see a corresponding output vector. So if we're visualizing the transformation defined by M
, then the user can drag around the vector v
, which is defined in terms of background grid coords, and then we can also show the output vector Mv
. Similar to this old Desmos prototype: https://www.desmos.com/calculator/dpcgriuq6v
The app should support full light and dark themes. We can use the darkdetect
module to detect dark mode independent of OS, but we need styles and/or palettes for light and dark mode. The default Qt style is called Fusion, although there's no official dark mode variant. I'll probably find someone else's implementation and tweak it for lintrans.
I'm not entirely sure how I'm going to handle colours for vectors etc. Maybe that's handled with palettes? I'll have to do more research.
When animating a transformation, we're currently ignoring the determinant. Ideally, we would smoothly animate between determinant of 1, to the determinant of the matrix. This would mean that the animation looks significantly smoother.
I think we can do this by multiplying matrix_move
by some number, which starts as 1 / det
, but increases up to 1. Possibly not, I'm not sure.
Matrix expressions should support simple MATLAB/Octave-style matrices. These would not be assigned to a name, and would thus be anonymous.
The syntax for these matrices is like so (the space after the semicolon is optional):
[1 2; 3 4]
[2.3 -5.2; 10 -1]
They could be used like any other matrix:
2[3 -2.1; 7 0.1]^2
(3rot(-45)A^T[-6.43 -1; 3 2.9])^-1
This should just be a case of updating the parser.
See title. They would not be interactive, and should be faded-out and/or partially transparent to cue this visually.
This feature would allow the user to easily see where their given input vector would get mapped by a matrix while they're defining it.
No response
Currently, while an expression is animating, the user cannot scroll or resize the window. If you scroll, then the change in zoom will only take effect after the animation is complete. Resizing the window simply isn't possible.
I don't know how to allow for handling these events while we're animating. Most of the time spent in the animate_expression
method is in time.sleep(0.01)
. Perhaps if this was asynchronous, the Qt event loop could handle QWheelEvent
s and QResizeEvent
s while we're animating.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.