/*
* Datei: GuiSimulation.java
* Autor(en): Lukas König
* Java-Version: 6.0
* Erstellt (vor): 09.05.2008
*
* (c) This file and the EAS (Easy Agent Simulation) framework containing it
* is protected by Creative Commons by-nc-sa license. Any altered or
* further developed versions of this file have to meet the agreements
* stated by the license conditions.
*
* In a nutshell
* -------------
* You are free:
* - to Share -- to copy, distribute and transmit the work
* - to Remix -- to adapt the work
*
* Under the following conditions:
* - Attribution -- You must attribute the work in the manner specified by the
* author or licensor (but not in any way that suggests that they endorse
* you or your use of the work).
* - Noncommercial -- You may not use this work for commercial purposes.
* - Share Alike -- If you alter, transform, or build upon this work, you may
* distribute the resulting work only under the same or a similar license to
* this one.
*
* + Detailed license conditions (Germany):
* http://creativecommons.org/licenses/by-nc-sa/3.0/de/
* + Detailed license conditions (unported):
* http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en
*
* This header must be placed in the beginning of any version of this file.
*/
package eas.startSetup.marbBuilder.traceBetrachter;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Observable;
import java.util.Observer;
import eas.math.geometry.Pixel2D_08Bit;
import eas.math.geometry.Vector2D;
import eas.miscellaneous.StaticMethods;
import eas.plugins.standard.eaPlugin.xmlRecording.XMLAufnLesen;
import eas.simulation.ConstantsSimulation;
import eas.startSetup.ParCollection;
import eas.startSetup.marbBuilder.SteuerFenster;
/**
* Klasse zur Darstellung einer Simulation in Echtzeit.
*
* @author Lukas König
*/
public class GuiSimulation extends Frame
implements Observer,
Runnable {
private transient BufferedImage img;
/**
* Die VersionsUID.
*/
private static final long serialVersionUID = -2260012518107957607L;
/**
* Ob der Rahmen der freien Pixel bereits berechnet ist (Wert) oder nicht
* (null).
*/
private Vector2D rahmenLinksOben = null;
/**
* Ob der Rahmen der freien Pixel bereits berechnet ist (Wert) oder nicht
* (null).
*/
private Vector2D rahmenRechtsUnten = null;
/**
* Das Feld in seiner tatsächlichen Darstellungsgröße.
*/
private byte[][] darFeld;
/**
* Die Pixel, die noch in der Darstellung gesetzt werden müssen.
*/
private LinkedList<Pixel2D_08Bit> wartPixel2D;
/**
* Die noch nicht in die Darstellung eingefügten NormPixel.
*/
private LinkedList<Pixel2D_08Bit> neueNormPixel2D;
/**
* Der Taktgeber für die Darstellung.
*/
private SimulationsZeit simZ;
/**
* Das Steuerfenster, das zu dieser Umgebung gehört.
*/
private SteuerFenster eltFenst;
/**
* Die beobachtete Umgebung.
*/
private Umgebung umgebung;
/**
* Der ParCollection.
*/
private ParCollection pars;
/**
* Die Breite des Fensters.
*/
private int breite;
/**
* Die Höhe des Fensters.
*/
private int hoehe;
/**
* Der linke Rand.
*/
private double xRand;
/**
* Der rechte Rand.
*/
private double yRand;
/**
* Gibt an, ob beim nächsten repaint() das gesamte Feld (und nicht nur
* die anstehenden neuen Pixel) neu gezeichnet werden soll.
*/
private boolean allesNeuZeichnen = false;
/**
* Das Fenster, in dem die Sensoren dargestellt werden.
*/
private SensorenDarstellung sensFenster;
/**
* Ob die Darstellung weiter aktualisiert werden soll.
*/
private boolean weiterlaufen;
/**
* Der Thread, in dem die GUI zur Darstellung der Simulationsumgebung
* läuft.
*/
private Thread guiThread;
/**
* Gibt an, ob die Zeichensession beendet ist und die aktuellen Pixel
* gezeichnet werden können.
*/
private boolean zeichnenErlaubt;
/**
* Der Eltern-Tracebetrachter.
*/
private TraceBetrachter eltTraceB;
/**
* Initialisiert das Objekt mit einem Tracebetrachter als Elternfenster.
*
* @param fensterText Der Anzuzeigende Text im Titel des
* Simulationsfensters.
* @param sensFenst Das Fenster, in dem die Sensoren dargestellt werden
* (optional).
* @param elt Das Eltern-Tracebetrachter-Fenster.
* @param params Die Parameter.
*/
public GuiSimulation(
final String fensterText,
final SensorenDarstellung sensFenst,
final TraceBetrachter elt,
final ParCollection params,
final XMLAufnLesen aufnahme) {
this(
fensterText,
sensFenst,
(SteuerFenster) null,
params,
aufnahme);
this.eltTraceB = elt;
}
/**
* Der Konstruktor. Initialisiert das Objekt mit einem Steuerfenster als
* Elternfenster.
*
* @param fensterText Der Anzuzeigende Text im Titel des
* Simulationsfensters.
* @param sensFenst Das Fenster, in dem die Sensoren dargestellt werden
* (optional).
* @param elt Das Eltern-Steuerfenster.
* @param params Die Parameter.
*/
public GuiSimulation(
final String fensterText,
final SensorenDarstellung sensFenst,
final SteuerFenster elt,
final ParCollection params,
final XMLAufnLesen aufnahme) {
super(fensterText);
this.setResizable(false);
this.pars = params;
this.zeichnenErlaubt = true;
int breit = aufnahme.getInputField().length
+ ConstantsSimulation.SIM_RAND_LINKS
+ ConstantsSimulation.SIM_RAND_RECHTS
+ 0;
int hoch = aufnahme.getInputField()[0].length
+ ConstantsSimulation.SIM_RAND_OBEN
+ ConstantsSimulation.SIM_RAND_UNTEN
+ 25;
this.darFeld = new byte[aufnahme.getInputField().length]
[aufnahme.getInputField()[0].length];
this.guiThread = null;
this.wartPixel2D = new LinkedList<Pixel2D_08Bit>();
this.neueNormPixel2D = new LinkedList<Pixel2D_08Bit>();
this.eltFenst = elt;
this.weiterlaufen = false;
this.breite = breit;
this.hoehe = hoch;
this.setSize(this.breite, this.hoehe);
img = new BufferedImage(this.breite, this.hoehe, BufferedImage.TYPE_INT_ARGB);
this.feldGrAnpass();
this.setAlwaysOnTop(true);
if (this.eltFenst != null) {
this.addWindowListener(new WindowClosingAdapter2(false,
this.eltFenst,
this));
}
this.addComponentListener(
new ComponentAdapter() {
@Override
public void componentResized(final ComponentEvent event) {
GuiSimulation.this.breite
= GuiSimulation.this.getSize().width;
GuiSimulation.this.hoehe
= GuiSimulation.this.getSize().height;
GuiSimulation.this.allesNeuZeichnen = true;
GuiSimulation.this.feldGrAnpass();
}
}
);
this.addMouseListener(
new MouseAdapter() {
@Override
public void mousePressed(final MouseEvent event) {
int x = event.getX();
int y = event.getY();
if (event.getButton() == 1) {
GuiSimulation.this.deselektiereAlle();
if (event.getClickCount() <= 1) {
Roboter r
= GuiSimulation.this.holeNahenDar(x, y, null);
r.setSelektiert(true);
if (GuiSimulation.this.eltFenst != null) {
} else if (GuiSimulation.this.eltTraceB != null) {
GuiSimulation.this.eltTraceB.selGraph(r);
}
StaticMethods.log(
StaticMethods.LOG_INFO,
"Selektiert ("
+ r.getId()
+ " ["
+ r.getFitString()
+ "]"
+ ").",
GuiSimulation.this.pars);
StaticMethods.log(
StaticMethods.LOG_DEBUG,
"TransCode: " + r.getTransCodes()[0] + ".",
GuiSimulation.this.pars);
StaticMethods.log(
StaticMethods.LOG_DEBUG,
"VerhCode: " + r.getVerhCodes()[0] + ".",
GuiSimulation.this.pars);
}
GuiSimulation.this.allesNeuZeichnen = true;
// if (GuiSimulation.this.pars.getVerzoeg().longValue()
// > 2000
// && GuiSimulation.this.eltFenst != null) {
// GuiSimulation.this.pars.setVerzoegerung(2000);
// }
if (!GuiSimulation.this.weiterlaufen) {
GuiSimulation.this.zeichnenErlaubt = true;
GuiSimulation.this.neuZeichnen();
}
} else {
// int modus = GuiSimulation.this.pars.getAnzModus();
// modus++;
// modus %= 3;
// GuiSimulation.this.pars.setAnzModus(modus);
}
}
}
);
this.setVisible(true);
if (sensFenst != null) {
this.sensFenster = sensFenst;
this.sensFenster.setLocation(this.getLocation().x + this.getWidth(),
this.getLocation().y);
this.sensFenster.setVisible(true);
this.setSensorFenster();
}
}
/**
* Setzt das Sensorfenster (das Fenster, in dem während der Simulation
* die Sensorwerte angezeigt werden).
*/
public void setSensorFenster() {
}
/**
* @return Returns the breite.
*/
public int getBreite() {
return this.breite;
}
/**
* @param breit The breite to set.
*/
public void setBreite(final int breit) {
this.breite = breit;
}
/**
* @return Returns the hoehe.
*/
public int getHoehe() {
return this.hoehe;
}
/**
* @param hoeh The hoehe to set.
*/
public void setHoehe(final int hoeh) {
this.hoehe = hoeh;
}
/**
* Passt die Größe der Darstellung des simulierten Feldes der Größe
* des Fensters von <code>this</code> an.
*/
private void feldGrAnpass() {
this.setzeDarAusdehnung(this.breite
- ConstantsSimulation.SIM_RAND_LINKS
- ConstantsSimulation.SIM_RAND_RECHTS,
this.hoehe
- ConstantsSimulation.SIM_RAND_OBEN
- ConstantsSimulation.SIM_RAND_UNTEN
- 25);
}
/**
* Deselektiert alle RobEA in der Liste der Akteure der Umgebung.
*/
private void deselektiereAlle() {
Iterator<Roboter> it = this.umgebung.getAgenten().iterator();
Roboter rob;
while (it.hasNext()) {
rob = it.next();
rob.setSelektiert(false);
}
if (this.eltTraceB != null) {
this.eltTraceB.deselektiereAlle();
}
}
/**
* Setzt die Ausdehnung der tatsächlichen Darstellungsgröße des Feldes.
* Beachte: Nach einem Neusetzen der Ausdehnung muss das ganze Bild einmal
* neu gezeichnet werden.
*
* @param x X-Richtung der tatsächlichen Ausdehnung des Feldes.
* @param y Y-Richtung der tatsächlichen Ausdehnung des Feldes.
*/
public void setzeDarAusdehnung(final int x,
final int y) {
this.darFeld = new byte[x][y];
this.rahmenLinksOben = null;
this.rahmenRechtsUnten = null;
}
/**
* Berechnet den Rahmen des Darstellungsfeldes:
*
* Rechteckiger Rahmen, der alle Pixel umfasst, die auf dem
* Darstellungsfeld nicht auf eine Rahmenfarbe gesetzt sind und der
* kleinste aller solcher Rahmen ist.
*/
private void berechneRahmen() {
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double maxX = 0;
double maxY = 0;
for (int i = 0; i < this.darFeld.length; i++) {
for (int j = 0; j < this.darFeld[0].length; j++) {
if (this.darFeld[i][j] != ConstantsSimulation.FARBE_RAHMEN) {
if (i < minX) {
minX = i;
}
if (i > maxX) {
maxX = i;
}
if (j < minY) {
minY = j;
}
if (j > maxY) {
maxY = j;
}
}
}
}
this.rahmenLinksOben = new Vector2D(minX, minY);
this.rahmenRechtsUnten = new Vector2D(maxX, maxY);
}
/**
* Gibt die linke obere Ecke eines rechteckigen Rahmens zurück, der alle
* Pixel umfasst, die auf dem Darstellungsfeld nicht auf eine Rahmenfarbe
* gesetzt sind und der kleinste aller solcher Rahmen ist.
*
* @return Die linke obere Ecke des nicht-gesetzt-Rahmens.
*/
public Vector2D getLinksOben() {
if (this.rahmenLinksOben == null) {
this.berechneRahmen();
}
return this.rahmenLinksOben;
}
/**
* Gibt die rechte untere Ecke eines rechteckigen Rahmens zurück, der alle
* Pixel umfasst, die auf dem Darstellungsfeld nicht auf eine Rahmenfarbe
* gesetzt sind und der kleinste aller solcher Rahmen ist.
*
* @return Die rechte untere Ecke des nicht-gesetzt-Rahmens.
*/
public Vector2D getRechtsUnten() {
if (this.rahmenRechtsUnten == null) {
this.berechneRahmen();
}
return this.rahmenRechtsUnten;
}
/**
* Berechnet die Pixel, die sich in der DARSTELLUNG seit der letzten
* Aktualisierung verändert haben und in die Grafik eingezeichnet werden
* müssen.
*/
private void berZuZeichnendePixel() {
if (this.umgebung == null) {
// Zeichnen vor Initialisierung angefordert.
return;
}
double aF = this.ausdDarX();
double bF = this.ausdDarY();
double af = this.umgebung.ausdX();
double bf = this.umgebung.ausdY();
double xFakt = aF / af;
double yFakt = bF / bf;
int xNeuErst;
int yNeuErst;
int xNeuLetzt;
int yNeuLetzt;
Pixel2D_08Bit normPix = null;
Iterator<Pixel2D_08Bit> it = this.neueNormPixel2D.iterator();
while (it.hasNext()) {
try {
normPix = it.next();
xNeuErst = (int) Math.round(normPix.x * xFakt);
yNeuErst = (int) Math.round(normPix.y * yFakt);
xNeuLetzt = (int) Math.round((normPix.x + 1) * xFakt);
yNeuLetzt = (int) Math.round((normPix.y + 1) * yFakt);
for (int x = xNeuErst; x < xNeuLetzt; x++) {
for (int y = yNeuErst; y < yNeuLetzt; y++) {
if (this.darFeld[x][y] != normPix.color()) {
this.wartPixel2D.add(new Pixel2D_08Bit(x,
y,
normPix.color()));
this.darFeld[x][y] = normPix.color();
}
}
}
} catch (final Exception e) {
StaticMethods.log(StaticMethods.LOG_WARNING,
"Norm-Pixel-Liste wurde unerlaubt modifiziert.",
this.pars);
it = this.neueNormPixel2D.iterator();
}
}
this.neueNormPixel2D.clear();
}
/**
* Gibt die Ausdehnung des Darstellungs-Feldes in X-Richtung zurück.
*
* @return Die Ausdehnung des Darstellungs-Feldes in X-Richtung.
*/
public int ausdDarX() {
return this.darFeld.length;
}
/**
* Gibt die Ausdehnung des Darstellungs-Feldes in Y-Richtung zurück.
*
* @return Die Ausdehnung des Darstellungs-Feldes in Y-Richtung.
*/
public int ausdDarY() {
return this.darFeld[0].length;
}
/**
* Gibt den RobEA zurück, der sich am nächsten zum
* angegebenen Punkt befindet. Die Koordinate bezieht sich dabei auf das
* DARSTELLUNGSfeld.
*
* @param x X-Koordinate
* @param y Y-Koordinate
* @param nicht Welche RobEA nicht zurückgegeben werden sollen.
*
* @return Der RobEA, der sich im Darstellungsfeld am nächsten zu der
* Koordinate befindet.
*/
public Roboter holeNahenDar(
final int x,
final int y,
final HashSet<Roboter> nicht) {
final double xx = x;
final double yy = y;
double normX;
double normY;
HashSet<Roboter> nichtBeachten;
if (nicht == null) {
nichtBeachten = new HashSet<Roboter>();
} else {
nichtBeachten = nicht;
}
normX = xx * this.umgebung.ausdX() / this.ausdDarX();
normY = yy * this.umgebung.ausdY() / this.ausdDarY();
normX = normX - ConstantsSimulation.SIM_RAND_LINKS;
normY = normY - ConstantsSimulation.SIM_RAND_OBEN - 25;
return this.umgebung.holeNahenNorm((int) normX,
(int) normY,
nichtBeachten);
}
/**
* Zeichnet das Feld neu.
*
* @param g Das Grafik-Objekt.
*/
@Override
public void paint(final Graphics g) {
super.paint(g);
this.maleFeld(g);
}
/**
* Zeichnet die aktuellen änderungen am Feld auf den Bildschirm.
*
* @param g Das Grafik-Feld.
*/
private void maleFeld(final Graphics g) {
Graphics g2 = img.getGraphics();
LinkedList<Pixel2D_08Bit> pixel2D;
Pixel2D_08Bit aktPix;
Iterator<Pixel2D_08Bit> it;
if (this.xRand == 0 && this.yRand == 0) {
this.xRand = g.getClipBounds().getX()
+ ConstantsSimulation.SIM_RAND_LINKS;
this.yRand = g.getClipBounds().getY()
+ ConstantsSimulation.SIM_RAND_OBEN;
}
this.berZuZeichnendePixel();
pixel2D = this.wartPixel2D;
it = pixel2D.iterator();
while (it.hasNext()) {
aktPix = it.next();
if (aktPix.color() == ConstantsSimulation.FARBE_ROB) {
g2.setColor(ConstantsSimulation.C_VORDERGRUND);
} else if (aktPix.color() == ConstantsSimulation.FARBE_SEL) {
g2.setColor(ConstantsSimulation.C_SPEZIAL);
} else if (aktPix.color() == ConstantsSimulation.FARBE_RAHMEN) {
g2.setColor(ConstantsSimulation.C_RAHMEN);
} else if (aktPix.color() == ConstantsSimulation.FARBE_GGSTD) {
g2.setColor(ConstantsSimulation.C_GEGENSTAND);
} else if (aktPix.color() >= 100 && aktPix.color() <= 125) {
// Benutzerfarben.
g2.setColor(ConstantsSimulation.C_BENUTZER[aktPix.color() - 100]);
} else if (aktPix.color() == ConstantsSimulation.FARBE_DURCHLAESSIG) {
g2.setColor(ConstantsSimulation.C_DURCHLAESSIG);
} else {
g2.setColor(this.getBackground());
}
/*
g.drawRect((int) (aktPix.x + this.xRand),
(int) (aktPix.y + this.yRand),
0,
0);
*/
g2.drawRect((int) (aktPix.x + this.xRand),
(int) (aktPix.y + this.yRand),
0,
0);
//img.setRGB(x, y, makeRGBA(0, r, 0, 255));
}
pixel2D.clear();
g.drawImage(img, 0, 0, this);
}
/**
* überladen der update-Methode ohne Löschen des aktuellen Bildes.
*
* @param g Das Graphics-Objekt.
*/
@Override
public void update(final Graphics g) {
this.paint(g);
}
/**
* St��t das Neuzeichnen zum nächstmöglichen Zeitpunkt über
* <code>repaint</code> an.
*/
private void neuZeichnen() {
if (this.allesNeuZeichnen) {
this.umgebung.alleFelderNeu();
}
this.repaint();
this.allesNeuZeichnen = false;
}
/**
* Die RUN-Methode des Threads.
*/
@Override
public void run() {
}
/**
* Beendet diesen Lauf.
*/
public void simBeenden() {
if (this.weiterlaufen) {
this.weiterlaufen = false;
this.simZ.zeitBeenden();
this.simZ.reset();
if (this.pars.getParValueBoolean(eas.statistics.ConstantsStatistics.EINFACHE_DARSTELLUNG)) {
this.pars.setParValue(eas.statistics.ConstantsStatistics.BEZIER_KONST,
eas.startSetup.marbBuilder.zeichenModi.ConstantsZeichenModi.BEZ_FANCY);
} else {
this.pars.setParValue(eas.statistics.ConstantsStatistics.BEZIER_KONST,
eas.startSetup.marbBuilder.zeichenModi.ConstantsZeichenModi.BEZ_NORMAL);
}
}
}
/**
* Startet die Darstellung einer Simulation.
*/
public void simStarten() {
this.weiterlaufen = true;
this.guiThread = new Thread(this);
this.guiThread.start();
}
/**
* @param takt Die Simulationszeit.
*/
public void registriereTakt(final SimulationsZeit takt) {
this.simZ = takt;
}
/**
* Die vom Interface geforderte Methode zum Reagieren auf änderungen am
* beobachteten Objekt.
*
* @param o Das Observierte Objekt.
* @param arg Wei� der Himmel.
*/
@Override
public void update(
final Observable o,
final Object arg) {
if (this.umgebung == null && o.getClass() == Umgebung.class) {
this.umgebung = (Umgebung) o;
}
if (this.umgebung.getNeuerPixel2D() == null) {
this.setzeSensoren();
this.zeichnenErlaubt = true;
if (this.eltFenst == null && this.zeichnenErlaubt) {
this.neuZeichnen();
this.zeichnenErlaubt = false;
}
} else {
this.neueNormPixel2D.add(this.umgebung.getNeuerPixel2D());
this.zeichnenErlaubt = false;
}
}
/**
* Gibt alle zu zeichnenden Pixel zurück.
*
* @return Alle zu zeichnenden Pixel.
*/
public LinkedList<Pixel2D_08Bit> allePixel2D() {
this.umgebung.alleFelderNeu();
this.berZuZeichnendePixel();
return this.wartPixel2D;
}
/**
* Setzt die aktuell berechneten Sensoren in das Sensorfenster ein
* und (TODO!) setzt die im Sensorfenster eingetragenen Sensoren als
* Konstante Sensoren für den RobEA.
*/
private void setzeSensoren() {
}
/**
* Leert das Feld und löscht es in der Darstellung.
*/
public void loescheFeld() {
this.umgebung.leereFeld();
this.umgebung.beendeZeichenSession();
}
/**
* @return Returns the umgebung.
*/
public Umgebung getUmgeb() {
return this.umgebung;
}
/**
* @param weiter The weiterlaufen to set.
*/
public void setWeiterlaufen(final boolean weiter) {
this.weiterlaufen = weiter;
}
}