Skip to main content

Simple rectangle drawing & animation

5 replies [Last post]
Anonymous

Hello

I'm designing a swarm intelligence simulator as an msc project, and I need to visualize the theory and practice in a 2D environment. I'm talking about very simple things here. All I want to do is have an ant be represented by a small square, and a piece of food represented by a square of different color. Note, I have never done any 2D or 3D programming before.
So i created a class, which constructs a frame, and then tries to draw small squares on it. The squares that are drawn by a thread, which constantly deletes them and draws new ones, simulating animated behavior show fine. The squares drawn initially either disappear on resize, minimize, or sometimes do not show up at all. I know i'm doing something wrong, but I have spent 4 days trying to figure out what I'm not doing right and can't so far. Basically I cannot use paint or repaint, I need a method from the simulation class that will allow me to draw a small square at a given X,Y coordinate.
Here's my code below, if anyone would be nice enough to explain to me what I'm doing wrong I'd be grateful.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class sim2d extends Frame{
private final int height = 2;
private final int weight = 2;

//draw a new ant representation at X,Y
public void draw_ant(int x,int y){
Graphics temp_g = getGraphics();
Graphics g2d = (Graphics2D)temp_g;

g2d.setColor(Color.BLUE);
g2d.drawRect(x, y, weight, height);
g2d.fillRect(x, y, weight, height);
super.paint(temp_g);
}

//Move an ant from X,Y to new A,B
public void move_ant(int x,int y,int a, int b) throws InterruptedException{
Graphics temp_g = getGraphics();
Graphics g2d = (Graphics2D)temp_g;

g2d.clearRect(x, y, weight+1, height+1);
g2d.setColor(Color.BLUE);
g2d.drawRect(a, b, weight, height);
g2d.fillRect(a, b, weight, height);
super.paint(g2d);
}

@Override
public void paint(Graphics g){
super.paint(g);
g = getGraphics();
Graphics g2d = (Graphics2D)g;

}

/*
@Override
public void update(Graphics g) {
paint(g);
}
*/

public static void setNativeLookAndFeel(){
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception e) {
System.out.println("Error setting native LAF: " + e);
}
}

public sim2d(){

super("AntBot Simulation v.01.a");
setSize(800,600);
setResizable(false);
setVisible(true);
repaint(10);
setPreferredSize(new Dimension(800,600));
setBounds(0,0,800,600);

addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent e)
{
dispose();
System.exit(0);
}
}
);
}

Note: i have tried with repaint(); with super.paint(g2d); and various combinations, but none seem to work in a stable manner. I tested this code on a Linux desktop and a windows machine just to make sure it's an issue of other kind.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
cradle
Offline
Joined: 2004-09-29

vortex636,

