Graphics help please

R

Rexx Magnus

I've got some code below, in its early stages - my ultimate goal is to
be able to render pixel graphics to the screen, but I don't want to
render an entire image and then display it. I'd prefer to have it
shown as it draws, so that I can see the activity (for the time
being).
At a later stage I may want to render a chunk to the buffer and then
display it in one go (doublebuffering maybe).

How do I get it to render after the initial display of the applet? I
tried adding a loop where I draw the circle in the for loop, but it
only displays after it's finished. I assume it's because the loop is
running after the initial paint, but before the display is finally
rendered to the screen. What I need to know is how to run it outside
of this loop, and being a total java newbie, I can't see how to do it.

Suggestions as to how to rearrange this code would be greatly
appreciated. It took me long enough to find out how to render single
pixels directly without tons of code (lines with the same start and
end coordinates are not suitable for my ultimate goal).



import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.lang.Math.*;

public class Pixelgraphics extends JFrame
{
public static void main(String [] args)
{
new Pixelgraphics();
}

public Pixelgraphics()
{
this.setSize(300,300);
this.setTitle("Pixel Graphics");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

this.add(new PaintSurface(), BorderLayout.CENTER);
this.setVisible(true);
}

private class PaintSurface extends JComponent
{
public void paint(Graphics g)
{
//drawing code goes here
Graphics2D g2 = (Graphics2D)g;

BufferedImage buf = g2.getDeviceConfiguration
().createCompatibleImage(300, 300, Transparency.OPAQUE);

for(int i = 0; i <360; i++)
{
int x = 150 + (int)(20 * Math.sin(i));
int y = 150 + (int)(20 * Math.cos(i));
buf.setRGB(x, y, new Color(255,255, 255).getRGB());
g2.drawImage(buf,0,0,null);

// I want to be able to pause here to see the screen as it draws
}

}
}
}
 
K

Knute Johnson

Rexx said:
I've got some code below, in its early stages - my ultimate goal is to
be able to render pixel graphics to the screen, but I don't want to
render an entire image and then display it. I'd prefer to have it
shown as it draws, so that I can see the activity (for the time
being).
At a later stage I may want to render a chunk to the buffer and then
display it in one go (doublebuffering maybe).

How do I get it to render after the initial display of the applet? I
tried adding a loop where I draw the circle in the for loop, but it
only displays after it's finished. I assume it's because the loop is
running after the initial paint, but before the display is finally
rendered to the screen. What I need to know is how to run it outside
of this loop, and being a total java newbie, I can't see how to do it.

Suggestions as to how to rearrange this code would be greatly
appreciated. It took me long enough to find out how to render single
pixels directly without tons of code (lines with the same start and
end coordinates are not suitable for my ultimate goal).



import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.lang.Math.*;

public class Pixelgraphics extends JFrame
{
public static void main(String [] args)
{
new Pixelgraphics();
}

public Pixelgraphics()
{
this.setSize(300,300);
this.setTitle("Pixel Graphics");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

this.add(new PaintSurface(), BorderLayout.CENTER);
this.setVisible(true);
}

private class PaintSurface extends JComponent
{
public void paint(Graphics g)
{
//drawing code goes here
Graphics2D g2 = (Graphics2D)g;

BufferedImage buf = g2.getDeviceConfiguration
().createCompatibleImage(300, 300, Transparency.OPAQUE);

for(int i = 0; i <360; i++)
{
int x = 150 + (int)(20 * Math.sin(i));
int y = 150 + (int)(20 * Math.cos(i));
buf.setRGB(x, y, new Color(255,255, 255).getRGB());
g2.drawImage(buf,0,0,null);

// I want to be able to pause here to see the screen as it draws
}

}
}
}

You've got a bunch of problems. Drawing Swing components must occur in
the paintComponent() method not paint(). You can't pause in the
paintComponent() method and get it to work. So what you need to do is
keep the state somewhere else and draw it in the paintComponent(). You
can use an offline image as you did but it is not necessary. I use the
volatile variable 'angle' to store the current drawing state. Using a
separate thread to set the angle makes it really easy to have many
different threads doing different things. In the example below I start
the thread running right after setting the frame visible but it could
easily be started with a button.

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

