undo method for a paint program (Java) - java

I'm trying to create an undo method for a basic paint program. Currently, you can change size and color of the brush, and erase. I'm trying to undo by saving the previous screen as an image (last), and when undo is called, painting that image. I've tried a few things, but nothing has worked. Simply drawing the image "last" creates an identical effect to the clear method. Any ideas?:
import java.applet.*;
import java.util.*;
import java.awt.*;
import java.lang.Object.*;
public class Paint extends Applet
{
private int x;
private int y;
private int size = 10;
private int sides = 200;
private int color = 0;
private Rectangle red, orange, yellow, green, blue, purple, pink, black;
private Rectangle triangle, square, pentagon, hexagon, octagon, circle;
private Rectangle small, medium, large;
private Rectangle eraser, clear, undo;
private Rectangle menuBar;
private Image last;
private Graphics g2;
//defines rectangles
public void init()
{
setSize(400,600);
red = new Rectangle(0,0,25,25);
orange = new Rectangle(0,25,25,25);
yellow = new Rectangle(0,50,25,25);
green = new Rectangle(0,75,25,25);
blue = new Rectangle(0,100,25,25);
purple = new Rectangle(0,125,25,25);
pink = new Rectangle(0,150,25,25);
black = new Rectangle(0,175,25,25);
triangle = new Rectangle(0,200,25,25);
square = new Rectangle(0,225,25,25);
pentagon = new Rectangle(0,250,25,25);
hexagon = new Rectangle(0,275,25,25);
octagon = new Rectangle(0,300,25,25);
circle = new Rectangle(0,325,25,25);
small = new Rectangle(0,355,25,25);
medium = new Rectangle(0,370,50,50);
large = new Rectangle(0,420,100,100);
eraser = new Rectangle(0,520,50,25);
clear = new Rectangle(0,545,60,30);
undo = new Rectangle(0,575,60,30);
menuBar = new Rectangle(0,0,70,650);
}
//paints the blocks of color in the menu bar
public void paintColors(Graphics g)
{
g.setColor(Color.red);
g.fillRect(0,0,25,25);
g.setColor(Color.orange);
g.fillRect(0,25,25,25);
g.setColor(Color.yellow);
g.fillRect(0,50,25,25);
g.setColor(Color.green);
g.fillRect(0,75,25,25);
g.setColor(Color.blue);
g.fillRect(0,100,25,25);
g.setColor(new Color(160,32,240));
g.fillRect(0,125,25,25);
g.setColor(Color.pink);
g.fillRect(0,150,25,25);
g.setColor(Color.black);
g.fillRect(0,175,25,25);
}
//paints the shapes, eraser, clear, and undo in the menu bar
public void paintShapes(Graphics g)
{
g.setColor(Color.black);
Utility.fillTri(g,12,212,25);
g.fillRect(2,227,20,20);
Utility.fillPent(g,12,262,25);
Utility.fillHex(g,12,287,25);
Utility.fillOct(g,12,312,25);
Utility.fillPoly(g,12,337,25,300);
g.fillOval(2,355,10,10);
g.fillOval(2,370,50,50);
g.fillOval(2,420,100,100);
g.setColor(Color.black);
g.drawRect(1,521,52,26);
g.setColor(Color.pink);
g.fillRect(2,522,40,25);
g.setColor(Color.black);
g.setFont(new Font("Arial",Font.PLAIN,20));
g.drawString("CLEAR",2,580);
g.drawString("UNDO",2,610);
}
public void paint(Graphics g)
{
g2 = getGraphics();
g2.setColor(Color.white);
g2.fillRect(0,0,60,getHeight());
paintColors(g2);
paintShapes(g2);
draw(g2);
}
public void draw(Graphics g)
{
getColor(g);
Utility.fillPoly(g,x,y,size,sides); //fills a regular polygon with specified center, size, and number of sides
}
public boolean mouseDown(Event e, int xx, int yy)
{
x = xx;
y = yy;
if(red.inside(xx,yy))
color = 0;
else if(orange.inside(xx,yy))
color = 1;
else if(yellow.inside(xx,yy))
color = 2;
else if(green.inside(xx,yy))
color = 3;
else if(blue.inside(xx,yy))
color = 4;
else if(purple.inside(xx,yy))
color = 5;
else if(pink.inside(xx,yy))
color = 6;
else if(black.inside(xx,yy))
color = 7;
if(triangle.inside(xx,yy))
sides = 3;
else if(square.inside(xx,yy))
sides = 4;
else if(pentagon.inside(xx,yy))
sides = 5;
else if(hexagon.inside(xx,yy))
sides = 6;
else if(octagon.inside(xx,yy))
sides = 7;
else if(circle.inside(xx,yy))
sides = 200;
if(small.inside(xx,yy))
size = 10;
else if(medium.inside(xx,yy))
size = 50;
else if(large.inside(xx,yy))
size = 100;
if(eraser.inside(xx,yy))
color = 8;
if(clear.inside(xx,yy))
clear(g2);
else if(undo.inside(xx,yy))
undo(g2);
if(!menuBar.inside(xx,yy))
last = createImage(getWidth(),getHeight());
return true;
}
public boolean mouseDrag(Event e, int xx, int yy)
{
x = xx;
y = yy;
if(!menuBar.inside(xx,yy))
repaint();
return true;
}
public void update(Graphics g)
{
paint(g);
}
public void clear(Graphics g)
{
color = 8;
getColor(g);
g.fillRect(0,0,getWidth(),getHeight());
color = 0;
repaint();
}
public void undo(Graphics g)
{
{
public int getColor(Graphics g)
{
switch(color){
case 0: g.setColor(Color.red);
break;
case 1: g.setColor(Color.orange);
break;
case 2: g.setColor(Color.yellow);
break;
case 3: g.setColor(Color.green);
break;
case 4: g.setColor(Color.blue);
break;
case 5: g.setColor(new Color(160,32,240));
break;
case 6: g.setColor(Color.pink);
break;
case 7: g.setColor(Color.black);
break;
case 8: g.setColor(new Color(238,238,238));
break;
}
return color;
}
}

I strongly recommend to use some variant of Command pattern to handle history.
Swing has a simple history manager named UndoManager. Generally it's used with text editors, but it works fine also with custom commands.
If you don't want to use Swing or UndoManager is not fit to your requirements, then make a try with an alternative standalone solution or implement your own. I also implemented my own for a large cross platform application.
So, you should wrap all your editing methods to command classes which implement a common interface (e. g. Command or UndoableEdit) and have methods for "do" and "undo".
It is harder to implement commands for graphics then for text documents where you need to store only minimal information about the edit. Store the changed area's rectangle from the original image on "do" and restore on "undo".

You add to the undo list on every mousedown even if the mousedown doesn't change the image.
So, when you click on the undo option you first save the image and then restore that same image.
You should only save to the undo list just before the image actually gets modified by the user.

Related

When drawing multiple lines in applet, it shows only the last one

I am trying to draw multiple lines by using Java applet and canvas. I have defined class Canvas:
public class Canvas extends JPanel {
private static final int RIGHT=0, LEFT=1, UP=2, DOWN=3;
public static final int WIDTH=600, HEIGHT=500;
private int direction = 0 ;
private int pixels;
/**
* Canvas() constructor sets its size
*/
public Canvas() {
setSize(WIDTH, HEIGHT);
}
public void setPatt(int pat, int lev) {
direction = pat;
pixels = lev;
}
public void paintComponent(Graphics g) {
g.setColor(getForeground());
switch (direction) {
case LEFT:
drawLineLeft(g, pixels);
break;
case RIGHT:
drawLineRight(g, pixels);
break;
case UP:
drawLineUp(g, pixels);
break;
case DOWN:
drawLineDown(g, pixels);
break;
}
}
private void drawLineLeft(Graphics g, int pix){
if(pix > 0){
Dimension d = getSize();
int x = d.width/2;
int y = d.height/2;
g.drawLine(x, y, x-10*pix, y);//left
}
}
private void drawLineUp(Graphics g, int pix){
if(pix > 0){
Dimension d = getSize();
int x = d.width/2;
int y = d.height/2;
g.drawLine(x, y, x, y-10*pix);//up
}
}
private void drawLineRight(Graphics g, int pix){
//Graphics2D g2 = (Graphics2D) g;
if(pix > 0){
Dimension d = getSize();
int x = d.width/2;
int y = d.height/2;
g.drawLine(x, y, x+10*pix, y);//right
}
}
private void drawLineDown(Graphics g, int pix){
if(pix > 0){
Dimension d = getSize();
int x = d.width/2;
int y = d.height/2;
g.drawLine(x, y, x, y+10*pix);// down
}
}
}
I also have another class where I have defined drop down list, text field and button. I can choose direction from drop down list in which line should be painted (RIGHT, LEFT, UP, DOWN - direction) and I can define how long the line will be by typing a number in text field (pixels). When the button is pushed, method setPatt is called and line appears in canvas:
public class TurtleApplet extends JApplet implements ActionListener
{
.....
.....
.....
public void actionPerformed( ActionEvent e)
{
if(e.getSource() == drawButton){
int y = Integer.parseInt(pixels.getText());
canvas.setPatt(direction.getSelectedIndex(), Integer.parseInt(pixels.getText()));
}
//repaint();
}
.....
}
The problem is that when I push the button new line appears in canvas only then when I resize applet window. I tried to add repaint() method, this helped, but still there is another problem - how to make draw new line without losing the previous one?
Only the last line is visible on the screen, for example, when I draw a line to the left from center (line always starts from the center of canvas) and then I want to draw a new line to the right from the center, the line first line disappears and I can see only the second one and so on.
You should keep track of lines drawn already. The paintComponent method supposed to print (or reprint) the entire component. As far as I see you always print just 1 line, therefore you loose the previous state.
Try to create a Map<Integer, Integer> where you store the line data and iterate through in the paintComponent method to draw all lines.
The method setPatt may be called addPatt instead.
PS: If the order matters, try LinkedHashMap

Java - Making gradient paint brush

I made a java paint application and I made a rainbow brush function; however, I want to make the randomized colors into a smooth gradient. It is currently just printing ovals of differently colors and you can notice each distinct oval. Is there a way to make it a gradient?
Paint Project - CLICK HERE TO SEE PROGRAM
My Rainbow Fuction:
public void rainbow() {
Random generator = new Random();
int r = generator.nextInt(256);
int g = generator.nextInt(256);
int b = generator.nextInt(256);
Color color = new Color(r, g, b);
g2.setPaint(color);
}
My Mouse Listeners:
public DrawArea() {
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
// save coord x,y when mouse is pressed
oldX = e.getX();
oldY = e.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
// coord x,y when drag mouse
currentX = e.getX();
currentY = e.getY();
if (g2 != null) {
// draw oval if g2 context not null
g2.drawOval(oldX, oldY, 40, 40);
g2.fillOval(oldX, oldY, 40, 40);
// refresh draw area to repaint
repaint();
// store current coords x,y as olds x,y
oldX = currentX;
oldY = currentY;
}
}
});
}
Paint Component:
public void paintComponent(Graphics g) {
if (image == null) {
image = createImage(getSize().width, getSize().height);
g2 = (Graphics2D) image.getGraphics();
clear();
}
// enable antialiasing
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(image, 0, 0, null);
}
I presume you're calling your rainbow function every ticks, which is why you get the effect as such.
To create the illusion of having a gradient, the r,g,b values must be changing slow enough to have the desired effect.
One way you can do that is by storing the value you want to LERP (see: https://en.wikipedia.org/wiki/Linear_interpolation) to.
//declare a variable to store the destinated Color;
public Color colorToBeLerped = null;
public Color currentColor = null;
public Color originalColor = null;
public final float deltaFactor = 0.2;
public void rainbow() {
if(currentColor==null||currentColor.equals(colorToBeLerped)){
Random generator = new Random();
int r = generator.nextInt(256);
int g = generator.nextInt(256);
int b = generator.nextInt(256);
colorToBeLerped = new Color(r, g, b);
originalColor = colorToBeLerped;
}
if(currentColor==null){
currentColor=colorToBeLerped;
}else{
//using the Color constructor that accepts float arguments, divide 255
currentColor= new Color((colorToBeLerped.getRed()-originalColor.getRed()*deltaFactor)/255,
(colorToBeLerped.getGreen()-originalColor.getGreen()*deltaFactor)/255,
(colorToBeLerped.getBlue()-originalColor.getBlue()*deltaFactor)/255);
}
g2.setPaint(currentColor);
}
Explaination:
1.Keep track of the color you want to lerp to, the current color you are in and the original color when the randomize function is called.
2.When we the first time rainbow function is called, current color will be set to the randomized color.
3.For every tick, if the current Color is not the destination color, we will increment by 1/5 of how much the difference is between original Color and destination Color.
4.A constant delta factor of 0.2 means that we will need 5 ticks to get from one color to another color, the lesser this variable is, the longer it will get from your original color to the destination color.
5.If we have reached the color, we will elect another new destination color.
*Not tested but I think you can figure out the rest

Java Applet adding thread makes while loop infinite

I am trying to make a program that generates 25 random ovals then draw a ball and make it bounce, I got it somewhat done, I generated the ovals and I got the ball to move but when I added the thread it kept repeating the draw oval loop, I get somewhat why this is happening but I have no idea how to fix it.
Basically my program should:
draw 25 random sized ovals on random locations within the border - completed
draw a ball and make it move - completed
make the ball bounce - not done but I know how to do it
but it keeps repeating step one.
this is my code that I have written, oh and I have to use applets right now its part of the course please don't suggest I use swing or something else:
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
public class As4B extends Applet implements Runnable
{
public int x, y;
public int width = 854;
public int height = 480;
public int border = 20;
public Image offscreen;
public Graphics d;
public void init()
{
setSize(width,height);
Thread th = new Thread(this);
th.start();
offscreen = createImage(width,height);
d = offscreen.getGraphics();
}
public void run()
{
x = 100;
y = 100;
while(true)
{
x ++;
y ++;
repaint();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void paint(Graphics gfx)
{
d.setColor(java.awt.Color.black);
d.fillRect(0, 0, width, height);
d.setColor(java.awt.Color.green);
d.fillRect(0 + border, 0 + border, (width - (border * 2)), (height - (border* 2)));
genOval(25, d);
d.setColor(Color.gray);
d.fillOval(x, y, 50, 50);
gfx.drawImage(offscreen, 0, 0, this);
}
public int random(int low, int high)
{
int answer =(int)((Math.random()*(high-low))+ low);
return answer;
}
public void genOval(int amount, Graphics f)
{
int ranWidth, ranHeight, ranX, ranY, red, blue, green;
int i = 0;
while(i < 25)
{
green = random(0,255);
blue = random(0,255);
red = random(0,255);
f.setColor(new Color(red,green,blue));
ranWidth = random(30,400);
ranHeight = random(30,200);
ranX = random(0 + border, ((width - border)- (ranWidth)));
ranY = random(0 + border , ((height - border)- (ranHeight)));
f.fillOval(ranX, ranY, ranWidth, ranHeight);
i++;
}
}
public void update(Graphics gfx) {
paint(gfx);
}
}
Your genOval() method has no persistent backing. Every time repaint() is called (by your thread), the paint() method is called, and this generates new locations for the random ovals. You need to create a persistent source for that information, like so:
List<Rectangle> rectangles = new ArrayList<Rectangle>();
List<Color> colors = new ArrayList<Color>();
public void init() {
...
for (int i = 0; i < 25; i++) {
int green = random(0,255);
int blue = random(0,255);
int red = random(0,255);
colors.add(new Color(red,green,blue));
ranWidth = random(30,400);
ranHeight = random(30,200);
ranX = random(0 + border, ((width - border)- (ranWidth)));
ranY = random(0 + border , ((height - border)- (ranHeight)));
rectangles.add(new Rectangle(ranX, ranY, ranWidth, ranHeight));
}
}
public void genOval(Graphics g) {
for (int i = 0; i < 25; i++) {
Color color = colors.get(i);
Rectangle rectangle = rectangle.get(i);
// Draw using color & rectangle
}
}

Cutting part of an image out and keeping the original image

So I have a black image that acts as darkness (In my game). I want to show a small portion around the character only. Like so
The red square is the player.
The green bit is the ground in the game (grass).
The black is the shadow/darkness.
What I want to do is cut a Ellipse/hole out of the blank, black image. I want this Ellipse to be centered around the players (The red square) x and y position.
Currently I am using this to get the effect:
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
//draw mask
g.setColor(Color.black);
if(!nearPlayer(x, y)) {
g.drawLine(x, y, x, y);
}
}
}
But, this processes extremely slow and laggs the players movement drastically.
Is this possible?
..the Player is the red square. Basically what i want to do is cut a circle out of the blank, black image around the coordinates of the player relative to said black image.
What DYM by 'black image' - exactly? To me that just looks like the BG is painted black, which would make more sense for any solid color. In that case, just create the red thing using an Area, fill it, then for the border set a stroke & the color to black, and draw it. This example shows how.
The relevant part of that short code is..
public void paintDaisyPart(Graphics2D g, Area daisyArea) {
g.setClip(daisyArea);
g.setColor(Color.YELLOW);
g.fillRect(0, 0, 200, 200);
g.setColor(Color.YELLOW.darker());
g.setClip(null);
g.setStroke(new BasicStroke(3));
g.draw(daisyArea);
}
I must be bored. This is an animated SSCCE version of the code that drew the image above. It is typically showing >130 FPS. And that is on a clunky machine for which I told the guy my spec. was 'cheap' & reminded him twice that I don't play (heavy rendering) games.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class DaisyDisplay {
DaisyDisplay() {
JPanel gui = new JPanel(new BorderLayout(2,2));
final BufferedImage daisy = new BufferedImage(
200,200,BufferedImage.TYPE_INT_RGB);
final JLabel daisyLabel = new JLabel(new ImageIcon(daisy));
gui.add(daisyLabel,BorderLayout.CENTER);
final Daisy daisyPainter = new Daisy();
daisyPainter.setSize(200);
final JLabel fps = new JLabel("FPS: ");
gui.add(fps,BorderLayout.SOUTH);
ActionListener animator = new ActionListener() {
int counter = 0;
long timeLast = 0;
long timeNow = 0;
public void actionPerformed(ActionEvent ae) {
Graphics2D g = daisy.createGraphics();
g.setColor(Color.GREEN.darker());
g.fillRect(0, 0, 200, 200);
daisyPainter.paint(g);
g.dispose();
daisyLabel.repaint();
counter++;
timeNow = System.currentTimeMillis();
if (timeLast<timeNow-1000) {
fps.setText("FPS: " + counter);
counter = 0;
timeLast = timeNow;
}
}
};
Timer timer = new Timer(1,animator);
timer.start();
JOptionPane.showMessageDialog(null, gui);
timer.stop();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new DaisyDisplay();
}
});
}
}
class Daisy {
double size = 200;
Point location;
double offset = 0.0;
public void paint(Graphics2D g) {
Area daisyArea = getDaisyShape();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
offset += .02d;
AffineTransform plain = g.getTransform();
g.setTransform(AffineTransform.getRotateInstance(
offset + (Math.PI*1/8),
100,100));
paintDaisyPart(g,daisyArea);
g.setTransform(AffineTransform.getRotateInstance(
offset + (Math.PI*3/8),
100,100));
paintDaisyPart(g,daisyArea);
g.setTransform(AffineTransform.getRotateInstance(
offset,
100,100));
paintDaisyPart(g,daisyArea);
g.setTransform(AffineTransform.getRotateInstance(
offset + (Math.PI*2/8),
100,100));
paintDaisyPart(g,daisyArea);
g.setTransform(plain);
}
public void setLocation(Point location) {
this.location = location;
}
public void paintDaisyPart(Graphics2D g, Area daisyArea) {
g.setClip(daisyArea);
g.setColor(Color.YELLOW);
g.fillRect(0, 0, 200, 200);
g.setColor(Color.YELLOW.darker());
g.setClip(null);
g.setStroke(new BasicStroke(3));
g.draw(daisyArea);
}
public void setSize(double size) {
this.size = size;
}
public Area getDaisyShape() {
int diameter = (int)size*6/20;
Ellipse2D.Double core = new Ellipse2D.Double(
(size-diameter)/2,(size-diameter)/2,diameter,diameter);
int pad = 10;
int petalWidth = 50;
int petalLength = 75;
Area area = new Area(core);
// left petal
area.add(new Area(new Ellipse2D.Double(
pad,(size-petalWidth)/2,petalLength,petalWidth)));
// right petal
area.add(new Area(new Ellipse2D.Double(
(size-petalLength-pad),(size-petalWidth)/2,petalLength,petalWidth)));
// top petal
area.add(new Area(new Ellipse2D.Double(
(size-petalWidth)/2,pad,petalWidth,petalLength)));
// bottom petal
area.add(new Area(new Ellipse2D.Double(
(size-petalWidth)/2,(size-petalLength-pad),petalWidth,petalLength)));
return area;
}
}

