Swing timer - time fluctuating - java

I am using a Swing Timer in my game but when the game is running it appears to have moments when it runs smoothly and moments when it slows down.
Why is the time fluctuating?
And how do I fix it?
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main extends JFrame {
public Main() {
super("JFrame");
// you can set the content pane of the frame
// to your custom class.
setContentPane(new ImagePanel());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 400);
setResizable(false);
setVisible(true);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new Main();
}
class ImagePanel extends JPanel {
Timer movementtimer;
int x, y;
public ImagePanel() {
x = 0;
y = 0;
movementtimer = new Timer(12, new ActionListener() {
public void actionPerformed(ActionEvent e) {
long timstarted = System.currentTimeMillis();
moveImage();
repaint();
long timefinished = System.currentTimeMillis() - timstarted;
System.out.println(timefinished + " to run");
};
});
movementtimer.start();
}
public void moveImage() {
x++;
y++;
if (x > 800) {
x = 0;
}
if (y > 400) {
y = 0;
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(0, 0, 800, 400);
g.setColor(Color.BLUE);
g.fillRect(x, y, 50, 50);
}
}
}
Here is an example of my code. In my actual program I am drawing Images and not just a rectangle. There is also a lot of collision detection and other small calculations happening.
Also, here is a link to the Jar file for the game so you can run it and (hopefull) see what I mean. http://dl.dropbox.com/u/8724803/Get%20To%20The%20Chopper%201.3.jar
Thanks
Tom

Because the rendering is trivial, I find this variation of your example to be very smooth. The render time is well below a half millisecond, so the 12 millisecond period (~83 Hz) is plenty of time to finish a frame, typically taking less that 10% of one core. As the render time grows, the timer thread becomes saturated, and events are coalesced. The effect is magnified on a single core, as rendering competes with garbage collection and external processing demands. Java is not a real-time system, and not all schedulers are created equal.
You'll certainly want to profile your actual code, as suggested here, to see any correlation with fluctuating performance. One alternative approach is to lengthen the period (decrease the frequency) to meet your rendering deadline and use a larger increment in moveImage() to get the same velocity.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main extends JFrame {
private static final int W = 800;
private static final int H = 400;
public Main() {
super("JFrame");
this.add(new ImagePanel());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
setSize(W, H);
this.setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Main();
}
});
}
class ImagePanel extends JPanel {
Timer movementTimer;
int x, y;
public ImagePanel() {
x = 0;
y = 0;
movementTimer = new Timer(12, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
moveImage();
repaint();
}
});
movementTimer.start();
}
public void moveImage() {
x++;
y++;
if (x > W) {
x = 0;
}
if (y > H) {
y = 0;
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
long start = System.nanoTime();
g.setColor(Color.RED);
g.fillRect(0, 0, W, H);
g.setColor(Color.BLUE);
g.fillRect(x, y, 50, 50);
double delta = (System.nanoTime() - start) / 1000000d;
g.drawString(String.format("%1$5.3f", delta), 5, 15);
}
}
}

The Swing Timer is notorious for its inaccuracy. Use something else instead.
On prompting, I've decided to undelete this post. OTOH most of the extra information that makes it worth reinstating comes from trashgod, so I'll merely quote/paraphrase their comments.
I'd argue that it's reasonably accurate but easy to saturate.
And trashgod goes on to add in a separate comment that:
(I) might cite Clock Quality. javax.swing.Timer isn't very accurate, but it has usefully precise resolution on modern platforms.

In the panel constructor do:
setBackground(Color.RED);
Then you do not need to erase the background, as you are calling super.paintComponent.
In general calculate positions on actual time passed (System.nanoTime()) and do not rely on timer frames. There are a couple of gaming frameworks out there, so maybe it is worth looking at their solution. I liked the sample.

Related

Trying to create disappearing squares in Java