I think you may have a fundamental misunderstanding of how the Swing painting model works. Whenever the framework needs your component to refresh part of the display, the framework calls the paintComponent() method, which is then responsible for redrawing everything (or at least what's in the clip).

In other words, when you draw a rect, say, it's not as if you're adding a rectangle to scene graph, where it will continue to get drawn as needed automatically. Instead, once it's painted, the underlying framework forgets about it, and if it needs you to refresh the display, you have to draw it (and everything else) from scratch. It doesn't remember that there's a rect there.

See http://download.oracle.com/javase/tutorial/uiswing/painting/problems.html for more common problems.

Since you're in a time crunch, this should get you started, I hope ...

(Hmm, looks like it might get truncated. See here:
http://cradle.brokenglass.com/misc/AntColony.java )

import java.awt.*;
import java.util.*;
import javax.swing.*;

public class AntColony extends JFrame {

class AntPanel extends JPanel {

public final static int NUM_ANTS = 100;
public final static int WIDTH = 400; // pixels
public final static int HEIGHT = 400; // pixels

private final static int SZ = 4; // ant side length, pixels

private Rectangle[] ants = new Rectangle[NUM_ANTS];

public AntPanel() {
Random rand = new Random();
for (int i =0; i < ants.length; i++) {
ants[i] = new Rectangle( rand.nextInt(WIDTH),
rand.nextInt(HEIGHT), SZ, SZ);
}

setPreferredSize( new Dimension( WIDTH, HEIGHT));
setBackground( Color.WHITE );
}

private void checkID( int id) {
if (id < 0 || id >= ants.length)
throw new IllegalArgumentException( "Bad ant id: " + id);
}

public synchronized Dimension getAntLocation( int id) {
checkID( id);
return new Dimension( ants[id].x, ants[id].y );
}

public synchronized void setAntLocation( int id, int x, int y) {
checkID( id);
// Java % operator doesn't do what we want for neg. numbers
if (x < 0)
x = (Math.abs(x)/WIDTH + 1)*WIDTH + x;

if (y < 0)
y = (Math.abs(y)/HEIGHT + 1)*HEIGHT + y;

ants[id].setLocation( x % WIDTH, y % HEIGHT); // periodic boundaries
}

public synchronized void moveAnt( int id, int dx, int dy) {
checkID( id);
setAntLocation( id, ants[id].x + dx, ants[id].y + dy);
}

@Override
protected void paintComponent( Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.clearRect(0, 0, WIDTH, HEIGHT);

g2.setColor( Color.RED);
for( int i=0; i < ants.length; i++)
g2.fill( ants[i]);
}
}

private AntPanel antPanel = new AntPanel();
boolean keepRunning = true;
private Random rand = new Random();

public AntColony() {
getContentPane().add( antPanel);
setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE);
}

public void updateLoop() {
while (keepRunning) {
for (int i=0; i < AntPanel.NUM_ANTS; i++) {
antPanel.moveAnt( i, rand.nextInt(5) - 2, rand.nextInt(5) - 2);
}
antPanel.repaint();
try {Thread.sleep( 50 );} catch (Exception ex) {};
}
}

public void startAnimation() {
Thread animationThread = new Thread(
new Runnable() {
public void run() {
updateLoop();
}
}
);
animationThread.start();
}

public static void createAndShowGUI() {
final AntColony antWindow = new AntColony();
antWindow.pack();
antWindow.setLocationByPlatform( true);
antWindow.setVisible( true);

antWindow.startAnimation();
}

/**
* @param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}

cradle
Offline
Joined: 2004-09-29

You don't seem to be calling draw_ant() or move_ant(), so that's dead code.

I'd start by making sure you understand the material in the "Performing Custom Painting" lesson in the Swing Tutorial:

http://download.oracle.com/javase/tutorial/uiswing/painting/index.html

For animation, you might want to take a look at these two blog posts:

http://weblogs.java.net/blog/chet/archive/2006/02/make_your_anima.html
http://today.java.net/pub/a/today/2006/02/23/smooth-moves-solutions.html

Alternately, look into the Processing language (http://processing.org). It runs on the Java VM, and is essentially a higher-level api for doing 2D and 3D graphics and animations.

Here's an example that is relevant to what you're doing (in this case, it's a swarm of fish):
http://processing.org/exhibition/works/pond/index_link.html

Here's a better example, of flocking birds (and with source code):

http://processing.org/learning/topics/flocking.html

Good luck.

imagero
Offline
Joined: 2003-11-18

You need BufferedImage here. Draw your ants to Graphics object of BufferedImage. Don't extend Frame or JFrame. Extend JPanel instead.
Override paintComponent(Graphics g) method of JPanel to draw your BufferedImage.

vortex636
Offline
Joined: 2010-10-22

Thank you for the replies :)

Imagero do you mean something like the following?:

public void draw_food(int x,int y){
BufferedImage temp_G = new BufferedImage(400, 400, BufferedImage.TYPE_INT_RGB);
Graphics2D createGraphics = temp_G.createGraphics();
createGraphics.setColor(Color.red);
createGraphics.drawRect(x, y, weight, height);
createGraphics.fillRect(x, y, weight, height);
super.paint(createGraphics);
}

Still nothing at all...
I've tried with JPanel instead, but no go...
Can anyone explain to me why sometimes the squares are drawn and other times they are not ?

Cradle, thanx for the info. Sadly I've done most part of the project in Java so its not really an option doing it in another language.
The methods you see are being called from another class and put into threads. Once they are put into a thread, they work fine, im guessing because they are being redrawn all the time?
My problem is with the squares that are drawn once only in the beggining. They are the ones to dissapear or not show up at all.

imagero
Offline
Joined: 2003-11-18

No. I mean something like this:

public class MyPanel extends JPanel {

BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
//draw to this graphics
Graphics2D g = bi.createGraphics();

@override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(bi, 0, 0, null);
}

@override
public Dimension getPreferredSize() {
return new Dimension(w, h);//size of your image
}
}

public static void main(String [] args) {
MyPanel myPanel = new MyPanel();
JFrame frame = new JFrame();
frame.getContentPane().add(myPanel);
frame.setVisible(true);
frame(pack);
}

if you need to update your image constanly, make your panel implement Runnable
and put all your computations (e.g. ants moving) in
public void run() {}

Then start it with new Thread(myPanel).start();