public class test2 extends JPanel implements Runnable {
private volatile int angle;

public test2() {
setPreferredSize(new Dimension(300,300));
}

public void run() {
while (--angle >= -360) {
repaint();
try {
Thread.sleep(20);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}

public void paintComponent(Graphics g2D) {
Graphics2D g = (Graphics2D)g2D;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0,0,getWidth(),getHeight());
g.setColor(Color.BLUE);
g.drawArc(0,0,getWidth()-1,getHeight()-1,90,angle);
}

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test2 t2 = new test2();
f.add(t2);
f.pack();
f.setVisible(true);
new Thread(t2).start();
}
});
}
}
 
R

Rexx Magnus

You've got a bunch of problems. Drawing Swing components must occur in
the paintComponent() method not paint(). You can't pause in the
paintComponent() method and get it to work. So what you need to do is
keep the state somewhere else and draw it in the paintComponent(). You
can use an offline image as you did but it is not necessary. I use the
volatile variable 'angle' to store the current drawing state. Using a
separate thread to set the angle makes it really easy to have many
different threads doing different things. In the example below I start
the thread running right after setting the frame visible but it could
easily be started with a button.

Thanks, I'll give that a go and see if I can adapt it to my use.
 
R

Rexx Magnus

On Sat, 26 Jan 2008 20:31:18 -0000, Knute Johnson

Actually, I don't think the code below helps me much with what I want to
do (draw images pixel by pixel) as I don't want to clear the entire image
before displaying the changes, which is effectively what happens below. I
just used the circle as test code to render with pixels. How do I just add
to the image and then display that without clearing it each time?
 
K

Knute Johnson

Rexx said:
On Sat, 26 Jan 2008 20:31:18 -0000, Knute Johnson

Actually, I don't think the code below helps me much with what I want to
do (draw images pixel by pixel) as I don't want to clear the entire
image before displaying the changes, which is effectively what happens
below. I just used the circle as test code to render with pixels. How do
I just add to the image and then display that without clearing it each
time?

The JPanel is buffered so that it doesn't actually clear the screen just
the offscreen buffer. You can't draw a circle pixel by pixel with the
technique you used. It will produce gaps because it only draws a pixel
and every degree of angle and not the points in between.

It would be easy to modify the code I provided to draw on an image
instead of the JPanel. You still must do any delaying outside of the
paintComponent() method or you will block the Event Dispatch Thread and
none of the rest of your GUI will respond and it probably won't show
anything until it is finished.

So since you haven't told us exactly what you are going to display in
this slow drawing it is still my opinion that it is better to draw it in
paintComponent() unless there is some compelling reason not to.

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

public class test2 extends JPanel implements Runnable {
private final BufferedImage bi;
private volatile int angle;

public test2() {
bi = new BufferedImage(300,300,BufferedImage.TYPE_INT_ARGB);
setPreferredSize(new Dimension(300,300));
}

public void run() {
while (--angle >= -360) {
render();
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}

void render() {
Graphics2D g = bi.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0,0,getWidth(),getHeight());
g.setColor(Color.BLUE);
g.drawArc(0,0,getWidth()-1,getHeight()-1,90,angle);
g.dispose();
}


public void paintComponent(Graphics g) {
g.drawImage(bi,0,0,null);
}

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test2 t2 = new test2();
f.add(t2);
f.pack();
f.setVisible(true);
new Thread(t2).start();
}
});
}
}
 
R

Rexx Magnus

The JPanel is buffered so that it doesn't actually clear the screen
just the offscreen buffer. You can't draw a circle pixel by pixel with
the technique you used. It will produce gaps because it only draws a
pixel and every degree of angle and not the points in between.
It would be easy to modify the code I provided to draw on an image
instead of the JPanel. You still must do any delaying outside of the
paintComponent() method or you will block the Event Dispatch Thread and
none of the rest of your GUI will respond and it probably won't show
anything until it is finished.
So since you haven't told us exactly what you are going to display in
this slow drawing it is still my opinion that it is better to draw it in
paintComponent() unless there is some compelling reason not to.


I'm trying to write a basic engine that I can use for all sorts of
purposes, one of them being for rendering fractals, but it needs to be
visible as it's being drawn so you can abort if it ends up being slow
besides other things - and also so that I can plot graphs and render
fields to show various effects such as gravity.

I'm not bothered about lines being discontinuous - I'm just trying to
replicate some old experimental programming, similar to what I used to do
in BASIC. It's just annoying that there has to be so much set-up just to
do something that is quite simple in a lot of other languages.

A lot of the stuff I write doesn't actually have a definite end, so it's
just a case of watching it draw and stopping it when you're satisfied.
 
D

Daniele Futtorovic

