package uk.org.squirm3.ui.collider;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import uk.org.squirm3.derivative.RoundGradientPaint;
import uk.org.squirm3.engine.ApplicationEngine;
import uk.org.squirm3.engine.ApplicationEngineEvent;
import uk.org.squirm3.listener.Listener;
import uk.org.squirm3.model.Atom;
import uk.org.squirm3.model.Configuration;
import uk.org.squirm3.model.DraggingPoint;
import uk.org.squirm3.model.type.AtomType;
import uk.org.squirm3.model.type.def.BasicType;
public class AtomsPanel extends JScrollPane {
private static final long serialVersionUID = 1L;
DraggingPoint draggingPoint;
Atom[] latestAtomsCopy;
private int simulationWidth;
private int simulationHeight;
BufferedImage bimg;
private boolean needRepaint = true;
byte scale;
private final JPanel imagePanel;
// yellow, grey, blue, purple, red, green
private static final Map<BasicType, Color> atomColors;
static {
atomColors = new HashMap<BasicType, Color>();
atomColors.put(BasicType.A, new Color(0xbdcf00));
atomColors.put(BasicType.B, new Color(0x5f5f5f));
atomColors.put(BasicType.C, new Color(0x0773db));
atomColors.put(BasicType.D, new Color(0xee10ac));
atomColors.put(BasicType.E, new Color(0xef160f));
atomColors.put(BasicType.F, new Color(0x00df06));
}
private static final Map<AtomType, BufferedImage> atomsImages = new HashMap<AtomType, BufferedImage>();
private final Image spikyImage;
final ApplicationEngine applicationEngine;
public AtomsPanel(final ApplicationEngine applicationEngine,
final Image spikyImage) {
imagePanel = new ImagePanel(this);
setViewportView(imagePanel);
this.applicationEngine = applicationEngine;
createAtomsImages();
this.spikyImage = spikyImage;
needRepaint = true;
scale = 100;
addComponentListener(new ResizeListener());
imageSizeHasChanged();
bindToApplicationEngine(applicationEngine);
}
private void bindToApplicationEngine(
final ApplicationEngine applicationEngine) {
applicationEngine.addListener(new AtomListener(applicationEngine),
ApplicationEngineEvent.ATOMS);
applicationEngine.addListener(new SizeListener(),
ApplicationEngineEvent.CONFIGURATION);
applicationEngine.addListener(new DraggingPointListener(
applicationEngine), ApplicationEngineEvent.DRAGGING_POINT);
}
private static void createAtomsImages() {
// size
final float R = Atom.getAtomSize() - 2;
final int w = (int) (2 * R);
final int h = (int) (2 * R);
for (final Entry<BasicType, Color> entry : atomColors.entrySet()) {
// creation of the image
final BufferedImage image = new BufferedImage(w, h,
BufferedImage.TYPE_INT_ARGB);
atomsImages.put(entry.getKey(), image);
// creation of the graphic
final Graphics2D g2 = image.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// creation of the colors
final Color baseColor = entry.getValue();
final int colorOffset = 220;
final int red = baseColor.getRed() < 255 - colorOffset ? baseColor
.getRed() + colorOffset : 255;
final int green = baseColor.getGreen() < 255 - colorOffset
? baseColor.getGreen() + colorOffset
: 255;
final int blue = baseColor.getBlue() < 255 - colorOffset
? baseColor.getBlue() + colorOffset
: 255;
final Color lightColor = new Color(red, green, blue);
// drawing the image
final RoundGradientPaint gradient = new RoundGradientPaint(w / 3,
h / 3, lightColor, new Point2D.Double(w / 2, h / 2),
baseColor);
g2.setPaint(gradient);
g2.fillOval(0, 0, w, h);
}
}
private void imageSizeHasChanged() {
final Configuration configuration = applicationEngine
.getConfiguration();
if (configuration != null) {
simulationHeight = (int) configuration.getHeight();
simulationWidth = (int) configuration.getWidth();
}
if (imagePanel != null) {
final Dimension d = getSize();
final int pseudoScale = (int) (Math.min(
(d.height * 0.99 / simulationHeight),
(d.width * 0.99 / simulationWidth)) * 100);
scale = pseudoScale >= 100 ? 100 : (byte) pseudoScale;
}
final float zoom = (float) scale / 100;
imagePanel
.setPreferredSize(new Dimension((int) (simulationWidth * zoom),
(int) (simulationHeight * zoom)));
SwingUtilities.updateComponentTreeUI(this);
imageHasChanged();
}
private void imageHasChanged() {
needRepaint = true;
if (imagePanel == null || !imagePanel.isDisplayable()) {
return;
}
imagePanel.repaint();
}
void updateImage() {
if (!needRepaint) {
return;
}
needRepaint = false;
final int R = (int) Atom.getAtomSize();
// to avoid the array to be changed (multithreading issue maybe later)
final Atom[] atoms = latestAtomsCopy;
// get the dimensions
final int w = simulationWidth;
final int h = simulationHeight;
// do we have a correct bimg ?
if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
bimg = (BufferedImage) createImage(w, h);
}
if (bimg == null) {
return;// collisionsPanel is not displayable
}
// create graphics
final Graphics2D g2 = bimg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2.setBackground(Color.WHITE);
g2.clearRect(0, 0, w, h);
// draw the atoms themselves
g2.setStroke(new BasicStroke(4));
g2.setFont(new Font("Arial", Font.BOLD, R));
g2.setPaint(Color.black);
if (atoms != null) {
final int offset_x = R;
final int offset_y = R;
final int text_offset_y = (int) (R * 8.0 / 22.0);
for (int i = 0; i < atoms.length; i++) {
if (!atoms[i].isKiller()) {
// draw the normal colour atom image and label it
g2.drawImage(atomsImages.get(atoms[i].getType()),
(int) atoms[i].getPhysicalPoint().getPositionX()
- offset_x, (int) atoms[i]
.getPhysicalPoint().getPositionY()
- offset_y, R * 2, R * 2, this);
final String label = atoms[i].toString();
final int width = g2.getFontMetrics().stringWidth(label);
g2.drawString(label, (int) atoms[i].getPhysicalPoint()
.getPositionX() - width / 2, (int) atoms[i]
.getPhysicalPoint().getPositionY() + text_offset_y);
} else {
// draw a special spiky image and no label
g2.drawImage(spikyImage, (int) atoms[i].getPhysicalPoint()
.getPositionX() - offset_x, (int) atoms[i]
.getPhysicalPoint().getPositionY() - offset_y,
R * 2, R * 2, this);
}
}
}
// draw the bonds
g2.setPaint(new Color(0, 0, 0, 50));
if (atoms != null) {
for (final Atom atom : atoms) {
final Iterator<Atom> it = atom.getBonds().iterator();
while (it.hasNext()) {
final Atom other = it.next();
final float x1 = atom.getPhysicalPoint().getPositionX();
final float y1 = atom.getPhysicalPoint().getPositionY();
final float dx = other.getPhysicalPoint().getPositionX()
- x1;
final float dy = other.getPhysicalPoint().getPositionY()
- y1;
final float d = (float) Math.sqrt(dx * dx + dy * dy);
final float x_cut = dx * R * 0.8f / d;
final float y_cut = dy * R * 0.8f / d;
g2.drawLine((int) (x1 + x_cut), (int) (y1 + y_cut),
(int) (x1 + dx - x_cut), (int) (y1 + dy - y_cut));
}
}
}
// draw the dragging line if currently dragging
if (draggingPoint != null) {
g2.setStroke(new BasicStroke(5));
g2.setPaint(new Color(0, 0, 0, 100));
g2.drawLine((int) draggingPoint.getX(), (int) draggingPoint.getY(),
(int) atoms[draggingPoint.getWhichBeingDragging()]
.getPhysicalPoint().getPositionX(),
(int) atoms[draggingPoint.getWhichBeingDragging()]
.getPhysicalPoint().getPositionY());
g2.setStroke(new BasicStroke(4)); // else the stroke would have been
// changed
// when outlining the collider area
}
// draw the dragging point used
if (applicationEngine.getLastUsedDraggingPoint() != null) {
final DraggingPoint lastUsedDraggingPoint = applicationEngine
.getLastUsedDraggingPoint();
g2.setStroke(new BasicStroke(1));
g2.setPaint(new Color(200, 0, 0, 100));
g2.drawLine((int) lastUsedDraggingPoint.getX(),
(int) lastUsedDraggingPoint.getY(),
(int) atoms[lastUsedDraggingPoint.getWhichBeingDragging()]
.getPhysicalPoint().getPositionX(),
(int) atoms[lastUsedDraggingPoint.getWhichBeingDragging()]
.getPhysicalPoint().getPositionY());
g2.setStroke(new BasicStroke(4)); // else the stroke would have been
// changed
// when outlining the collider area
}
// outline the collider area
g2.setPaint(new Color(100, 100, 200));
g2.drawRoundRect(2, 1, simulationWidth - 4, simulationHeight - 4, 9, 9);
g2.dispose();
}
private final class SizeListener implements Listener {
@Override
public void propertyHasChanged() {
imageSizeHasChanged();
}
}
private final class DraggingPointListener implements Listener {
private final ApplicationEngine applicationEngine;
private DraggingPointListener(final ApplicationEngine applicationEngine) {
this.applicationEngine = applicationEngine;
}
@Override
public void propertyHasChanged() {
draggingPoint = applicationEngine.getCurrentDraggingPoint();
imageHasChanged();
}
}
private final class AtomListener implements Listener {
private final ApplicationEngine applicationEngine;
private AtomListener(final ApplicationEngine applicationEngine) {
this.applicationEngine = applicationEngine;
}
@Override
public void propertyHasChanged() {
final Collection<? extends Atom> c = applicationEngine.getAtoms();
final Iterator<? extends Atom> it = c.iterator();
latestAtomsCopy = new Atom[c.size()];
int i = 0;
while (it.hasNext()) {
latestAtomsCopy[i] = it.next();
i++;
}
imageHasChanged();
}
}
private final class ResizeListener implements ComponentListener {
@Override
public void componentResized(final ComponentEvent arg0) {
imageSizeHasChanged();
}
@Override
public void componentHidden(final ComponentEvent arg0) {
}
@Override
public void componentMoved(final ComponentEvent arg0) {
}
@Override
public void componentShown(final ComponentEvent arg0) {
}
}
}