# 1
... any better solution to this problem than just setting some boolean flags and use
the if statements inside the paint method which will trigger a specific method that will
paint something on the JPanel.
There are two general approaches to this.
One is to use an (offscreen) image to do all the drawing on and draw the image during each
trip through paintComponent. All additions of geom primitives is done in event code by
drawing directly to the image. In pseudo杍ava:
BufferedImage image;
...
protected void paintComponent(Graphics g) {
if(image == null) initImage();
g.drawImage(image, 0, 0, this);
}
private void initImage() {
int w = getWidth();
int h = getHeight();
int type = BufferedImage.TYPE_INT_RGB;
image = new BufferedImage(w, h, type);
Graphics2D g2 = image.createGraphics();
g2.setBackground(getBackground());
g2.clearRect(0,0,w,h);
g2.dispose();
}
// In your event code, when you are ready to draw a new geom primitive
// update the image and call repaint.
Graphics2D g2 = (Graphics2D)image.getGraphics();
g2.draw(new_geom_primitive);
g2.dispose();
repaint();
For componentResized events you set the image to null and call repaint.
The other is to use one or more data structures to keep the geom primitives that have been
added and draw all of them during each trip through paintComponent.
All of the additions of geom primitives are done in event code followed by a call to
repaint to show the latest change(s).
Here's an example of this second approach. The extra methods for locating/scaling the new
additons are unnecessary frills; adding the new geom primitives and calling repaint from
within the event code are the essential parts.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
public class DrawingObjects extends JPanel implements ActionListener {
List<Shape> shapes;
Random seed;
final int R = 60;
final int MAX_SIZE_REDUCTIONS = 2;
final int MAX_FIT_ATTEMPTS = 4;
public DrawingObjects() {
shapes = new ArrayList<Shape>();
seed = new Random();
}
public void actionPerformed(ActionEvent e) {
String id = e.getActionCommand();
int sides = 0;
if(id.equals("triangle"))
sides = 3;
if(id.equals("pentagon"))
sides = 5;
if(id.equals("hexagon"))
sides = 6;
if(id.equals("septagon"))
sides = 7;
int[][] xy = generateShapeArrays(0, 0, R, sides);
Polygon p = new Polygon(xy[0], xy[1], sides);
placePolygon(p);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(Color.blue);
for(Shape s : shapes)
g2.draw(s);
}
public Dimension getPreferredSize() {
return new Dimension(400,400);
}
private void placePolygon(Polygon polygon) {
Shape shape = locateBySize(polygon);
if(shape != null) {
shapes.add(shape);
repaint();
} else {
System.out.println("polygon could not be placed");
}
}
private Shape locateBySize(Polygon polygon) {
Shape shape = polygon;
int sides = polygon.npoints;
int sizeReductions = 0;
do {
Shape s = fitShape(shape, sides);
if(s != null)
return s;
AffineTransform at = AffineTransform.getScaleInstance(0.6, 0.6);
shape = (Shape)at.createTransformedShape(shape);
sizeReductions++;
}
while(sizeReductions <= MAX_SIZE_REDUCTIONS);
return null;
}
private Shape fitShape(Shape shape, int sides) {
int w = getWidth();
int h = getHeight();
Rectangle r = shape.getBounds();
AffineTransform at = new AffineTransform();
int count = 0;
while(count < MAX_FIT_ATTEMPTS) {
int x = seed.nextInt(w - r.width);
int y = seed.nextInt(h - r.height);
// Original shape has its center at the view (this) origin (0,0).
// The extra factors after x and y below are to move the polygon
// fully into the view.
double dy = getOffset(r.height, sides);
at.setToTranslation(x+r.width/2, y+r.height/2+dy);
Shape s = at.createTransformedShape(shape);
if(isClear(s)) {
return s;
}
count++;
}
return null;
}
private double getOffset(int height, int sides) {
// R + r = height center to vertex
// r = R * cos(PI/sides)center to side
double R = height/(1 + Math.cos(Math.PI/sides));
double r = R * Math.cos(Math.PI/sides);
return (R - r)/2;
}
private boolean isClear(Shape shape) {
Area area = new Area(shape);
for(int j = 0; j < shapes.size(); j++) {
Area test = (Area)area.clone();
test.subtract(new Area(shapes.get(j)));
if(!test.equals(area)) {
return false;
}
}
return true;
}
private int[][] generateShapeArrays(int cx, int cy, int R, int sides) {
int radInc = 0;
if(sides % 2 == 0)
radInc = 1;
int[] x = new int[sides];
int[] y = new int[sides];
for(int i = 0; i < sides; i++) {
x[i] = cx + (int)(R * Math.sin(radInc*Math.PI/sides));
y[i] = cy - (int)(R * Math.cos(radInc*Math.PI/sides));
radInc += 2;
}
// keep base of triangle level
if(sides == 3)
y[2] = y[1];
return new int[][] { x, y };
}
private Box getUIBox() {
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalGlue());
String[] ids = { "triangle", "pentagon", "hexagon", "septagon" };
for(int j = 0; j < ids.length; j++) {
JButton button = new JButton(ids[j]);
button.setActionCommand(ids[j]);
button.addActionListener(this);
box.add(button);
box.add(Box.createHorizontalGlue());
}
return box;
}
public static void main(String[] args) {
DrawingObjects test = new DrawingObjects();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(test);
f.getContentPane().add(test.getUIBox(), "Last");
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}