I'm trying to write a basic engine that I can use for all sorts of
purposes, one of them being for rendering fractals, but it needs to
be visible as it's being drawn so you can abort if it ends up being
slow besides other things - and also so that I can plot graphs and
render fields to show various effects such as gravity.

I'm not bothered about lines being discontinuous - I'm just trying to
replicate some old experimental programming, similar to what I used
to do in BASIC. It's just annoying that there has to be so much
set-up just to do something that is quite simple in a lot of other
languages.

A lot of the stuff I write doesn't actually have a definite end, so
it's just a case of watching it draw and stopping it when you're
satisfied.

I don't really have much experience in this, but you may find it worth
your while to have a look at the ImageProducer/ImageConsumer pattern.

I've been googling it up a bit, and it appeared difficult to find a good
(and recent) presentation for it. The following article was about the
best I found... but I haven't spent all that much time on it:
<http://www.javaworld.com/javaworld/jw-03-1997/jw-03-howto.html?page=1>

You'll have to detach the image production off the event dispatch
thread, of course.

HTH,
df.
 
K

Knute Johnson

Rexx said:
I'm trying to write a basic engine that I can use for all sorts of
purposes, one of them being for rendering fractals, but it needs to be
visible as it's being drawn so you can abort if it ends up being slow
besides other things - and also so that I can plot graphs and render
fields to show various effects such as gravity.

I'm not bothered about lines being discontinuous - I'm just trying to
replicate some old experimental programming, similar to what I used to
do in BASIC. It's just annoying that there has to be so much set-up just
to do something that is quite simple in a lot of other languages.

A lot of the stuff I write doesn't actually have a definite end, so it's
just a case of watching it draw and stopping it when you're satisfied.

OK. Well then I would definitely use an offscreen buffer and draw it to
the onscreen component on some sort of regular schedule. You can put a
repaint() call in your drawing thread or use another thread or a timer.

This example puts a 1ms delay between drawing elements and draws the
offscreen image to the onscreen every 200ms. Depending on how much
processing it takes to produce your pixels, you might not need the
drawing delay.

Sometimes it really is better to provide a little more information up
front about what you are actually trying to do so as to get a better
answer here.

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

public class test2 extends JPanel implements ActionListener, Runnable {
private final BufferedImage bi;
private final Random random;
private final javax.swing.Timer timer;

public test2() {
bi = new BufferedImage(300,300,BufferedImage.TYPE_INT_ARGB);
random = new Random(new Date().getTime());
timer = new javax.swing.Timer(200,this);
setPreferredSize(new Dimension(bi.getWidth(),bi.getHeight()));
}

public void run() {
timer.start();
while (true) {
int x = random.nextInt(bi.getWidth());
int y = random.nextInt(bi.getHeight());
bi.setRGB(x,y,255<<24|255);
try {
Thread.sleep(1);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}

public void actionPerformed(ActionEvent ae) {
repaint();
}

public void paintComponent(Graphics g) {
g.drawImage(bi,0,0,null);
}

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test2 t2 = new test2();
f.add(t2);
f.pack();
f.setVisible(true);
new Thread(t2).start();
}
});
}
}
 
S

Stefan Ram

Rexx Magnus said:
I've got some code below, in its early stages - my ultimate goal is to
be able to render pixel graphics to the screen, but I don't want to
render an entire image and then display it. I'd prefer to have it
shown as it draws, so that I can see the activity (for the time
being).

I would like to try to summarize how to do animation.
This is somewhat daring, because I never actually wrote
anything like this myself:

The method that paints an on-screen area usually is called
»paintComponent«. Whenever it is being activated, it should
synchronize the model (internal state of the painting) to
the screen (output area).

To make sure that »paintComponent« is being activated,
one can call »repaint«.

For »paintComponent« to be executed on a regular base one
thread needs to call repaint() repeatedly. Usually this will
be done with the update frequency wanted.

Special care might be necessary if the update frequency should
be »as fast as possible«: Then the time between two calls of
»repaint()« should be significantly smaller then the run time
of a single paintComponent(). This run time, however, is not
known when writing the program. So, one might measure it at
run time and then call repaint() after 1/20 of it? Multiple
calls of repaint() will be coalesced by Swing when they arrive
while paintComponent() is still running, so that this should
be harmless.

The behavior of paintComponent() depends on whether this is a
continous animation depending on the time or a stepped
calculation proceeding in generations. In the first case, the
picture is calculated for the current time. In the second
case, the next generation is calculated and then being displayed.
Both, however, might conflict recommendations not to
do long calculations within the event-dispatch thread.

