Java Multiple Graphics

java multiple graphics

For each sort graph you need some kind of model, which should hold the current state of each sort. This would probably mean adding your list of ints to a separate list per sort.

You would also need some kind of mechanism that would allow you to loop through each sort algorithm and tell it to move to the next step in its algorithm, thus allowing you to control when each sort algorithm update and therefore control when the screen is updated.

Updated

Based on a comment from the OP, basically, I've ripped out the sort algorithm as a separate interface. Each algorithm would need to extend from this interface, but it provides the basic requirements to allow the UI to render the sort animation.

Bellow is basic implementation, while it's based on Swing, if required, it wouldn't be a stretch to get it to work with AWT.

Sample Image

import java.awt.BorderLayout;
import java.awt.Color;
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.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class TestSort {

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

public TestSort() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}

SortPane sortPane = new SortPane();
int values[] = new int[10];
for (int index = 0; index < values.length; index++) {
values[index] = (int)Math.round(Math.random() * 100f);
}
BubbleSort sorter = new BubbleSort(values);
sortPane.setSorter(sorter);

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(sortPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
sorter.sort();
}
});
}

public class SortPane extends JPanel {

private Sorter sorter;
private ChangeHandler changeHandler;
private int maxValue;

@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int values[] = getSorter().getValues();
int width = getWidth() - 1;
int height = getHeight() - 1;
int colWidth = Math.round((float)width / (float)values.length);
int x = 0;
Color fill = Color.YELLOW;
Color highlight = null;
switch (getSorter().getState()) {
case Sorting:
fill = Color.BLUE;
highlight = Color.RED;
break;
case Done:
fill = Color.GREEN;
break;
}
for (int index = 0; index < values.length; index++) {
g2d.setColor(fill);
int value = values[index];
int colHeight = (int)((float)height * ((float)value / (float)maxValue));
g2d.fillRect(x, height - colHeight, colWidth - 1, colHeight);
if (getSorter().isActiveIndex(index) && highlight != null) {
g2d.setColor(highlight);
g2d.drawRect(x, height - colHeight, colWidth - 1, colHeight);
}
x += colWidth;
}
g2d.dispose();
}

public Sorter getSorter() {
return sorter;
}

public void setSorter(Sorter value) {
if (sorter != value) {
if (sorter != null) {
sorter.removeChangeListener(getChangeHandler());
}
sorter = value;
if (sorter != null) {
sorter.addChangeListener(getChangeHandler());
maxValue = 0;
for (int intValue : sorter.getValues()) {
maxValue = Math.max(maxValue, intValue);
}
}
repaint();
}
}

public ChangeHandler getChangeHandler() {
if (changeHandler == null) {
changeHandler = new ChangeHandler();
}
return changeHandler;
}

public class ChangeHandler implements ChangeListener {
@Override
public void stateChanged(ChangeEvent e) {
repaint();
}
}

}

public interface Sorter {

public enum State {
Waiting,
Sorting,
Done
}

public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
public int[] getValues();
public void sort();
public State getState();
public boolean isActiveIndex(int index);
}

public abstract class AbstracSorter implements Sorter {

private List<ChangeListener> listeners;
private int[] values;
private State state = State.Waiting;
private List<Integer> activeIndices;

public AbstracSorter(int[] values) {
this.values = values;
listeners = new ArrayList<>(25);
activeIndices = new ArrayList<>(2);
}

@Override
public State getState() {
return state;
}

public void setState(State value) {
if (value != state) {
state = value;
fireStateChanged();
}
}

@Override
public int[] getValues() {
return values;
}

@Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}

@Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}

protected void fireStateChanged() {
if (listeners.size() > 0) {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
}

@Override
public boolean isActiveIndex(int index) {
return activeIndices.contains(index);
}

protected void setActiveIndicies(int lower, int upper) {
activeIndices.clear();
activeIndices.add(lower);
activeIndices.add(upper);
fireStateChanged();
}

protected void swap(int[] anArrayOfInt, int i, int j) {
setActiveIndicies(i, j);
int x = anArrayOfInt[i];
anArrayOfInt[i] = anArrayOfInt[j];
anArrayOfInt[j] = x;
fireStateChanged();
}
}