Dynamic Graphics Object Painting

Trying to figure out the best way to do this (And without crossing any specifics DO NOTs that I don't know about).
I'm working on visually displaying a graph (Various nodes, with edges connecting them) with circles and lines to represent such. Each node will be added during runtime and I can't hardcode this. From what I understand, all painting needs to be done in the paint(Graphics g) method - which isn't that helpful, since I can't be change the parameters and it seems this is only called during the initial creation?
Right now I was thinking about having it call various other methods, passing the Graphics object, and depending on other variables - I'll decide whether that's what I even want to call (Since the paint() method is the only one I can call).
Am I going about this completely wrong? Never bothered with this before.
To give you a better idea of what I want to end up with: I want to be able to pass the coordinates of the shape I want to add for the node, and then add it to whatever I have on the graph so far. And then same with the edges, I want to be able to pass the beginning and end point of the line to repaint on top of whatever is existing at that time.
Not exactly what I want right now - but you'll get the idea from what I patched together so far:
import java.awt.*;
import javax.swing.*;
public class MyCanvas extends Canvas
{
public MyCanvas()
{
}
public void paint(Graphics graphics)
{
// Keep this until I figured out if it's painted on load or not.
graphics.drawLine(10, 20, 350, 380);
}
public static void main(String[] args)
{
MyCanvas canvas = new MyCanvas();
JFrame frame = new JFrame();
int vertexes = 0;
// Change this next part later to be dynamic.
vertexes = 10;
int canvasSize = vertexes * vertexes;
frame.setSize(canvasSize, canvasSize);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(canvas);
frame.setVisible(true);
}
public void drawNode(int x, int y, Graphics g)
{
// Treat each location as a 10x10 block. If position 1,1 then go to (5,5) - If position 3,5 then go to (25, 45) eg: (x*10)-5, (y*10)-5
int xLoc = (x*10) - 5;
int yLoc = (y*10) - 5;
g.setColor(Color.white);
g.fillOval(xLoc, yLoc, 8, 8);
g.drawOval(xLoc, yLoc, 8, 8);
}
public void drawArc(int x, int y, int xx, int yy, Graphics g)
{
int xLoc = (x*10) - 5;
int yLoc = (y*10) - 5;
int xxLoc = (xx*10) - 5;
int yyLoc = (yy*10) - 5;
g.drawLine(xLoc, yLoc, xxLoc, yyLoc);
}
}
Edit: (Response for Andrew)
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class MyCanvas extends JPanel
{
public MyCanvas() {
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
}
public static void main(String[] args)
{
int vertexes = 0;
// Change this next part later to be dynamic.
vertexes = 10;
int canvasSize = vertexes * vertexes;
JFrame frame = new JFrame();
JLabel label = new JLabel();
BufferedImage bImage = new BufferedImage(canvasSize, canvasSize, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bImage.createGraphics();
g2d.drawLine(50, 50, 300, 300);
ImageIcon iIcon = new ImageIcon(bImage);
label.setIcon(iIcon);
frame.add(label);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
g2d = drawNode(1,1,g2d);
label.repaint();
}
public static Graphics2D drawNode(int x, int y,Graphics2D g2d)
{
// Treat each location as a 10x10 block. If position 1,1 then go to (5,5) - If position 3,5 then go to (25, 45) eg: (x*10)-5, (y*10)-5
int xLoc = (x*10) - 5;
int yLoc = (y*10) - 5;
g2d.setColor(Color.white);
g2d.fillOval(xLoc, yLoc, 8, 8);
g2d.drawOval(xLoc, yLoc, 8, 8);
return g2d;
}
public static void drawArc(int x, int y, int xx, int yy)
{
int xLoc = (x*10) - 5;
int yLoc = (y*10) - 5;
int xxLoc = (xx*10) - 5;
int yyLoc = (yy*10) - 5;
// g.drawLine(xLoc, yLoc, xxLoc, yyLoc);
}
}
There are various strategies you might pursue for this.
If the objects are never removed from the drawing once done, use a BufferedImage, put it in a (ImageIcon in a) JLabel. When it comes time to update:
Get the graphics instance of the image and draw the new element.
Dispose of the graphics object.
Call repaint() on the label.
Keep a list of the drawn elements. In the paint method, paint them all. When a new element is added, call repaint() on the rendering component.
Here is an example of the 1st technique:
import java.awt.image.BufferedImage;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
public class MyCanvas
{
JLabel view;
BufferedImage surface;
Random random = new Random();
public MyCanvas()
{
surface = new BufferedImage(600,400,BufferedImage.TYPE_INT_RGB);
view = new JLabel(new ImageIcon(surface));
Graphics g = surface.getGraphics();
g.setColor(Color.ORANGE);
g.fillRect(0,0,600,400);
g.setColor(Color.BLACK);
// Keep this until I figured out if it's painted on load or not.
g.drawLine(10, 20, 350, 380);
g.dispose();
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
addNewElement();
}
};
Timer timer = new Timer(200, listener);
timer.start();
}
public void addNewElement() {
boolean drawArc = random.nextBoolean();
int x = random.nextInt(60);
int y = random.nextInt(40);
Graphics g = surface.getGraphics();
if (drawArc) {
g.setColor(Color.BLUE);
int xx = random.nextInt(60);
int yy = random.nextInt(40);
drawArc(x,y,xx,yy,g);
} else {
drawNode(x,y,g);
}
g.dispose();
view.repaint();
}
public static void main(String[] args)
{
MyCanvas canvas = new MyCanvas();
JFrame frame = new JFrame();
int vertexes = 0;
// Change this next part later to be dynamic.
vertexes = 10;
int canvasSize = vertexes * vertexes;
frame.setSize(canvasSize, canvasSize);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(canvas.view);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public void drawNode(int x, int y, Graphics g)
{
// Treat each location as a 10x10 block. If position 1,1 then go to (5,5) - If position 3,5 then go to (25, 45) eg: (x*10)-5, (y*10)-5
int xLoc = (x*10) - 5;
int yLoc = (y*10) - 5;
g.setColor(Color.white);
g.fillOval(xLoc, yLoc, 8, 8);
g.drawOval(xLoc, yLoc, 8, 8);
}
public void drawArc(int x, int y, int xx, int yy, Graphics g)
{
int xLoc = (x*10) - 5;
int yLoc = (y*10) - 5;
int xxLoc = (xx*10) - 5;
int yyLoc = (yy*10) - 5;
g.drawLine(xLoc, yLoc, xxLoc, yyLoc);
}
}
Further tip
You might notice that the lines look quite 'jagged' & ugly. Both the BufferedImage or a JComponent has access to the more useful Graphics2D object (for the JComponent it is necessary to cast it in paintComponent()). A Graphics2D instance accepts rendering hints that can be used to smooth (dither) the elements drawn.

Categories