When the calculation of the next generation is being done
within »paintComponent«, one can assume a quite regular loop:
One execution of paintComponente does »calculate();
display();«; and fast triggering via »repaint« will start this
anew as soon as it has been finished. This results in a
calculate-display-iteration.

Another approach would be to draw into a memory buffer from an
extra thread. paintComponent() will be called whenever the
buffer has been changed significantly. When the programm does
not have to be fast, one might want to repaint() after every
pixel changed. If it should be fast, one might do several
modifications to the picture before the next »repaint« is
called. I.e., one might like to keep paintComponent from
taking to much processing time away from the other thread.

Two other notes:

One can add additional care not to repaint faster than the
hardware video refresh cylce for a full screen, or much faster
than the eyes can see because more speed would be wasted.

I believe, there also is a full-screen mode for Java, where
some rules may change. For example, one might not have to
worry about overlapping or moving windows in this case.
 
S

Stefan Ram

Knute Johnson said:
The JPanel is buffered so that it doesn't actually clear the
screen just the offscreen buffer. You can't draw a circle
pixel by pixel with the technique you used. It will produce
gaps because it only draws a pixel and every degree of angle
and not the points in between.

Alan Kay was teaching five-year old children how to
program a circle: They were asked to walk in a circle
and to report what they did. The children would answer
"walk a little and turn a little." After that cognition
they could write a program to draw a circle.

Ten-year old children already knew what a circle is:
"The set of all point, having the same distance to a
center." So they startet to program individual points
starting at a center, which was more complicated; and
the result was non a connected circle but only single
dots.

Fifteen-year old children already knew the formula »r² = x² + y²«.
They tried to draw a circle using that formula, but
failed. (This formula is not a good starting point for such a
program.) Just because of their additional knowledge, it was
actually more difficult or impossible for them to write such a
program. At least that is what Alan Kay said in a video.

This made me want to write a little Java program.

It draws a part of something like a circle without AWT or Swing.

The start of the program implements a little framework,
then the contents of the method »main« solely consists of
»Turtle commands«.

public class Turtle
{
static double x = 0;
static double y = 0;
static double stepsize = 1;
static double direction = 0;
static char[][] screen = new char[24][78];

static void turnALittle(){ direction += 10; }

static void walkALittle()
{ final double angle = direction / 360 * 2 * java.lang.Math.PI;
y = y + stepsize * java.lang.Math.sin( angle );
x = x + stepsize * java.lang.Math.cos( angle );
set( y, x ); }

static
{ for( int i = 0; i < 24; ++i )for( int j = 0; j < 78; ++j )
screen[ i ][ j ]= ' ';
java.lang.Runtime.getRuntime().addShutdownHook
( new Thread
( new java.lang.Runnable()
{ public void run()
{ for( int i = 0; i < 24; ++i )
{ for( int j = 0; j < 78; ++j )
java.lang.System.out.print( screen[ i ][ j ] );
java.lang.System.out.println(); }}})); }

static void set( final double y, final double x )
{ try{ screen[( int )( y + 11 )][( int )( x + 38 )]= '*'; }
catch( final Exception e ){} }

public static void main( final java.lang.String[] args )
{ walkALittle(); turnALittle();
walkALittle(); turnALittle();
walkALittle(); turnALittle();
walkALittle(); turnALittle();
walkALittle(); turnALittle();
walkALittle(); turnALittle();
walkALittle(); turnALittle();
walkALittle(); turnALittle();
walkALittle(); turnALittle();
walkALittle(); turnALittle();
walkALittle(); turnALittle();
walkALittle(); turnALittle(); }}

**
**
*
*
*
*
*
*
*
 
D

Daniele Futtorovic

I would like to try to summarize how to do animation.
This is somewhat daring, because I never actually wrote
anything like this myself:

The method that paints an on-screen area usually is called
»paintComponent«. Whenever it is being activated, it should
synchronize the model (internal state of the painting) to
the screen (output area).

To make sure that »paintComponent« is being activated,
one can call »repaint«.

For »paintComponent« to be executed on a regular base one
thread needs to call repaint() repeatedly. Usually this will
be done with the update frequency wanted.

Special care might be necessary if the update frequency should
be »as fast as possible«: Then the time between two calls of
»repaint()« should be significantly smaller then the run time
of a single paintComponent(). This run time, however, is not
known when writing the program. So, one might measure it at
run time and then call repaint() after 1/20 of it? Multiple
calls of repaint() will be coalesced by Swing when they arrive
while paintComponent() is still running, so that this should
be harmless.

