How to increase painting performance in Java Swing - java

I am currently working on a complex project and struggle with increasing the performance, because I ran out of ideas.
So this is what my program looks like at the moment:
It is a Graph, that shows the path of an object, starting at the green node and ending at the red node. The container is a JPanel, which holds another JPanel at the top right corner, containing the buttons, a JLabel at the top left corner for the status and the different Nodes itself of course. The Nodes and lines are JPanels as well.
So what exactly is the problem?
The Graph contains lots of functionalities. Zooming, DnD and adding end deleting new nodes.
All of these functionalities need the container to repaint, especially DnD needs this very often. So this is just an example with 4 nodes. everything works fine. But at 1000+ nodes and 999 lines between them, it really gets slow. The graph should be able to work with ~20K nodes without having big troubles.
Everytime you move a Node 1px with the mouse in any direction, the whole container gets repainted.
Creating a minimal reproducable example is not quiet possible, because the code for this application is long and there are a lot of dependencies, that would make it not "minimal".
The ideas I have to increase performance are:
Reducing the amount of Nodes (and so the amount of lines), so that less components need to be repainted. This is quiet hard, most nodes are not in a straight line, means there are not many ways to do this. One option would be to reduce the Nodes and increase the level of detail with the zoom function.
Clipping the area, that has to be repainted. The problem with this approach is, that I don't know how exactly this clipping function works or whether it is even possible.
The last idea of mine would be, to somehow create a JLayer object containing (total amount of nodes / 100) layers, that are all visible and only repainting the layer, that contains the specific Node. This seems very unstructured, I don't really think that this is an option, just an idea.
What am I asking for right now?:
How do you manage so much components that have to be repainted? There are definitly ways and I am open to try them all. I just want you to give me some advice, hints on useful built in libraries, that I maybe haven't found in my research. And if one of my ideas could be the one I should go for.
I am not asking for concrete code examples, just for your experience.
Thank you in advance and I'm sorry if my question does not fit the SO conventions.

Related

Why can't GridLayout allow components to be changed by row and column?

This is perhaps more of a whine than a question, and I'm aware there's a workaround to the problem described in this post. But I have a situation where I have to add components to a GridLayout, possibly enlarging the grid as I go; but the components don't get created and inserted in a nice neat order; and I don't know when I'm done being handed components to insert into the grid. In other words, in a 3x3 grid I might get handed a component to put at (0,2) and then another at (1,0) and then one at (0,0). And then I might get one at (5,2) and need to enlarge the grid. And then I might get told to replace the line at (0,0) with something else.
I understand that there's no way to say "put a component at x, y". I get that I'm going to have to build a 2D array to hold my components and then empty and refill the grid from the array, each time it changes, which is going to be quite often in several large grids, so I'd rather not. I get that life is like that and the language is the language and who am I to question why.
But I'm really curious. This seems like the most basic of operations for a grid to support. Not having it feels like I'm working with a spreadsheet that only lets you enter values in order from left to right - it's simple madness.
I haven't taken apart the source code for GridLayout, but any naive understanding of how it "must work" makes me think add(component, row, col) should be trivial to implement. It must not be, because GridLayout isn't exactly new and I can't believe I'm the first to think random access to a grid is a good idea. So it must be really hard. But why?
I realize understanding why the internals of GridLayout can't support this, doesn't solve any real problems, except the problem of me unable to stop thinking about how fundamentally weird the restriction is. In the end I'm going to end up with a parallel array, or messing with the more complex GridBagLayout, unless someone knows a better way. But I just want to know why I have to.
Why not approach the problem from a different angle:
Create your grid using GridLayout
Fill the grid with empty JPanels
JPanels that each uses a BorderLayout
Keep these JPanels where they are
But swap the JPanels that they display within them in each of their BorderLayout.CENTER positions
Alternatively, you could have them use a CardLayout, but the effect would be the same

Java AWT setLayout(null) doesn't seem to be working