class GraphicsExampleComponent extends JComponent
{
//#Override
public void paintComponent(Graphics g)
{
//make the first call to your recursive routine
drawSquare1(g, 0, 0, 80);
}
public void drawSquare1(Graphics g, int x, int y, int size)
{
//draw a rectangle
g.drawRect(x, y, size, size);
g.fillRect(x, y, size, size);
//reset the parameters
x = x + size + 10;
y = y + (size/4);
size = size/4;
//determine if you should call it again.
if (size<4 || x>600)
drawSquare1(g, x, y, size);
}
My assignment is to create disappearing squares that get 25% smaller as they move to the right. When I run the code, it just creates the one square and stops. Can anyone help me understand what I am doing wrong.
disappearing squares that get 25% smaller as they move to the right.
Ok, let's step back for a second and break this down a bit.
You need to know...
The amount of space to be covered
The amount of space already covered (by the square)
The original size of the square
The size of the square should be when it reaches the other side (25% smaller)
When you have all this, you can calculate the size of the square at any point along its journey.
To determine the amount of space, you can use the component's width, via getWidth().
To determine the space already covered, you could start by having a look at box's current x position
// Assuming that box is a instance of Rectangle
double progress = (double)box.x / (double)getWidth();
We could argue that we should look at the middle of the box, or the trailing edge, but both of those are easy to implement.
Next, we need know the range of change (from start to end size), we can then use that to calculate the delta to be applied to the box...
double range = startSize - endSize;
double value = (range * progress);
box.width = (int)startSize - (int)value;
box.height = (int)startSize - (int)value;
Soooo, this will provide with the means to determine the size of the component based on it's current location (horizontally) through the component.
Next, you need some way to update the box's position and update the UI.
One of the better solutions is to use a Swing Timer. This will allow you to perform a repeating action (with a specified delay between updates) which won't block the UI and will generate updates within the Event Dispatching Queue, which is important because Swing is not Thread safe.
Have a look at How to Use Swing Timers for more details.
And finally, all we need, is to update the component with current state, via it's paintComponent method ... easy :P
Runnable example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
protected static double startSize = 50;
protected static double endSize = startSize * 0.25;
private Rectangle box;
private Timer timer;
public TestPane() {
box = new Rectangle(0, 100 - 25, 50, 50);
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (box.x + box.width >= getWidth()) {
box.x = getWidth() - box.width;
box.width = (int)endSize;
box.height = (int)endSize;
timer.stop();
repaint();
}
box.x += 1;
double progress = (double)box.x / (double)getWidth();
double range = startSize - endSize;
double value = (range * progress);
box.width = (int)startSize - (int)value;
box.height = (int)startSize - (int)value;
repaint();
}
});
}
#Override
public void addNotify() {
super.addNotify();
box.x = 0;
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
timer.stop();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fill(box);
g2d.dispose();
}
}
}
The condition if (size < 4 || x>600) is never true because when drawSquare1 is invoked for the first time size=80 and x=0.
Changing it to say if (size > 4 && x<600) will paint 3 squares on the screen without any noticeable animation.
To animate it we'll need to add some delay between paintings and remove previously painted squares.
To do so we use a swing Timer. We use the timer to repeatedly invoke drawSquare1.
drawSquare1 should modify the parameters controlling the painting, and call repaint.
import java.awt.*;
import javax.swing.*;
public class Main {
Main() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
GraphicsExampleComponent board = new GraphicsExampleComponent();
frame.add(board);
frame.pack();
frame.setVisible(true);
board.animate();
}
public static void main(String[] args) {
new Main();
}
}
class GraphicsExampleComponent extends JComponent{
private final static int W = 200, H = 100, MIN_SIZE = 4, STEP = 10, DELAY = 2000;
private int x= 0, y = 0, size = 80;
private Timer timer;
void animate(){
timer = new Timer(DELAY, e->drawSquare());
timer.start();
}
public void drawSquare(){
//check stop criteria
if (size < MIN_SIZE || x >= getWidth()) {
timer.stop();
return;
}
//reset the parameters
x = x + size + STEP;
y = y + size/4;
size = size/4;
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//draw a rectangle
g.fillRect(x, y, size, size);
g.dispose();;
}
#Override
public Dimension preferredSize() {
return new Dimension(W, H);
}
}
(Test in online here)
Thank you everyone for the help. I unfortunately could not use a timer due to us not learning about it in class and we are not allowed to use methods/codes that we haven't gone over. To fix this I had to fix my base case by making it size >= 4 && x < 600.

