Problem with Thread.sleep(x) in a for loop - Java - java

I'm making a frame which contains a round rectangle. This rectangle is constantly (re)painting
itself with smaller values.
The plan is, First the rectangle decreases in width(x), after that the rectangle decreases in height(y).
But for now I just want to get the width decreasing done with. But I'm having troubles here already.
Please note, i'm only drawing the rectangle's borders, so I don't want to fill it.
I made a for loop as follows:
for (rectWidth = 470; rectWidth >= 0; --rectWidth) {
try {
//simply made to represent rectWidth's value, not really relevant
System.out.println("rectWidth is: " + rectWidth);
//draw the rectangle with it's new width, ignore the "rectHeight" for now.
g.drawRoundRect(5, 5, rectWidth, rectHeight, 10, 10);
//this Thread.sleep is messing up my frame which has an instance of this class added
//to it also, my program is uninterruptable when adding this Thread.sleep
Thread.sleep(500);
} catch (Exception ex) {
//rectangle's value will be returned here when interrupted.
}
}
My question is, how can I add a 'sleep' in my for loop in order to make the drawing not go all too fast,
withouth messing my frame. This thread.sleep is messing my frame up in a way that I don't even see
the rectangle anymore.
I want to achieve a smooth (re)painting of the rectangle. (And yes I know as this piece of code is now
it isn't repainting the rectangle but constantly painting a slightly smaller rectangle in the frame.)

The reason that the rectangle isn't getting displayed is that the display is updated on the EventDispatchThread which is probably the same one your loop is on. That is it can't paint the rectangle because it's too busy sleeping.
The solution is to use a Swing Timer which will run and happily sleep sending tasks to the EventDispatchThread when updating.

Also you can start drawing thread
#Override
public void run(){
while(shouldDraw())
this.wait(500);
yourCalculations(); // maybe setting fields in runnable object
SwingUtilities.invokeAndWait(yourRunnableObject); // or invokeLater if you prefer non blocking version
}
This code shows only conception (I've skipped synchronization and exception handling). SwingTimer looks more elegant but I've never used it.

Related

Repainting an Ellipse, JAVA Swing/AWT

I am aware of the java repaint() method that repaints the whole screen and the repaint(int, int, int, int) for repainting a certain section of the screen but I have an ellipse that I am making move across the screen. Is there a way to only repaint the space containing the ellipse? Or a way to obtain the space an ellipse is taking up? Thanks.
If what you're trying to do is repaint the portion of the screen that contains the ellipse, you could do something like this:
int ovalposX = 0;
int ovalposY = 0;
int ovalwidth = 10;
int ovalheight = 10;
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(ovalposX,ovalposY,ovalwidth,ovalheight)
}
public void redraw(){
repaint(ovalposX, ovalposY, ovalwidth, ovalheight);
}
(If this is incorrect I'm terribly sorry, this is my first post)
Is there a way to only repaint the space containing the ellipse?
Read the section from the Swing tutorial on Custom Painting.
The working example shows how to use two repaint() requests to move a square:
the first for the current location of the object (this will cause the background area to be cleared from the old location)
the second for the new location of the object
The two requests will be combined into one larger area before the painting is done.
In reality you don't really need to worry about getting this detailed. I doubt you will notice a performance difference between repainting the entire panel or only a portion of it.

What does the OFFSET do in repaint()-method with varargs?

private void moveSquare(int x, int y) {
int OFFSET = 1;
if ((squareX!=x) || (squareY!=y)) {
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
squareX=x;
squareY=y;
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
}
}
This is a snippet from the Java-Tutorials. It's a tutorial about painting in Java.
https://docs.oracle.com/javase/tutorial/uiswing/painting/step3.html
I don't understand what the OFFSET is exactly doing.
Or what happens inside the repaint()-method.
I know, that it is necessary to paint the square correctly, because I noticed, that without the OFFSET, the Square is sometimes missing a side, which is not painted, or not deleted by the first repaint()-method.
I let the program write the variables (used for painting the rectangle (squareX...and so on)) in the console. But the width and heigth were always 20 and not 21 (width + OFFSET). Even when I read the variables inside the paintComponent()-method.
That is why I don't understand, why the square is drawn correctly the very first time, but every other time it gets repainted without the OFFSET, then it is drawn incorrectly.
And I can't look inside the repaint()-method (at least I don't know how to do it)
Another little question: Does the repaint method always "delete / overwrite" the object (in this case the square) it wants to draw, if the variables like color, position haven't changed?
This is what the paintComponent()-method is doint.
g.setColor(Color.RED);
g.fillRect(squareX,squareY,squareW,squareH);
g.setColor(Color.BLACK);
g.drawRect(squareX,squareY,squareW,squareH);
What I don't understand is, why the first repaint()-method deletes the old square. squareX/Y/W/H are the same as before. Afterwards they get the new coordinates from the mouse click, and then the square gets painted at the new location.
Why does the same code delete in the first repaint() and in the second one it creates a new square?
Sorry, if my english is bad. I'm from Germany.
Thanks in advance for all your answers!!!
I don't understand, why the square is drawn correctly the very first time,
The paintComponent(...) method is invoked WITHOUT clipping, so the entire (250 x 200) area of the panel is repainted.
but every other time it gets repainted without the OFFSET, then it is drawn incorrectly.
When you click on the panel the paintComponent(...) method is invoked WITH clipping as the two repaint(...) requests are consolidated into a single clipped painting request to make the painting more efficient.
For example, initially the square is painted at (50, 50). If you now click at (80, 80) the area repainted will be: (50, 50, 101, 101), which is the minimum area needed to clear the old square and paint the new square.
You can see the size of the clipped area change by adding the following to the paintComponent() method:
System.out.println(g.getClipBounds());
Note: for a simple painting like this you don't really need to be fancy. You could just invoke a single repaint() statement after resetting the x/y values and the entire panel will be repainted.
I don't understand what the OFFSET is exactly doing.
I think this comes down to how what a width and height value actually is.
Generally speaking, most people see a width and height as been 1 indexed (1 to width/height), where as the computer sees it as 0 indexed (0 to width/height).
So, when you define a area as been 20 pixels wide/high, is it 0-20, 0-19 or 1-20?
You can see this if you try and draw a rectangle surrounding the entire component. If you were to do something like....
g.drawRect(0, 0, getWidth(), getHeight());
the right/bottom edges would appear of the screen, instead, you need to use something more like
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
What is happening here is, the API is allowing for both circumstances. The size of the component (defined by the width and height properties) is represented as 1 indexed, but we need to adjust for this to make it 0 indexed.
Why EXACTLY this is happening is beyond my knowledge (and generally my caring), all I care about is knowing that it happens ;)
Or what happens inside the repaint()-method.
Generally, you shouldn't. What you should care about is the fact that the API provides a consistent result, as the underlying workings of the API are delegating to native functionality (DirectX or OpenGL in most cases)
I let the program write the variables (used for painting the rectangle (squareX...and so on)) in the console. But the width and heigth were always 20 and not 21 (width + OFFSET). Even when I read the variables inside the paintComponent()-method.
That is why I don't understand, why the square is drawn correctly the very first time, but every other time it gets repainted without the OFFSET, then it is drawn incorrectly.
You wouldn't. The repaint isn't changing the physical state of the variables, but instead is asking that a small area of the component be updated, without effecting the rest of the component. What the API does is generate a clipping rectangle (see, it's another rectangle), which only allows updates to appear within that area (painting beyond it has no effect).
This is why it's called twice, once for the old position and once for the new
Another little question: Does the repaint method always "delete / overwrite" the object (in this case the square) it wants to draw, if the variables like color, position haven't changed?
It depends. Painting is considered destructive, that is, each time paintComponent is called, you are expected to repaint the entire state of the component from scratch. So, even if the state hasn't changed, the entire area must be repainted, because the paintComponent has no idea what the previous state was.
In Swing, the Graphics context is a shared resource, it is used to paint all the components for a common native peer.
You could have a look at Painting in AWT and Swing and Performing Custom Painting which might provide you with some more information about the painting process

Processing: Trying to animate the drawing of lines without draw function

I'm trying to write a program that is under 140 characters(Twitter Char Limit). The program displays the final output I want but I don't understand why it's not showing the animation as it creates it. I thought it was because I don't have a draw function but I don't see why that matters if the drawing is all done within a For loop, anyways the draw function didn't help. I've tried bringing the framerate way down but yet for some reason it's only giving me a static final output. Any help is much appreciated.
int j=600, i=j/3;
size(j, j);
smooth();
translate(j/2, j/2);
for (i=1; i<12500; ) {
fill(i%j, i%j);
rotate(j%i*5);
line(i++%j, i++%j, i+++j, int(i/99)%99);
}
Processing uses double buffering, which means that when you draw "to the screen", you're actually drawing to an off-screen buffer. Since your code is not in the draw() function, this happens before the frame is even visible. Then when the frame becomes visible, it takes the entire off-screen buffer, and draws the whole thing to the screen.
That's why you only see the end result of the drawing. If you want to display an animation, you'll have to use the draw() function and timing of some kind (the millis() method, for example).

Infinite Painting Java

I started playing with someone's else code and came across an interesting experiment. The program will work fine with the if statement. But I found out if I change the if statement into a while loop, the program runs but I could not close the program with the X button instead I had to press Eclipse terminate button. I am guessing this is a sign of an infinite loop or is it the fact that Java cannot repeatedly draw the same images over and over again?
// if you want to draw graphics on the screen, use the paintComponent method
// it give you a graphic context to draw on
public void paintComponent(Graphics g){
super.paintComponent(g);
// when the player is still in the game
if(inGame){
g.drawImage(apple, apple_x, apple_y, this);
for (int z = 0; z < dots; z++) {
if (z == 0)
g.drawImage(head, collisionX[z], collisionY[z], this);
else g.drawImage(tail, collisionX[z], collisionY[z], this);
}
Toolkit.getDefaultToolkit().sync();
// dispose graphics and redraw new one
g.dispose();
}
else gameOver(g);
}
Changing this line into a while statement
if (inGame) {
will not allow the variable to be reset to false, resulting in the infinite loop. Having while loops or any resource-heavy calls in paintComponent is a bad idea in general. Swing has concurrency mechanisms to deal with these.
If you want your UI to remain responsive, event handlers and repaints should finish within a reasonable amount of time. This means you shouldn't loop inside paintComponent() at all; instead you have to repeatedly trigger a repaint from somewhere else, like an animation timer.
Changing the if to a while, ie:
while(inGame){
will loop forever if inGame is true, because there would be only two ways to exit the loop:
inGame is set to false within the loop
the is a break statement in the loop
neither of which is found in the code.
fyi, the code pattern while(true) is a common way to create an infinite loop, which is needed for things like web services waiting for requests

java+Swing: efficient overlay of rectangle or other "sprite"

I'm not quite sure how to phrase this, so bear with me.
I have two JPanels in a container JPanel with an OverlayLayout. Both JPanels in the container override paint(Graphics).
The bottom JPanel is opaque and draws some fairly complicated graphics, so it takes a "long" time (10s or 100s of milliseconds) to render.
The top JPanel is transparent and just draws a rectangle or line or simple shape based on mouse input, so it's really quick.
Is there a way to set things up so when I change the simple shape in the upper panel, it doesn't redraw the bottom panel? (e.g. it somehow caches the bottom panel)
I'm vaguely familiar w/ concepts like bitblt, double-buffering, and XOR-drawing but not really sure what to apply here.
You'd be best off using a single JComponent and creating a BufferedImage to store the bottom image. When the paintComponent operation happens on the JComponent, you just blit the bottom image and use the Graphics object to do any further drawing on top of that (from a stored state). Should be fairly efficient.
You'll want to do the complex drawing operations for the bottom BufferedImage in another thread, as the other poster mentions (omitted this by accident, sorry :)). However, you don't want to cause contention on this image, so you must store an additional BufferedImage for this, and blit it synchronously to the other image at the very moment the drawing operations are complete on it.
Focusing on the complicated panel, the key is factoring everything out of paintComponent() except drawImage(). Put everything else in another thread that continually updates an offscreen buffer. Periodically update the screen at some rate that keeps the simple panel responsive. The only hard part is synchronizing, but SwingWorker is a good choice. There's more here.
What's sure is that if the upper panel is target for a full repaint(), then the lower one will be also.
Maybe you can try to optimize the region to repaint on the upper panel in order to avoid repainting all the lower one. But if the painted rectangle in the upper panel covers the whole area, then you end up with full repaint() once again.
Normally, Swing tries to optimize the regions that need a repaint, but it also aggregates these regions when several repaint are performed in a short time, and if I remember well, the aggregated region is just a rectangle that is the union of all repaint rectangles, which is not always optimized but allows for fast computation of repaint events creation.
Now, I think you should follow the advices given in previous replies; indeed, you should really avoid having a paint() method that can perform computations that can be that long (a few 10s of ms should be the real maximum). Painting should be as fast as possible if you don't want to have a GUI that looks unresponsive to the end user. Hence, favour performing the computation only once (and outside the EDT if possible) store the result in a BufferedImage that you just simply draw later on in the paint() method.
EDIT: added other sources of reflection
If you want to optimize the update of the list of points but still keep it in the paint() method, then you can use the clipping region of the passed Graphics to limit the calls to drawing methods, something like:
Rectangle clip = g.getClipBounds();
for (Point p: allPoints) {
if (clip.contains(p)) {
// draw p with g graphics
}
}
You can even try to optimize the list of points to draw by using a QuadTree instead of a simple List, but you'll have to code it yourself (or find some free implementations, there are probably a few of them out there). With a quadtree, you can optimize the time to find the list of all points that have to be redrawn (based on the Graphics clipping rectangle) and only redraw those points.
Addenda for answer by trashgod and jfpoilpret
1/ OverlayLayout is strange way how to layout JPanels, are you same output with once JPanel (without OverlayLayout and Translucentcy)
2/ (10s or 100s of milliseconds) is maybe small value because there is Native OS Latency (45-75ms for today OS and PC)
3/ synchronizations would be managed by using SwingWorker on BackGround Task and with order, directions and synchronizations for painting processes to the JPanel, maybe your paints are too fast/quickly
4/ you didn't describe more about how, where and which about paint()/paintComponent()
if (SwingUtilities.isEventDispatchThread()) {
paintImmediately(int x, int y, int w, int h) // or Rectangle r
} else {
Runnable doRun = new Runnable() {
#Override
public void run() {
repaint(long tm, int x, int y, int width, int height) // or Rectangle r
}
};
SwingUtilities.invokeLater(doRun);
}

Categories