I'm trying to re-purpose an existing Java AWT (stand alone) application to run on dedicated, single-purpose hardware (think a kiosk in a museum that also controls hardware behind the scenes) and my presumption that if I simply set the layout manager on my main panel to null I'd be able to lay out items using something like Rectangle(starting x, starting y, x-width, y-height) or perhaps another similar method to position things, has proven false! So, I'm more lost than I thought I would be!
Here are a few excerpts:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
[...]
public class myGUI extends JFrame
{
JPanel MainPanel = new JPanel();
JMenuBar Menu = new JMenuBar();
int MaxWidth = 1920;
int MaxHeight = 1080;
Dimension FullScreen = new Dimension(MaxHeight, MaxWidth);
Rectangle recHZbar = new Rectangle(0, 32, MaxWidth, 4);
[...]
MainPanel.setLayout(null);
MainPanel.setPreferredSize(FullScreen);
MainPanel.setEnabled(true);
MainPanel.setBackground(LightBlue);
There are all manner of components totaling around a hundred or so and it makes no sense to present them here. Suffice to say that I'm trying to eliminate what were stand-alone frames and instead present all the data around the edges of a very large screen and then manage the center space of the screen separately with key data, hopefully able to use visibility to switch what the user sees (instead of panes / panels), since in many cases there's a lot of commonality.
I thought that by setting the layout manager to null I would then be able to position components on MainPanel using something like this horizontal bar with a message embedded in it:
JLabel HorizontalBar = new JLabel();
HorizontalBar.setBackground(DarkBlue);
HorizontalBar.setFont(new java.awt.Font("Dialog", 0, 10));
HorizontalBar.setForeground(LightBlue);
HorizontalBar.setPreferredSize(dimMxW10pt);
HorizontalBar.setOpaque(true);
HorizontalBar.setHorizontalTextPosition(SwingConstants.CENTER);
HorizontalBar.setText(HZBarTxt);
HorizontalBar.setBounds(recHZbar);
MainPanel.add(HorizontalBar, null);
... And this, of course, works, BUT, when I tried to position this horizontal bar (via setBounds(rectangle)), it's apparent that the coordinates are based off of the bottom of the JMenuBar I added earlier, and NOT from the upper left corner of the screen! This has me rather concerned! (I presume the next bar will be based on the space below the first one, etc?!) Am I correct in thinking I've got a layout manager I didn't (explicitly) ask for? (If so, how do I avoid it?)
I'm hoping I'm overlooking something simple to be able to do positioning myself without having to go through too much work. If I can't just pick where I want things to be on the screen, I'm going to be in trouble on this project! I'm hoping to avoid lots of little panels and such. I need to create irregular columns and so forth. I know I can do the math to lay things out how I want, and I'm loathe to trust a layout manager to get it right, especially since the testing on the actual production hardware is very hard, and if the layout manager is different, it'll mean trouble. I may well be I'm overlooking the right layout manager - the "do it yourself layout manager", perhaps? - but I don't see how "GridLayout" is going to work for me, at least, not easily. So I'm hoping to learn how to do my own layout as simply and directly as possible (which is what I thought I was already doing).
TIA.
It turns out that my original assumption that the call to setLayout(null) hadn't worked was itself mistaken, and that's a good thing!
Given MY requirements, I made the exactly correct choice of NOT using a window manager at all. Yes, it can easily be seen from the comments to the question that many people think it's foolhearty or even stupid to NOT use a window manager, but in my case I made the EXACTLY right choice, and here's why:
THIS PROJECT'S circumstances are one of the rare cases for "doing it yourself", and, indeed, if I'd used a window manager it would NOT have worked out _AT_ALL!_ ...at least not in the time I had available.
A brief review of the use-case for this project
This was for dedicated hardware control and, indeed, it could not run in production in any place but a very singular installation of specialized hardware that the Java code is providing a user-interface for. Further, it won't have any internet connection, ever, and will never be upgraded. There's ZERO concern over either operating system or other software upgrade - it just cannot happen. As it has a singular fixed running environment, there's no concern over font-change handling or anything like that.
The Application Design; WHY A Layout Manager Would Screw It Up
I chose to use one frame, "undecorated" so that if fills the entire display, like this:
This fills a 1090 X 1920 display completely, so IDK how well it will go here.
I created a JFrame that serves as a backdrop for the whole thing. Within it, I first created a menu bar, followed by a heading / title bar, and for these things, a layout manager could have done a great job, of course, but that's the end of the easy part.
I created a right and left column of necessarily different widths and then a section at the bottom just by placing the items using item.setBounds(X, Y, W, H). I used variables for the values, and used them to create standard row & column positions, widths and heights. This provided for easy shifting from the standard in places that required it; I'd just use a different set of variables (using a naming convention I invented to keep it easy). I'd imagine that you COULD have used a layout manager for this part, but it would have been tedious and it's not at all clear it would have been any less work. In particular, how do you get the two vertical columns do be where you want them? You'd have to create separate inset panes / panels for each differently formatted region within each column - and even the bottom rows! You'd have to get them to stack or space just right, too. Then there are those vertical and horizontal bars - how'd you do that?
Vitally, I left room for an inset panel in the center, of which there are a VERY large number (!!) way more than are apparent from what you can see in the sample image. They're JPanels, only one of which is visible here, of course. And this is where a layout manager would completely fall on its face. Good UI design keeps things consistent and so users can know where to expect to find various things. And on the various panels, there are things that are common among some panels and different on others with DIFFERENT commonality, and there are yet more panels that cross with commonality between different sets of panes, yet few of the panels are really full enough that the common layout manager packing algorithms could handle; Various items are - and need to be - in what may appear to be non-standard positions for various reasons, so using a layout manager would have required filling up the panes with lots of sub-panels and such so that each of the various layout managers could do their jobs properly. By NOT using a layout manager, and thereby being free to just exactly specify the different positioning of the components when looking at different inset panes, I was able to VERY SIMPLY just use panel.add(item) syntax to move items between panes and keep the position exactly. Further, because of the same top position of the outer panel and the inner ones, getting rows exactly right was a cinch!
This was hands-down the easiest approach. I would have been fighting the damned layout manager all along the way. ...DO NOT BE AFRAID TO SKIP A LAYOUT MANAGER AND DO IT YOURSELF, just be prepared to do the whole job yourself. If you're up to that task, and if you have a fixed-use-case situation like I had, it's not so bad at all, and it might even be the only practical way for some tasks.

fastest way to animate a lot of graphic elements in JavaFX

I'm taking my first steps in JavaFX. I want to animate a lot of graphic elements (ellipses, bézier curves, not images) with javafx. These elements are organized in groups of about 10 elements that have to move together. I'm trying to achieve 60 fps and I want to move thousands of these elements.
There are at least four ways to do this:
Using a single canvas, and its graphical context to draw directly at every frame.
Using groups and nodes. Each element is a node. I like this way because there are a lot of classes that can be used to draw easily what I need, and the logical structure of nodes and groups is just what I need.
Each group is a canvas. Draw its elements in its constructor using the canvas graphical context, and then move all the canvases at each frame.
Creating images with the elements that have to stick together, and then move these images. I haven't found already how this can be done, but I suppose it's not hard. The elements may change from time to time, so I will need to recreate some images, but only once in a while.
My question is, which of these ways (or another) would be the fastest way to do it? In particular, does the use of a lot of nodes and groups has a high performance impact?
I would start with option two as this seems to logically fit your needs best. It is also the most straight forward and cleanest way of doing it with JavaFX. If it later turns out that the performance is not good enough you can try to improve this by using caching together with the appropriate cache hints. For example you can optimize your code for speed or for quality this way. This even works dynamically because you can switch between these cache hints depending on the state of your application. If you switch caching on for a group this has basically the same effect as drawing it into a canvas or an image.

Draw2D GridLayoutAlgorithm with constant node width?

We're integrating Draw2D/GEF into an application, and are encountering an issue with the standard layouts provided.
We have a collection (say 100) of elements that need to be displayed in a grid-like fashion. We implemented our view using a GraphViewer, and applied a GridLayoutAlgorithm.
This works almost as we'd like it to, but the one stipulation we haven't been able to meet is that each node must be of a constant, defined size. Say, 50x50 pixels. The current GridLayoutAlgorithm we're using resizes the nodes so that they all fit in the window. If our window is small, the 100 elements become minuscule. We would instead like them to fill the width, then wrap to multiple rows, with a vertical scrollbar.
For the life of me, I can't find a simple, straightforward way to accomplish this.
Kind of a bummer answer, but I just ended up writing my own subclass of a GridLayoutAlgorithm and did a bunch of the math by hand. Frustrating that this wasn't included out-of-the-box, but it works fine.

Flocking boids behaviour problem

Yesterday I came across Craig Reynolds' Boids, and subsequently figured that I'd give implementing a simple 2D version in Java a go.
I've put together a fairly basic setup based closely on Conrad Parker's notes.
However, I'm getting some rather bizarre (in my opinion) behaviour. Currently, my boids move reasonably quickly into a rough grid or lattice, and proceed to twitch on the spot. By that I mean they move around a little and rotate very frequently.
Currently, I have implemented:
Alignment
Cohesion
Separation
Velocity limiting
Initially, my boids are randomly distributed across the screen area (slightly different to Parker's method), and their velocities are all directed towards the centre of the screen area (note that randomly initialised velocities give the same result). Changing the velocity limit value only changes how quickly the boids move into this pattern, not formation of the pattern.
As I see it, this could be:
A consequence of the parameters I'm using (right now my code is as described in Parker's pseudocode; I have not yet tried areas of influence defined by an angle and a radius as described by Reynolds.)
Something I need to implement but am not aware of.
Something I am doing wrong.
The expected behaviour would be something more along the lines of a two dimensional version of what happens in the applet on Reynolds' boids page, although right now I haven't implemented any way to keep the boids on screen.
Has anyone encountered this before? Any ideas about the cause and/or how to fix it? I can post a .gif of the behaviour in question if it helps.
Perhaps your weighting for the separation rule is too strong, causing all the boids to move as far away from all neighboring boids as they can. There are various constants in my pseudocode which act as weights: /100 in rule 1 and /8 in rule 3 (and an implicit *1 in rule 2); these can be tweaked, which is often useful for modelling different behaviors such as closely-swarming insects or gliding birds.
Also the arbitrary |distance| < 100 in the separation rule should be modified to match the units of your simulation; this rule should only apply to boids within close proximity, basically to avoid collisions.
Have fun!
If they see everyone, they will all try to move with average velocity. If they see only some there can be some separated groups.
And if they are randomly distributed, it will be close to zero.
If you limit them by rectangle and either repulse them from walls or teleport them to other side when they got close) and have too high separation, they will be pushed from walls (from walls itself or from other who just were teleported, who will then be pushed to other side (and push and be pushed again)).
So try tighter cohesion, limited sight, more space and distribute them clustered (pick random point and place multiple of them small random distance from there), not uniformly or normaly.
I encountered this problem as well. I solved it by making sure that the method for updating each boid's velocity added the new velocity onto the old, instead of resetting it. Essentially, what's happening is this: The boids are trying to move away from each other but can't accelerate (because their velocities are being reset instead of increasing, as they should), thus the "twitching". Your method for updating velocities should look like
def set_velocity(self, dxdy):
self.velocity = (self.velocity[0] + dxdy[0], self.velocity[1] + dxdy[1])
where velocity and dxdy are 2-tuples.
I wonder if you have a problem with collision rectangles. If you implemented something based on overlapping rectangles (like, say, this), you can end up with the behaviour you describe when two rectangles are close enough that any movement causes them to intersect. (Or even worse if one rectangle can end up totally inside another.)
One solution to this problem is to make sure each boid only looks in a forwards direction. Then you avoid the situation where A cannot move because B is too close in front, but B cannot move because A is too close behind.
A quick check is to actually paint all of your collision rectangles and colour any intersecting ones a different colour. It often gives a clue as to the cause of the stopping and twitching.

Categories

Resources