Animate Image in Java2d

im learning Java2d, and im trying to animate my image in x coordinate using a Timer, but is not working, the idea is between a time frame the image x value increments a value making it to move, can someone figure out what is the problem in my code?
Here is the code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class Screen extends JPanel {
int posX;
Timer timer;
private BufferedImage image;
public Screen() {
setDoubleBuffered(true);
posX = 1;
timer = new Timer();
timer.scheduleAtFixedRate(new Anima(), 100, 10);
//Smile Icon
try{
image = ImageIO.read(getClass().getResource("/smily.png"));
}catch(IOException e){
e.printStackTrace();
}
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(image,this.posX,100,null);
}
class Anima extends TimerTask{
public void run() {
posX += 20;
repaint();
}
}
public void incMove() {
posX += 20;
}
}
Two main things:
The delay is small
The change is large
This means that the object can be moved out side of the visible bounds quickly, usually quicker then the screen can be realized on the screen.
You could play with the timing, but, animation is the illusion of change over time, you may find it better to reduce the size of the change rather the then the delay (although 100fps might be asking a bit much ;))
There is also no bounds checking, so the object is free to move off the viewable area, this might be desirable, but probably would have at least hinted towards the problem you were having.
As Swing is not thread safe (you shouldn't update the UI from outside the context of the EDT), you are also running the risk of inuring a thread race condition. In this example, it would probably be very hard to do, but the concept of how you are changing the state is dangerous.
Because Swing uses a passive rendering engine, a paint cycle may occur at anytime, without your interaction or knowledge, so you should be careful updating variables which the paint methods need to render the state outside of the context of the EDT.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Screen extends JPanel {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Screen());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private int posX;
private int delta = 2;
public Screen() {
setDoubleBuffered(true);
posX = 1;
Timer timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
incMove();
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
Graphics2D g2d = (Graphics2D) g;
g2d.drawRect(this.posX, 100, 10, 10);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void incMove() {
posX += delta;
if (posX + 10 > getWidth()) {
posX = getWidth() - 10;
delta *= -1;
} else if (posX < 0) {
posX = 0;
delta *= -1;
}
}
}
This simple example uses a Swing Timer as the primary engine, it reduces the amount of change (to 2) as well as adds bounds checking
Take a look at Concurrency in Swing and How to use Swing Timers for more details
Your code is working, but you are updating too fast.
Try
timer.scheduleAtFixedRate(new Anima(), 100, 1000);
To check your animation.

Draw in my JPanel calling a function in the "main"

I have an informatic project with JAVA langage for class and it's rated to graduate from high school.
Btw my program consists in drawing geometrical forms in the JPanel with clic control but I don't know how to put, in my main program, fonctions which draw figures as I want like drawSquare(x,y,length). For the moment the program show this window (it's a JFrame with my JPanel inside):
http://i.imgur.com/YetG3VB.png
I have a method readClick() which give to a Point point the coordinates point.x and point.y in the frame but I don't know how, in my main, I can do to call a graphical drawing.
"My problem is that I want to put in my main program something like
Point clic;
clic= Fenetre.readClick() ; // I have this method which wait me to clic on the frame
x = clic.x ; //and give to point the coordonates
y= clic.y; // point.x && point.y
clic = Fenetre.readClick();
a = clic.x ;
b = clic.y ;
"Function which draws rectangle for example"(x,y,a,b);
//It's this fonction I want to create and
// I want it to draw the rectangle in the JPanel
But I don't know how to make the function which adds a drawing in the JPanel "
My main program :
public class Geogebra {
#SuppressWarnings("unused")
public static void main(String args[]){
Fenetre fen = new Fenetre();
}}
My window class :
import java.awt.Color;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.concurrent.SynchronousQueue;
import javax.swing.JFrame;
#SuppressWarnings({ "unused", "serial" })
public class Fenetre extends JFrame {
static Panneau component = null;
private final static SynchronousQueue<MouseEvent> clicks = new SynchronousQueue<MouseEvent>();
public Fenetre(){
JFrame frame = new JFrame();
component = new Panneau();
frame.setTitle("ISNOGEBRA");
frame.setSize(900, 700); // Taille initiale de la fenetre : 900 * 700
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(component);
frame.setVisible(true);
component.addMouseListener( new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
clicks.offer(e);
}
});
}
public static MouseEvent readMouse() {
try {
return clicks.take();
}
catch (InterruptedException e) {
throw new AssertionError();
}
}
public static Point readClick() {
return readMouse().getPoint();
}
}
My JPanel class (the two class Mathwrapper and Menu just draw the interface, I'll show them if needed but it's really long) :
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.swing.JPanel;
#SuppressWarnings({ "serial", "unused" })
public class Panneau extends JPanel implements MouseListener {
Menu menu;
MathWrapper mw;
public Panneau(){
super();
this.setBackground(Color.WHITE);
menu = new Menu(20, 20);
mw = MathWrapper.getInstance(0, 80);
this.setOpaque(true);
this.addMouseListener(this);
}
#Override
public void paintComponent(Graphics g){
menu.draw(g);
mw.draw(g);
}
public void mouseClicked(MouseEvent e) {
//That shows a blue square behind the icon in the menu I click on
//and draws a black one on the one which was selected
if((e.getX() > 20 && e.getX() < 60) && (e.getY()) > 20 && e.getY() < 60){
menu.btns[menu.activeTool].toggleClicked();
menu.btns[0].toggleClicked();
menu.activeTool = 0;
}else if((e.getX() > 80 && e.getX() < 120) && (e.getY()) > 20 && e.getY() < 60){
menu.btns[menu.activeTool].toggleClicked();
menu.btns[1].toggleClicked();
menu.activeTool = 1;
}else if((e.getX() > 140 && e.getX() < 180) && (e.getY()) > 20 && e.getY() < 60){
menu.btns[menu.activeTool].toggleClicked();
menu.btns[2].toggleClicked();
menu.activeTool = 2;
}else if((e.getX() > 200 && e.getX() < 240) && (e.getY()) > 20 && e.getY() < 60){
menu.btns[menu.activeTool].toggleClicked();
menu.btns[3].toggleClicked();
menu.activeTool = 3;
}else if((e.getX() > 260 && e.getX() < 300) && (e.getY()) > 20 && e.getY() < 60){
menu.btns[menu.activeTool].toggleClicked();
menu.btns[4].toggleClicked();
menu.activeTool = 4;
}
mw.setActiveTool(menu.activeTool);
this.repaint();
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
}
Please help me I've read plents of documents about JPanel and drawing but I still don't understand ..
If I understand your question, you basically want some function from which you can
wait for user interaction and get information about it
draw something to your window
(I'm ignoring right now that you want to do it from the main method, since I'm pretty sure you'll be happy to do it from another method as well)
Basically you are facing two obstacles in that project:
Drawing in most windowing systems is event driven, i. e. if the user puts the window to front, it has to know how to draw its current state
User interaction is also event driven, i. e. you will get callbacks when the user clicks.
The first obstacle is easy to solve: Create a BufferedImage somewhere that both your UI code and the function can access, and draw inside it. (BufferedImage has a createGraphics method you can use to draw inside the image's buffer). Then the paintComponent method just draws the image whenever somebody puts the window to foreground.
The second obstacle is a bit harder. Basically you'll need threading and synchronization for it. It used to be a lot of tweaking with wait and notify and synchronized calls up to Java 1.4, but since Java 1.5 the synchronization (for this special case) can be handled by BlockingQueue class. So your UI code waits (with MouseListener) for a mouse click and adds the coordinates (as a java.awt.Point) to BlockingQueue, and your UI code will just have to wait for the next point whenever it needs one. You will still have a bit of experience using multithreaded applications (so you should know how to start a Thread and that a Thread cannot interact directly with the UI), so depending on your experience of Java it may be a steep learning curve - but certainly doable.
EDIT: I see you are already using some kind of synchronization for the mouse events, so probably the only parts you still need is starting a thread and drawing to a BufferedImage instead of the real window itself.
EDIT2: Here is a very simplified example that shows you how you can draw to a BufferedImage from a second thread. It will ask for coordinates and colors from the console (standard input) and paint them to the image, which will show in the JPanel. It is your task to combine this example with what you already have to move the mouse position across etc.
import java.awt.*;
import javax.swing.*;
import java.awt.image.BufferedImage;
import java.util.Scanner;
public class BufferedImagePainting extends JFrame {
public static void main(String[] args) {
BufferedImage img = new BufferedImage(800, 600, BufferedImage.TYPE_3BYTE_BGR);
JPanel drawPanel = new DrawPanel(img);
new InteractionThread(img, drawPanel).start();
new BufferedImagePainting(drawPanel);
}
public BufferedImagePainting(final JPanel drawPanel) {
super();
setLayout(new GridLayout(1, 1));
add(drawPanel);
pack();
setVisible(true);
}
private static class DrawPanel extends JPanel {
private BufferedImage img;
public DrawPanel(BufferedImage img) {
this.img = img;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(img.getWidth(), img.getHeight());
}
}
private static class InteractionThread extends Thread {
private BufferedImage img;
private JPanel drawPanel;
public InteractionThread(BufferedImage img, JPanel drawPanel) {
this.img = img;
this.drawPanel = drawPanel;
}
#Override
public void run() {
#SuppressWarnings("resource")
Scanner s = new Scanner(System.in);
while (true) {
System.out.println("Enter a draw color and a fill color, separated by spaces");
System.out.println("Enter colors as 6-digit hex number, i. e. 000000 = black, ffffff = white");
Color drawColor = new Color(Integer.parseInt(s.next(), 16));
Color fillColor = new Color(Integer.parseInt(s.next(), 16));
System.out.println("Enter coordinates in form x y width height, separated by spaces");
int x = s.nextInt();
int y = s.nextInt();
int w = s.nextInt();
int h = s.nextInt();
Graphics g = img.createGraphics();
g.setColor(fillColor);
g.fillRect(x, y, w, h);
g.setColor(drawColor);
g.drawRect(x, y, w, h);
g.dispose();
drawPanel.repaint();
}
}
}
}
You must set visibility of your JFrame. Try this in your main class.
public class Geogebra {
#SuppressWarnings("unused")
public static void main(String args[]){
Fenetre fen = new Fenetre();
fen.setVisible(true);
}}

How to draw disappearing rectangles in Java [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 9 years ago.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Improve this question
I want to make a program in Java. What I am trying to do is, when the user click the screen a small square is drawn. One by one ten more squares are displayed with the previous square in the center. When the sixth square is drawn the first disappears, when the seventh square is drawn the second square disappears etc. until all the squares are gone.
This is the MainActivity:
import java.awt.Color;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MainActivity {
public static int width = 900;
public static int height = width / 16 * 9;
static HandlerClass handler = new HandlerClass();
static JFrame window;
static JPanel windowInner;
public static void main(String[] args) {
window = new JFrame("Squarmony 1.0");
window.setSize(width, height);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
windowInner = new JPanel();
windowInner.setSize(width, height);
windowInner.setBackground(Color.BLACK);
window.add(windowInner);
windowInner.addMouseListener(handler);
}
private static class HandlerClass implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
GraphicsActivity g = new GraphicsActivity();
g.drawRectRipple(window.getGraphics(), e);
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
}
}
And here is the GraphicsActivity:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
public class GraphicsActivity {
public void drawRectRipple(Graphics g, MouseEvent e) {
g.setColor(Color.WHITE);
for (int i = 0; i <= 10; i++) {
for (int j = 0; j <= 250; j += 10) {
g.drawRect(e.getX() + j / 2, e.getY() - j / 2, j - j / 2, j + j / 2);
try {
Thread.sleep(250);
} catch (InterruptedException e1) {
}
}
}
}
}
How could I draw the rectangles one by one (like ripples) to the screen?
Thanks,
John
So there are probably a number of ways to achieve this, some easier, some harder.
Backgound:
Animation is the illusion of change over time. This means over a time period, some kind of change is expected to happen. In your case, some squares get painted, some don't.
Now, this suggests that we need some way to update our UI on a regular bases. Now you could use a Thread, but to be frank, there are easier ways.
Swing provides a Timer class that provides events on a regular time interval, this allows us to make changes to the UI safely from within the context of the Event Dispatching Thread.
This is important, because it is expected that all updates and modifications to the UI occur within the context of the EDT. Also, any action which blocks the EDT will prevent the UI from being updated. Check out Concurrency in Swing for more details.
Now, you could set a timer to tick at a predefined interval that meets your needs. Ie, you need 10 updates over a period of n seconds, but this begins to put you at a slight disadvantage, as all you calculations would be based on some known, concert value...While you can certainly do this, I prefer a more flexible solution.
So, instead, I've chosen to use a "percentage" based approach. That is, all my animation is based on knowing that at given percentage of the way through the cycle, something needs to happen. This makes it possible to have a variable time frame without adversely effecting the paint algorithm.
To achieve this, we need to know when the cycle started and how long the cycle is. This way we can calculate the duration a cycle has being running and the percentile of the cycle that is completed, for example
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Check to see if the cycle has being completed...
if (cycleStartedAt == -1) {
// The time that this cycle started
cycleStartedAt = System.currentTimeMillis();
}
// The amount of time this cycle has being running
long duration = System.currentTimeMillis() - cycleStartedAt;
// The progress through this cycle...
progress = (double)duration / (double)runningTime;
// Check for the completion of this cycle...
if (progress > 1.0d) {
// Reset..
cycleStartedAt = -1;
progress = 0;
}
repaint();
}
});
timer.setRepeats(true);
timer.start();
What does this actually provide us? The provides us the ability to model the animation based on percentage of time. For example...
private double[][] squares = {
{0, 0.30},
{0.075, 0.375},
{0.15, 0.45},
{0.225, 0.525},
{0.30, 0.60},
{0.30, 0.675},
{0.375, 0.75},
{0.45, 0.825},
{0.525, 0.90},
{0.6, 0.975},
};
This is a list of the 10 squares, each with a start and end percentage, indicating when each square should be visible.
And finally, a runnable example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Squares {
public static void main(String[] args) {
new Squares();
}
public Squares() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private int runningTime = 5000;
private long cycleStartedAt = -1;
private double progress = 0;
private double[][] squares = {
{0, 0.30},
{0.075, 0.375},
{0.15, 0.45},
{0.225, 0.525},
{0.30, 0.60},
{0.30, 0.675},
{0.375, 0.75},
{0.45, 0.825},
{0.525, 0.90},
{0.6, 0.975},
};
public TestPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (cycleStartedAt == -1) {
cycleStartedAt = System.currentTimeMillis();
}
long duration = System.currentTimeMillis() - cycleStartedAt;
progress = (double)duration / (double)runningTime;
if (progress > 1.0d) {
cycleStartedAt = -1;
progress = 0;
}
repaint();
}
});
timer.setRepeats(true);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics g2d = (Graphics2D) g.create();
int width = getWidth() - 1;
int height = getHeight() - 1;
int hGap = width / squares.length;
int vGap = height / squares.length;
int index = 0;
for (double[] square : squares) {
if (progress >= square[0] && progress <= square[1]) {
int sWidth = hGap * (index + 1);
int sHeight = vGap * (index + 1);
int x = (width - sWidth) / 2;
int y = (height - sHeight) / 2;
g2d.drawRect(x, y, sWidth, sHeight);
}
index++;
}
g2d.dispose();
}
}
}
Check out Performing custom painting and 2D Graphics for more details