public class BubbleSort extends AbstracSorter {

private int outter = 0;
private int inner = 0;

public BubbleSort(int[] values) {
super(values);
}

@Override
public void sort() {
setState(State.Sorting);
outter = 0;
inner = 1;
Timer timer = new Timer(250, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] values = getValues();
inner++;
if (inner >= values.length - outter) {
outter++;
inner = 1;
}

if (outter < values.length) {
if (values[inner - 1] > values[inner]) {
swap(values, inner - 1, inner);
} else {
setActiveIndicies(inner - 1, inner);
}
} else {
((Timer)e.getSource()).stop();
setState(State.Done);
}
}
});
timer.setRepeats(true);
timer.start();
}
}
}

Example using the source array

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class TestSort {

public static void main(String[] args) {
new TestSort();
}
private List<Sorter> sorters;

public TestSort() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}

sorters = new ArrayList<>(2);
int values[] = new int[10];
for (int index = 0; index < values.length; index++) {
values[index] = (int) Math.round(Math.random() * 100f);
}

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(0, 2));
frame.add(createBubbleSortPane(values));
frame.add(createBubbleSortPane(values));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);

for (Sorter sorter : sorters) {
sorter.sort();
}
}
});
}

protected SortPane createBubbleSortPane(int[] values) {
SortPane sortPane = new SortPane();
BubbleSort sorter = new BubbleSort(values);
sortPane.setSorter(sorter);

sortPane.setBorder(new CompoundBorder(new LineBorder(Color.GRAY), new EmptyBorder(8, 8, 8, 8)));

sorters.add(sorter);

return sortPane;
}

public class SortPane extends JPanel {

private Sorter sorter;
private ChangeHandler changeHandler;
private int maxValue;

@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int values[] = getSorter().getValues();
Insets insets = getInsets();
int width = getWidth() - 1 - (insets.left + insets.right);
int height = getHeight() - 1 - (insets.top + insets.bottom);
int colWidth = Math.round((float) width / (float) values.length);
int x = insets.left;
Color fill = Color.YELLOW;
Color highlight = null;
switch (getSorter().getState()) {
case Sorting:
fill = Color.BLUE;
highlight = Color.RED;
break;
case Done:
fill = Color.GREEN;
break;
}
for (int index = 0; index < values.length; index++) {
g2d.setColor(fill);
int value = values[index];
int colHeight = (int) ((float) height * ((float) value / (float) maxValue));
g2d.fillRect(x, insets.top + height - colHeight, colWidth - 1, colHeight);
if (getSorter().isActiveIndex(index) && highlight != null) {
g2d.setColor(highlight);
g2d.drawRect(x, insets.top + height - colHeight, colWidth - 1, colHeight);
}
x += colWidth;
}
g2d.dispose();
}

public Sorter getSorter() {
return sorter;
}

public void setSorter(Sorter value) {
if (sorter != value) {
if (sorter != null) {
sorter.removeChangeListener(getChangeHandler());
}
sorter = value;
if (sorter != null) {
sorter.addChangeListener(getChangeHandler());
maxValue = 0;
for (int intValue : sorter.getValues()) {
maxValue = Math.max(maxValue, intValue);
}
}
repaint();
}
}

public ChangeHandler getChangeHandler() {
if (changeHandler == null) {
changeHandler = new ChangeHandler();
}
return changeHandler;
}

public class ChangeHandler implements ChangeListener {

@Override
public void stateChanged(ChangeEvent e) {
repaint();
}
}
}

public interface Sorter {

public enum State {

Waiting,
Sorting,
Done
}

public void addChangeListener(ChangeListener listener);

public void removeChangeListener(ChangeListener listener);

public int[] getValues();

public void sort();

public State getState();

public boolean isActiveIndex(int index);
}

public abstract class AbstracSorter implements Sorter {

private List<ChangeListener> listeners;
private int[] values;
private State state = State.Waiting;
private List<Integer> activeIndices;

public AbstracSorter(int[] values) {
this.values = new int[values.length];
System.arraycopy(values, 0, this.values, 0, values.length);
listeners = new ArrayList<>(25);
activeIndices = new ArrayList<>(2);
}

@Override
public State getState() {
return state;
}

public void setState(State value) {
if (value != state) {
state = value;
fireStateChanged();
}
}

@Override
public int[] getValues() {
return values;
}

@Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}

@Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}

protected void fireStateChanged() {
if (listeners.size() > 0) {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
}

@Override
public boolean isActiveIndex(int index) {
return activeIndices.contains(index);
}

protected void setActiveIndicies(int lower, int upper) {
activeIndices.clear();
activeIndices.add(lower);
activeIndices.add(upper);
fireStateChanged();
}

protected void swap(int[] anArrayOfInt, int i, int j) {
setActiveIndicies(i, j);
int x = anArrayOfInt[i];
anArrayOfInt[i] = anArrayOfInt[j];
anArrayOfInt[j] = x;
fireStateChanged();
}
}

public class BubbleSort extends AbstracSorter {

private int outter = 0;
private int inner = 0;

public BubbleSort(int[] values) {
super(values);
}

@Override
public void sort() {
setState(State.Sorting);
outter = 0;
inner = 1;
Timer timer = new Timer(250, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] values = getValues();
inner++;
if (inner >= values.length - outter) {
outter++;
inner = 1;
}

if (outter < values.length) {
if (values[inner - 1] > values[inner]) {
swap(values, inner - 1, inner);
} else {
setActiveIndicies(inner - 1, inner);
}
} else {
((Timer) e.getSource()).stop();
setState(State.Done);
}
}
});
timer.setRepeats(true);
timer.start();
}
}
}

Example Insertion Sorter

This is an example of using a Thread as the primary sort engine instead of a Swing Timer

public class InsertionSorter extends AbstracSorter {

public InsertionSorter(int[] values) {
super(values);
}

@Override
public void sort() {
setState(State.Sorting);
new Thread(new SortRunnable()).start();
}

@Override
protected void swap(int[] anArrayOfInt, int i, int j) {
setActiveIndicies(i, j);
int x = anArrayOfInt[i];
anArrayOfInt[i] = anArrayOfInt[j];
anArrayOfInt[j] = x;
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
fireStateChanged();
}
});
} catch (InterruptedException | InvocationTargetException exp) {
exp.printStackTrace();
}
}

public class SortRunnable implements Runnable {

@Override
public void run() {
int[] values = getValues();
for (int i = 0; i < values.length; ++i) {
for (int j = i - 1; j >= 0 && values[j] > values[j + 1]; --j) {
try {
Thread.sleep(250);
} catch (InterruptedException ex) {
}
swap(values, j, j + 1);
}
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setState(State.Done);
}
});
}
}
}

multiple graphics2d

Here are my suggestions:

  • Extend JComponent rather than Canvas (you probably want a lightweight Swing component rather than a heavyweight AWT one)
  • Then don't bother with the manual back-buffering for your drawing - Swing does back-buffering for you automatically (and will probably use hardware acceleration while doing so)
  • Have one component draw both items and the rest of the game background. There is no good reason to do it separately (even if you only change the items layer, the background will need to be redrawn because of the transparency effects)
  • Capitalise Your ClassNames, it makes my head hurt to see lowercase class names :-)

EDIT

Typically the approach would be to have a class that represents the visible area of the game e.g. GameScreen, with a paintCompoent method as follows:

public class GameScreen extends JComponent {
....

public void paintComponent(Graphics g) {
drawBackground(g);
drawItems(g);
drawOtherStuff(g); // e.g. animated explosions etc. on top of everything else
}
}

drawing multiple graphics on the same JLabel

It is not completely clear what you want to achieve, but note that when drawing the polygon, you are drawing the latest fixed polygon-point on exactly the same place as when drawing the point. To see this, increase the size of the point with

g.fillOval(x.intValue(), y.intValue(), 10, 10);

and draw the polygon with a different color in the draw method:

g.setColor(Color.BLUE);

Combine two graphics objects in Java

  • To impose images, use an appropriate layout, as shown here.

  • To compose images, use an appropriate composite rule, as shown here.

Addendum: @HFOE is correct. An instance of Graphics or Graphics2D, sometimes called a graphics context, is a transitory means to apply rendering methods; the target device or off-screen image must exist independently. Classes implementing the Shape interface are good candidates for encapsulating information about what to render in a given context. AffineTest and GraphPanel are examples.