The behavior of paintComponent() depends on whether this is a
continous animation depending on the time or a stepped
calculation proceeding in generations. In the first case, the
picture is calculated for the current time. In the second
case, the next generation is calculated and then being displayed.
Both, however, might conflict recommendations not to
do long calculations within the event-dispatch thread.

When the calculation of the next generation is being done
within »paintComponent«, one can assume a quite regular loop:
One execution of paintComponente does »calculate();
display();«; and fast triggering via »repaint« will start this
anew as soon as it has been finished. This results in a
calculate-display-iteration.

Another approach would be to draw into a memory buffer from an
extra thread. paintComponent() will be called whenever the
buffer has been changed significantly. When the programm does
not have to be fast, one might want to repaint() after every
pixel changed. If it should be fast, one might do several
modifications to the picture before the next »repaint« is
called. I.e., one might like to keep paintComponent from
taking to much processing time away from the other thread.

Two other notes:

One can add additional care not to repaint faster than the
hardware video refresh cylce for a full screen, or much faster
than the eyes can see because more speed would be wasted.

I believe, there also is a full-screen mode for Java, where
some rules may change. For example, one might not have to
worry about overlapping or moving windows in this case.

If you use an Image, and register the default ImageObserver in the
Graphics#drawImage call, to wit the Component itself, then theoretically
you won't have to bother about repaint() at all, since a call to
ImageObserver#imageUpdate should trigger a repaint, possibly with some
optimisations handled behind the scenes (? for the last point).

It would be interesting to know whether this also works if you use a
simple container for your Image, like a JLabel (ergo no need to bother
about paint(Graphics) or paintComponent(Graphics) at all). Although,
since I seem to recall seeing animated GIFs displayed with a JLabel, I
expect it to be the case.

DF.
 
D

Daniele Futtorovic

This made me want to write a little Java program.

It draws something like a circle without AWT or Swing.

Me too! Me too!

public class A {
public static void main(String[] ss){
System.out.println("O");
}
}

df.
 
K

Knute Johnson

Stefan said:
Special care might be necessary if the update frequency should
be »as fast as possible«: Then the time between two calls of
»repaint()« should be significantly smaller then the run time
of a single paintComponent(). This run time, however, is not
known when writing the program. So, one might measure it at
run time and then call repaint() after 1/20 of it? Multiple
calls of repaint() will be coalesced by Swing when they arrive
while paintComponent() is still running, so that this should
be harmless.

Actually this is usually a disaster for animation. Your image jerks and
jumps. Not good. The way around this is not to use repaint() unless
you are sure that it won't coalesce. Calling paintImmediately() is
usually better. Although if performance is critical there are other
methods such as active rendering that will significantly improve drawing
performance especially on Winblows.
 
K

Knute Johnson

Daniele said:
If you use an Image, and register the default ImageObserver in the
Graphics#drawImage call, to wit the Component itself, then theoretically
you won't have to bother about repaint() at all, since a call to
ImageObserver#imageUpdate should trigger a repaint, possibly with some
optimisations handled behind the scenes (? for the last point).

The user does not call imageUpdate(), it is called by the ImageObserver
and overridden by the user to detect changes in the image.
It would be interesting to know whether this also works if you use a
simple container for your Image, like a JLabel (ergo no need to bother
about paint(Graphics) or paintComponent(Graphics) at all). Although,
since I seem to recall seeing animated GIFs displayed with a JLabel, I
expect it to be the case.

DF.

You still have to have a call to Graphics.drawImage().
 
S

Stefan Ram

Knute Johnson said:
The way around this is not to use repaint() unless
you are sure that it won't coalesce.

The JDK demo, »Java2D« allows the user to set the sleep delays
between two calls of »repaint« via the slider control named
»Anim Delay =« (and, once, »Sleep =«).

The sleep delay can be reduced until »0 ms«, so one
can observe how the program behaves for this setting.
 
S

Stefan Ram

The JDK demo, »Java2D« allows the user to set the sleep delays
between two calls of »repaint« via the slider control named
»Anim Delay =« (and, once, »Sleep =«).

The access to "Sleep =" is tricky:

First, one needs to active the "Tools" check mark in the right
panel.

Next, one has to click with the right mouse button into the
background of the tools area below an animation.

Then "Sleep =" appears there and one can set the delay for
this animation only. This value is actually being used as
"sleepAmount" in

http://www.java2html.com/examples/Java2D_demo/java2d/AnimatingSurface.java.html