Running fan using java

I need to simulate a running fan with 3 buttons(start, reverse,stop) and a scroll bar to control the speed.
I wrote a code but and their are no errors but its not working.
At first the class extended Jframe and the window that has the buttons and the arcs of the fan appeared but when it extended Japplet it didn't appear.
But it didn't work both ways.
package Ass3_10203038;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Adjustable;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Ass3_10203038 extends JApplet implements Runnable {
private static
Lock lock = new ReentrantLock();
Graphics fan;
JButton start = new JButton("Start");
JButton stop = new JButton("Stop");
JButton reverse = new JButton("Reverse");
JScrollBar speed = new JScrollBar();
Thread timer = new Thread();
int thr=50;
int strtpt=0;
JFrame frame= new JFrame();
#Override
public void run() {
repaint();
while (true) {
try {
Thread.sleep(thr);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public Ass3_10203038() {
final ArcsPanel arcs = new ArcsPanel();
JPanel p = new JPanel();
p.setSize(500, 500);
p.add(arcs);
// p.setSize(5000, 5000);
p.add(start);
p.add(stop);
p.add(reverse);
p.add(speed);
p.setLayout(new GridLayout());
frame.add(p);
add(frame);
frame.setTitle("Fan");
start.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
timer.start();
for(int x=strtpt;x<362;x++)
{if(x==361)
x=0;
else
arcs.initializex(x);
arcs.paintComponent(fan);
strtpt=x;
}
}
});
stop.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
lock.lock();
timer.start();
}
});
reverse.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
timer.start();
for(int x=strtpt;x>-1;x--)
{if(x==0)
x=360;
else
arcs.initializex(x);
arcs.paintComponent(fan);
strtpt=x;
}
}});
speed.addAdjustmentListener(new AdjustmentListener(){
public void adjustmentValueChanged(AdjustmentEvent ae){
try {
switch (thr){
case AdjustmentEvent.UNIT_INCREMENT:
Thread.sleep( thr+=2);
break;
case AdjustmentEvent.UNIT_DECREMENT:
Thread.sleep(thr-=2);
break;
}
int value = ae.getValue();
} catch (InterruptedException ex) {
Logger.getLogger(Ass3_10203038.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
/**
* Main method
*/
public static void main(String[] args) {
Ass3_10203038 window = new Ass3_10203038();
window.setSize(500, 500);
window.setLocation(50, 50); // Center the frame
// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
// The class for drawing arcs on a panel
class ArcsPanel extends JPanel {
// Draw four blades of a fan
public int initializex(int x){
return x;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
int xCenter = getWidth() / 2;
int yCenter = getHeight() / 2;
int radius = (int) (Math.min(getWidth(), getHeight()) * 0.4);
int x = xCenter - radius;
int y = yCenter - radius;
{
g.fillArc(x, y, 2 * radius, 2 * radius, 0+initializex(x), 30);
g.fillArc(x, y, 2 * radius, 2 * radius, 90+initializex(x), 30);
g.fillArc(x, y, 2 * radius, 2 * radius, 180+initializex(x), 30);
g.fillArc(x, y, 2 * radius, 2 * radius, 270+initializex(x), 30);
}
}
}
There are quite a few problems with this code, and I won't try to address all of them. However, I'll list the ones I notice and describe what is going wrong.
First off, your code crashes with an exception:
java.lang.IllegalArgumentException: adding a window to a container
This happens in the Ass3_10203038 constructor, where you try to add a JFrame to the new Ass3_10203038 instance (which is a JApplet, and thereby a container). Since the JFrame only holds a JPanel, you can get around this by directly adding the JPanel to the Ass3_10203038 instance. This will at least display your UI when running as Applet.
Next, clicking a button will generate a NullPointerException. This happens because you call paintComponent directly, passing in fan as the Graphics parameter - but you never initialize fan to anything in your code, so it is always null. As a solution, you should not call paintComponent directly, because you don't have a Graphics object that can be used to draw on the screen. The Swing system has this object, though, so we can ask it to repaint instead of calling paintComponent directly:
arcs.repaint();
Now the program goes into an infinite loop on the press of a button though. What happens is that this loop never ends:
for (int x = strtpt; x < 362; x++)
{
if (x == 361)
x = 0;
else
arcs.initializex(x);
arcs.repaint();
strtpt = x;
}
It always keeps looping because x never reaches 362. Given that you want the fan to spin continuously that makes a kind of sense, but since you are running this loop on the UI thread, it will freeze up the entire window. Apart from that, the fan would rotate really quickly, since there is no timing in this loop - it would go as fast as the computer can make it go, which is pretty fast indeed.
Apparently you tried to solve this problem by involving a Thread called timer:
timer.start();
However, since timer is just a blank Thread object this line does absolutely nothing (at least, nothing of any importance to your program). In order to run code in a thread, you have to either extend Thread and override the run method, or pass a Runnable to the constructor of Thread. However, I don't think raw Threads will get us anywhere here, since they still don't really solve the timing problem. I suggest you look into javax.swing.Timer.
Even with this out of the way though, your fan blades still don't move. This is because your paintComponent method never actually sees any angle as input. It looks like you try to pass in an angle by calling initializex(x) in the loop shown above and in the paintComponent method, but initializex does not do anything except return its parameter - whatever you pass in, you get back out immediately. So in the paintComponent method, initializex(x) simply returns the value of x. This means that instead of you code:
g.fillArc(x, y, 2 * radius, 2 * radius, 0 + initializex(x), 30);
You could just as well have written
g.fillArc(x, y, 2 * radius, 2 * radius, 0 + x, 30);
with exactly the same effect.
I hope this explains a few of the things that went wrong.
I fixed the code, but thank you so much
i Just needed to organize the logic in my head
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package assign3_10203038;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.image.ImageObserver;
import java.text.AttributedCharacterIterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
public class Assign3_10203038 extends JFrame {
private JButton start = new JButton("Start");
private JButton stop = new JButton("Stop");
private JButton reverse = new JButton("Reverse");
private JScrollBar speed = new JScrollBar();
private Thread timer;
private final ArcsPanel arcs;
public Assign3_10203038() {
arcs = new ArcsPanel();
JPanel p = new JPanel();
p.setSize(5000, 5000);
p.add(arcs);
p.add(start);
p.add(stop);
p.add(reverse);
p.add(speed);
p.setLayout(new GridLayout());
add(p);
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timer = new Thread(arcs);
timer.start();
}
});
speed.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent ae) {
System.out.println(ae.getValue());
arcs.speed(ae.getValue());
}
});
reverse.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
arcs.reverse();
}
});
stop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.stop();
}
});
}
public static void main(String[] args) {
Assign3_10203038 window = new Assign3_10203038();
// (new Thread(window)).start();
window.setSize(500, 500);
window.setLocation(50, 50); // Center the frame
// window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
class ArcsPanel extends JPanel implements Runnable {
// Draw four blades of a fan
int thr = 1000;
int i = -20;
public void run() {
while (true) {
starting_x = (starting_x + i) % 360;
repaint();
try {
Thread.sleep(thr);
} catch (InterruptedException ex) {
Logger.getLogger(Assign3_10203038.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public void reverse() {
i = -i;
}
public void speed(int p) {
thr = 1000 - (p += p) * 5;
}
int starting_x;
public void initializex(int x_val) {
starting_x = x_val;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
int xCenter = getWidth() / 2;
int yCenter = getHeight() / 2;
int radius = (int) (Math.min(getWidth(), getHeight()) * 0.4);
int x = xCenter - radius;
int y = yCenter - radius;
{
g.fillArc(x, y, 2 * radius, 2 * radius, 0 + starting_x, 30);
g.fillArc(x, y, 2 * radius, 2 * radius, 90 + starting_x, 30);
g.fillArc(x, y, 2 * radius, 2 * radius, 180 + starting_x, 30);
g.fillArc(x, y, 2 * radius, 2 * radius, 270 + starting_x, 30);
}
}
}

Categories