Multiple independent layers in Graphics

There are a few ways to do it. You can keep a list of BufferedImages as your layers of images and render them accordingly:

class AdvancePaint extends JPanel(){
ArrayList<BufferedImage> layers;
//Other constructors, variables, initializations not shown

class AdvancePaint(){
layers = new ArrayList<BufferedImage>();
}

public void addLayer(BufferedImage layer){
layers.add(layer);
}

@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
for(BufferredImage buf : layers) //render all layers
g.drawImage(buf, 0, 0, width, height, null);
}
}

The above codes will render the BufferedImage from the list in accordance to the order they are added to the list. If you do not want to paint all layers on every repaint, you can always add another draw(Graphics g) method which you can pick the layers to be drawn onto another BufferedImage. Then only draw this BufferedImage on paintComponent(g).

Since the layers are stored as a Collection, you can always change their rendering order by sorting them or changing their position in the list.

You can always tweak on the minor details from my example to customize what you need.


To add a Layer:

BufferedImage layer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
advancePaint.addLayer(layer);

Using BufferedImage.TYPE_INT_ARGB, the background will be transparent, hence other layers will be able to "shine through".

Adding multiple graphics to a single JPanel

I'm guessing that each tank JPanel shows an image of a single tank (? not 100% sure). Some suggestions, but please do tell me if my assumptions are way off base:

  • Keep the logic and view well separated a la M-V-C.
  • Do all graphics in a single JPanel, perhaps called a DrawingPanel, in its paintComponent(...) method.
  • The Background image will be a BufferedImage that is draw in the DrawingPanel's paintComponent(...) method.
  • The a tank itself would not be represented by a JPanel but would be its own small BufferedImage sprite, again draw in the paintComponent method, but drawn after the background image.
  • Each tank would need at least 4 sprites, one for each direction.
  • You may need more sprites if your tanks will move along a diagonal.
  • And a separate sprite/image for the turret.
  • You would move a sprite's drawn position in response to changes of it's model representation's position.
  • Same thing for turret rotation.

How can I rotate multiple graphics shapes together in java without changing their position on the frame or their position relative to eachother?

If you want to be able to rotate the shapes together and keep their position on the JFrame I would recommend that you use the form of g2D.rotate that uses x and y coordinates to rotate with. You should make a standard x and y coordinate to draw each shapes position from so that when you use these standard x and y coordinates to rotate from all of the shapes will rotate together. It would probably look something like this:

//  imports
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Container;
import java.awt.Rectangle;
import java.awt.geom.Ellipse2D;
import java.awt.Polygon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JComponent;

public class GraphicsRotate extends JComponent implements ActionListener {

// JFrame and Container
JFrame frame = new JFrame("Graphics Rotate");
Container container = frame.getContentPane();
public int standardX = 100;
public int standardY = 100;
public int rotation = 0;

public static void main(String[] args) {
GraphicsRotate graphicsRotate = new GraphicsRotate();
graphicsRotate.setup();

}

public void setup() {
container.setBackground(Color.BLACK);
container.add(this);

KeyListener kl = new KeyListener() {
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_LEFT) {
rotation -= 10;
repaint();
}
if (code == KeyEvent.VK_RIGHT) {
rotation += 10;
repaint();
}
}

public void keyReleased(KeyEvent e) {

}

public void keyTyped(KeyEvent e) {

}
};

frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addKeyListener(kl);
frame.setFocusable(true);
frame.requestFocus();
frame.setVisible(true);
}

public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g.setColor(Color.GREEN);
g2.rotate(Math.toRadians(rotation), standardX, standardY);
Rectangle rect = new Rectangle(standardX + 10, standardY + 10, 5,
5);
Ellipse2D ellipse = new Ellipse2D.Double(standardX + 13, standardY
+ 5, 10, 10);
g2.fill(ellipse);
g2.fill(rect);

}

@Override
public void actionPerformed(ActionEvent e) {

}

}

This could be implemented on a much larger scale, and if you wanted the shapes to rotate around the center of the larger shape that you have made with them, then you could do the necessary calculations to figure out the center and do something like g2.rotate(Math.toRadians(rotation), standardX + middleX, standardY + middleY).

I hope that this answered your question.



Related Topics



Leave a reply



Submit