Actually, I was hoping to be able to learn from Sun
how they do animation in an »as fast as possible« mode.

But this demo seems to leave the choice of the delay
value to the user.
 
D

Daniele Futtorovic

The user does not call imageUpdate(), it is called by the
ImageObserver and overridden by the user to detect changes in the
image.

No, the user doesn't call imageUpdate(). But neither is the
ImageObserver likely to call it, given that it is a method of the
ImageObserver interface itself.

According to the docs:
"This method is called when information about an image which was
previously requested using an asynchronous interface becomes available"

The standard way of using Graphics#drawImage while performing custom
painting in a Component (which includes JComponents) is to pass that
method the Component itself as the ImageObserver. My comment was aiming
at the fact that the Component class, when its imageUpdate() method is
called, triggers a repaint. Consequently, if your code makes sure
ImageObserver#imageUpdate gets called, you won't have to bother yourself
with repaints at all. One way of getting that method called, following
my earlier reference to the ImageProducer/ImageConsumer pattern, would
be to write a proper, mayhap delaying, etc. ImageProducer, and let AWT
handle the rest.
You still have to have a call to Graphics.drawImage().

Not if you embed your Image in an ImageIcon and then in a JLabel, you don't.

Furthermore, to answer the question I asked myself, the docs for
ImageIcon#paintIcon(Component, Graphics, int, int) state:
"If this icon has no image observer, this method uses the c component as
the observer."

DF.
 
T

Tim Smith

This made me want to write a little Java program.

It draws a part of something like a circle without AWT or Swing.

The start of the program implements a little framework,
then the contents of the method »main« solely consists of
»Turtle commands«.

I've got something similar, with a 3D turtle. You can move it forward
with move(), and you can turn it with pitch(), roll(), and yaw(). When
you move it, you give it a graphics object, and it will trail a line as
it moves. You can give it null for the graphics object if you don't
want the line.

You can also draw a line from (x0,y0,z0) to (x1,y1,z1), with the
coordinates in the coordinate system of the turtle's frame of reference.
The normal way to use this would be to write a function that draws
something interesting (like a spaceship or a pterodactyl) using the
line() function. Since that is using the coordinate system of the
turtle, when you move the turtle (or change its pitch, yaw, or roll),
and draw your thing again, your thing will be moved and rotated
accordingly.

There's also functions in the turtle class to save and restore the
turtle position. These are meant to allow you to use the turtle itself
to draw interesting things, that you then move. Say, for example, you
wanted to make a flying saucer. You could draw it using line(), but
that is kind of a pain. So instead what you do is save the turtle's
position, and then you can draw the flying saucer's circle by moving and
turning the turtle, in addition to using line(). When done, you restore,
and the turtle is back to the starting position and orientation, so you
can them move and pitch and roll and yaw to get to the next position for
drawing your flying saucer.

Here's the code, as part of a program that draws a simple little
dohickey 4 times, once stationary, once flying away from the camera and
rolling, once doing a pitch loop, and once doing a yaw loop.

The program is called Boxes even though there are no boxes anywhere in
there. That's because the purpose of this was to do some experiments
with perspective. I was trying to understand why my drawing (the pencil
and paper kind, not the Java kind) didn't look right, and needed to see
some boxes at various angles and positions to understand what was going
on. I worked out my perspective puzzlement without needing the program,
so it never got past the turtle implementation and the dohickey loops
that were for debugging.

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

// Eye is at (0,0,eye)
// Projection plane is at z = 0, and its (0, 0) is in center of view
// Room's smaller of x and y axis goes from -50 to +50

public class Boxes extends JPanel
{
final int width = 1024;
final int height = 768;
RoomAndCamera env;
Random r;

JFrame frame;

static public void main( String args[] )
{
new Boxes();
}

Boxes()
{
r = new Random();
env = new RoomAndCamera( 100, width, height, -20 );

JFrame frame = new JFrame( "Perspective Test" );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setLocation( new Point(20,20) );
frame.setSize( width, height + 30 );
frame.setContentPane(this);

frame.setVisible(true);
}

public void paintComponent( Graphics g )
{
super.paintComponent(g);
draw_boxes(g);
}

public void dohickey( Graphics g, Turtle t )
{
g.setColor( Color.red );
t.line( g, -5, 0, 0, 5, 0, 0 );
g.setColor( Color.green );
t.line( g, 0, 0, 0, 0, 5, 0 );
g.setColor( Color.blue );
t.line( g, 0, 0, 0, 0, 0, 5 );
}

public void draw_boxes( Graphics g )
{
Turtle t[] = new Turtle[3];
t[0] = new Turtle( env, -3*env.size()/5, -3*env.size()/4,
env.size()/2 );
t[1] = new Turtle( env, -env.size()/3, 4*env.size()/5,
env.size()/2 );
t[2] = new Turtle( env, 3*env.size()/5, -3*env.size()/5,
env.size()/2 );

Turtle ref = new Turtle( env, 3*env.size()/4, 4*env.size()/5,
env.size()/2 );

dohickey( g, ref );

for ( int tn = 0; tn < 3; ++tn )
{
dohickey( g, t[tn] );

int len = 10;

for ( int i = 0; i < 20; ++i )
{
if ( tn == 0 )
t[tn].pitch( Math.PI/10 );
else if ( tn == 1 )
t[tn].roll( Math.PI/12 );
else if ( tn == 2 )
t[tn].yaw( Math.PI/10 );

t[tn].move( null, len );

dohickey( g, t[tn] );
}
}
}
}

class RoomAndCamera
{
double room_size;
int cx, cy;
double eye;
double conv;

RoomAndCamera( double size, int disp_w, int disp_h, double camloc )
{
room_size = size;
eye = camloc;
cx = disp_w / 2;
cy = disp_h / 2;
if ( disp_w < disp_h )
conv = disp_w / room_size;
else
conv = disp_h / room_size;
conv *= -eye;
}

int project_x( double x, double z )
{
return cx + (int)(conv * x / (z - eye));
}

int project_y( double y, double z )
{
return cy + (int)(conv * y / (z - eye));
}

double size() { return room_size; }
}

class Turtle
{
double x, y, z;
double vx[]; // x vector from object's point of view
double vy[]; // y vector from object's point of view
double vz[]; // z vector from object's point of view

double sx, sy, sz;
double svx[], svy[], svz[];

RoomAndCamera env;

Turtle( RoomAndCamera aenv, double ax, double ay, double az )
{
env = aenv;

x = ax;
y = ay;
z = az;

vx = new double[3];
vy = new double[3];
vz = new double[3];

vx[0] = 1; vx[1] = 0; vx[2] = 0;
vy[0] = 0; vy[1] = 1; vy[2] = 0;
vz[0] = 0; vz[1] = 0; vz[2] = 1;

mark();
}

Turtle( Turtle that )
{
env = that.env;

x = that.x;
y = that.y;
z = that.z;

vx = new double[3];
vy = new double[3];
vz = new double[3];

for ( int i = 0; i < 3; ++i )
{
vx = that.vx;
vy = that.vy;
vz = that.vz;
}

mark();
}

void mark()
{
sx = x;
sy = y;
sz = z;

svx = new double[3];
svy = new double[3];
svz = new double[3];
for ( int i = 0; i < 3; ++i )
{
svx = vx;
svy = vy;
svz = vz;
}
}

void restore()
{
x = sx;
y = sy;
z = sz;
for ( int i = 0; i < 3; ++i )
{
vx = svx;
vy = svy;
vz = svz;
}
}

void vector_rotate( double t, double v[], double w[] )
{
// v <- v cos(t) + w sin(t)
// w <- -v sin(t) + w cos(t)

double nv[] = new double[3];
double nw[] = new double[3];
int i;
for ( i = 0; i < 3; ++i )
{
nv = Math.cos(t) * v + Math.sin(t) * w;
nw = -Math.sin(t) * v + Math.cos(t) * w;
}
for ( i = 0; i < 3; ++i )
{
v = nv;
w = nw;
}
}

// spin around the x vector
void pitch( double t )
{
vector_rotate( t, vy, vz );
}

// spin around the y vector
void yaw( double t )
{
vector_rotate( t, vz, vx );
}

// spin around the z vector
void roll( double t )
{
vector_rotate( t, vx, vy );
}

void move( Graphics g, double dist )
{
boolean clip = false;
int x0 = env.project_x( x, z );
int y0 = env.project_y( y, z );
if ( z < 0 )
clip = true;
x += dist * vz[0];
y += dist * vz[1];
z += dist * vz[2];
int x1 = env.project_x( x, z );
int y1 = env.project_y( y, z );
if ( z < 0 )
clip = true;
if ( g != null && ! clip )
g.drawLine( x0, y0, x1, y1 );
}

void line( Graphics g, double x0, double y0, double z0, double x1,
double y1, double z1 )
{
double p0[] = new double[3];
double p1[] = new double[3];
for ( int i = 0; i < 3; ++i )
{
p0 = x0 * vx + y0 * vy + z0 * vz;
p1 = x1 * vx + y1 * vy + z1 * vz;
}
p0[0] += x; p1[0] += x;
p0[1] += y; p1[1] += y;
p0[2] += z; p1[2] += z;
int ix0 = env.project_x( p0[0], p0[2] );
int iy0 = env.project_y( p0[1], p0[2] );
int ix1 = env.project_x( p1[0], p1[2] );
int iy1 = env.project_y( p1[1], p1[2] );
if ( g != null && p0[2] >= 0 && p1[2] >= 0 )
g.drawLine( ix0, iy0, ix1, iy1 );
}
}
 
D

Daniele Futtorovic

No, the user doesn't call imageUpdate(). But neither is the
ImageObserver likely to call it, given that it is a method of the
ImageObserver interface itself.

According to the docs:
"This method is called when information about an image which was
previously requested using an asynchronous interface becomes available"

The standard way of using Graphics#drawImage while performing custom
painting in a Component (which includes JComponents) is to pass that
method the Component itself as the ImageObserver. My comment was aiming
at the fact that the Component class, when its imageUpdate() method is
called, triggers a repaint. Consequently, if your code makes sure
ImageObserver#imageUpdate gets called, you won't have to bother yourself
with repaints at all. One way of getting that method called, following
my earlier reference to the ImageProducer/ImageConsumer pattern, would
be to write a proper, mayhap delaying, etc. ImageProducer, and let AWT
handle the rest.


Not if you embed your Image in an ImageIcon and then in a JLabel, you
don't.

Furthermore, to answer the question I asked myself, the docs for
ImageIcon#paintIcon(Component, Graphics, int, int) state:
"If this icon has no image observer, this method uses the c component as
the observer."

DF.


The following code gives an example of what I mean:

package scratch;

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

/**
*
* @author da.futt
*/
public class ImageProducerTest {

private ImageProducerTest() {
}

public static void main(String[] ss){
JFrame f = new JFrame("ImageProducerTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

final ClockProducer cp = ClockProducer.newInstance(400, 0xfdfdfd);

JLabel lab = new JLabel(new ImageIcon( f.createImage(cp) ));
f.getContentPane().add(lab, BorderLayout.CENTER);

f.pack();
f.setLocationRelativeTo(null);

f.setVisible(true);

TimerTask tt = new TimerTask(){
public void run(){
cp.tick();
}
};

java.util.Timer t1 = new java.util.Timer(true);
t1.scheduleAtFixedRate(tt, 1000, 1000);
}


private static class ClockProducer
extends MemoryImageSource
{
static final int MARGIN = 25;

static int[] createBackground(int w, int h, int color){
int[] ret = new int[w * h];
Arrays.fill(ret, color);

return ret;
}

public static ClockProducer newInstance(int size, int background){
int[] pixels = createBackground(size, size, background);
return new ClockProducer(size, pixels);
}

int secondsrun = 0;
int size;
int[] points, pixels;

protected ClockProducer(int size, int[] pixels){
super(size, size, pixels, 0, size);

this.size = size;
this.pixels = pixels;

init0();
}

private void init0(){
setAnimated(true);
setFullBufferUpdates(false);

initPoints();
}

private void initPoints(){
int cxy = size >> 1;
int rad = (size - (MARGIN << 1)) >> 1;

points = new int[120];

double dg = Math.PI / 30;

double g = - Math.PI / 2;
for(int ii = 0; ii < points.length >> 1; ii++, g += dg){
points[2 * ii] = cxy + (int) Math.round(Math.cos(g) * rad);
points[2 * ii + 1] = cxy + (int) Math.round(Math.sin(g)
* rad);
}
}

public void tick(){
int x, y, off, col;
for(int ii = Math.max(0, secondsrun - 7); ii <= secondsrun;
ii++){
off = ii % (points.length >> 1);
if(off < 0) off += points.length >> 1;

x = points[2 * off];
y = points[2 * off + 1];

col = ((0xff >> (secondsrun - ii)) << 24);
for(int xx = x - 2; xx <= x + 2; xx++)
for(int yy = y - 2; yy <= y + 2; yy++)
pixels[xx + yy * size] = col;

this.newPixels(x-2, y-2, 5, 5, ii == secondsrun);
}

secondsrun++;
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,705
Latest member
Stefkari24

Latest Threads

Top