/*
* Datei: GuiSimulation.java
* Autor(en): Lukas K�nig
* Java-Version: 1.4
* Erstellt (vor): 09.05.2008
*
* (c) Lukas K�nig, die Datei unterliegt der LGPL
* -> http://www.gnu.de/lgpl-ger.html
*/
package fmg.fmg8.graphVis;
import fmg.fmg8.graphVis.fensterDialoge.AllgemeinerDialog;
import fmg.fmg8.graphVis.fensterDialoge.WindowClosingAdapter2;
import fmg.fmg8.sonstiges.SonstMeth;
import fmg.fmg8.statistik.Parametersatz;
import fmg.fmg8.statistik.TraceBetrachter;
import fmg.fmg8.umgebung2D.Konstanten;
import fmg.fmg8.umgebung2D.Pixel;
import fmg.fmg8.umgebung2D.Roboter;
import fmg.fmg8.umgebung2D.SimulationsZeit;
import fmg.fmg8.umgebung2D.Umgebung;
import fmg.fmg8.umgebung2D.Vektor2D;
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.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Observable;
import java.util.Observer;
/**
* Klasse zur Darstellung einer Simulation in Echtzeit.
*
* @author Lukas K�nig
*/
public class GuiSimulation extends Frame
implements Observer,
Runnable {
/**
* Die VersionsUID.
*/
private static final long serialVersionUID = -2260012518107957607L;
/**
* Ob der Rahmen der freien Pixel bereits berechnet ist (Wert) oder nicht
* (null).
*/
private Vektor2D rahmenLinksOben = null;
/**
* Ob der Rahmen der freien Pixel bereits berechnet ist (Wert) oder nicht
* (null).
*/
private Vektor2D 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<Pixel> wartPixel;
/**
* Die noch nicht in die Darstellung eingef�gten NormPixel.
*/
private LinkedList<Pixel> neueNormPixel;
/**
* Das Fenster, in dem w�hrend der Simulation die Sensorwerte
* des aktiven Roboters angezeigt werden.
*/
private SensorenDarstellung sensorFenster;
/**
* 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 Parametersatz.
*/
private Parametersatz pars;
/**
* Z�hlt mit, wie oft gezeichnet wurde.
*/
private int laufendeNummer = 0;
/**
* 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 Zeitpunkt des letzten Zyklus.
*/
private long letztZeit;
/**
* 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 Parametersatz params) {
this(fensterText, sensFenst, (SteuerFenster) null, params);
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 Parametersatz params) {
super(fensterText);
this.letztZeit = System.currentTimeMillis();
this.setResizable(false);
this.pars = params;
this.zeichnenErlaubt = true;
int breit = this.pars.getFeld().length
+ Konstanten.SIM_RAND_LINKS
+ Konstanten.SIM_RAND_RECHTS
+ 0;
int hoch = this.pars.getFeld()[0].length
+ Konstanten.SIM_RAND_OBEN
+ Konstanten.SIM_RAND_UNTEN
+ 25;
this.darFeld = new byte[this.pars.getFeld().length]
[this.pars.getFeld()[0].length];
this.guiThread = null;
this.wartPixel = new LinkedList<Pixel>();
this.neueNormPixel = new LinkedList<Pixel>();
this.eltFenst = elt;
this.weiterlaufen = false;
this.breite = breit;
this.hoehe = hoch;
this.setSize(this.breite, this.hoehe);
this.feldGrAnpass();
this.setAlwaysOnTop(true);
if (this.eltFenst != null) {
this.addWindowListener(new WindowClosingAdapter2(false,
this.eltFenst,
this));
}
this.addComponentListener(
new ComponentAdapter() {
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() {
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) {
GuiSimulation.this.eltFenst.selGraph(r);
} else if (GuiSimulation.this.eltTraceB != null) {
GuiSimulation.this.eltTraceB.selGraph(r);
}
SonstMeth.log(
SonstMeth.LOG_INFO,
"Selektiert ("
+ r.getId()
+ " ["
+ r.getFitString()
+ "]"
+ ").",
GuiSimulation.this.pars);
SonstMeth.log(
SonstMeth.LOG_DEBUG,
"TransCode: " + r.getTransCodes()[0] + ".",
GuiSimulation.this.pars);
SonstMeth.log(
SonstMeth.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(this.sensFenster);
}
}
/**
* Setzt das Sensorfenster (das Fenster, in dem w�hrend der Simulation
* die Sensorwerte angezeigt werden).
*
* @param sensFenst Das Sensorfenster.
*/
public void setSensorFenster(final SensorenDarstellung sensFenst) {
this.sensorFenster = sensFenst;
}
/**
* @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
- Konstanten.SIM_RAND_LINKS
- Konstanten.SIM_RAND_RECHTS,
this.hoehe
- Konstanten.SIM_RAND_OBEN
- Konstanten.SIM_RAND_UNTEN
- 25);
}
/**
* Deselektiert alle Roboter in der Liste der Akteure der Umgebung.
*/
private void deselektiereAlle() {
Iterator<Roboter> it = this.umgebung.getAkteure().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] != Konstanten.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 Vektor2D(minX, minY);
this.rahmenRechtsUnten = new Vektor2D(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 Vektor2D 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 Vektor2D 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 = (double) this.ausdDarX();
double bF = (double) this.ausdDarY();
double af = (double) this.umgebung.ausdX();
double bf = (double) this.umgebung.ausdY();
double xFakt = aF / af;
double yFakt = bF / bf;
int xNeuErst;
int yNeuErst;
int xNeuLetzt;
int yNeuLetzt;
Pixel normPix = null;
Iterator<Pixel> it = this.neueNormPixel.iterator();
while (it.hasNext()) {
try {
normPix = (Pixel) 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.farbe()) {
this.wartPixel.add(new Pixel(x,
y,
normPix.farbe()));
this.darFeld[x][y] = normPix.farbe();
}
}
}
} catch (final Exception e) {
SonstMeth.log(SonstMeth.LOG_WARNING,
"Norm-Pixel-Liste wurde unerlaubt modifiziert.",
this.pars);
it = this.neueNormPixel.iterator();
}
}
this.neueNormPixel.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 Roboter 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 Roboter nicht zur�ckgegeben werden sollen.
*
* @return Der Roboter, 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 = (double) x;
final double yy = (double) 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 - Konstanten.SIM_RAND_LINKS;
normY = normY - Konstanten.SIM_RAND_OBEN - 25;
return this.umgebung.holeNahenNorm((int) normX,
(int) normY,
nichtBeachten);
}
/**
* Zeichnet das Feld neu.
*
* @param g Das Grafik-Objekt.
*/
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) {
LinkedList<Pixel> pixel;
Pixel aktPix;
Iterator<Pixel> it;
if (this.xRand == 0 && this.yRand == 0) {
this.xRand = g.getClipBounds().getX()
+ Konstanten.SIM_RAND_LINKS;
this.yRand = g.getClipBounds().getY()
+ Konstanten.SIM_RAND_OBEN;
}
this.laufendeNummer++;
this.berZuZeichnendePixel();
pixel = this.wartPixel;
it = pixel.iterator();
while (it.hasNext()) {
aktPix = (Pixel) it.next();
if (aktPix.farbe() == Konstanten.FARBE_ROB) {
g.setColor(Konstanten.C_VORDERGRUND);
} else if (aktPix.farbe() == Konstanten.FARBE_SEL) {
g.setColor(Konstanten.C_SPEZIAL);
} else if (aktPix.farbe() == Konstanten.FARBE_RAHMEN) {
g.setColor(Konstanten.C_RAHMEN);
} else if (aktPix.farbe() == Konstanten.FARBE_GGSTD) {
g.setColor(Konstanten.C_GEGENSTAND);
} else if (aktPix.farbe() >= 100 && aktPix.farbe() <= 125) {
// Benutzerfarben.
g.setColor(Konstanten.C_BENUTZER[aktPix.farbe() - 100]);
} else if (aktPix.farbe() == Konstanten.FARBE_DURCHLAESSIG) {
g.setColor(Konstanten.C_DURCHLAESSIG);
} else {
g.setColor(this.getBackground());
}
g.drawRect((int) (aktPix.x + this.xRand),
(int) (aktPix.y + this.yRand),
0,
0);
}
pixel.clear();
}
/**
* �berladen der update-Methode ohne L�schen des aktuellen Bildes.
*
* @param g Das Graphics-Objekt.
*/
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.
*/
public void run() {
long restVerz;
while (this.weiterlaufen) {
restVerz = this.pars.getVerzoeg().longValue()
- (System.currentTimeMillis() - this.letztZeit);
while (restVerz > 0) {
restVerz = this.pars.getVerzoeg().longValue()
- (System.currentTimeMillis() - this.letztZeit);
}
this.letztZeit = System.currentTimeMillis();
if (this.neueNormPixel.size() == 0
&& this.wartPixel.size() == 0) {
this.simZ.step();
if (!this.umgebung.isSimLaeuft()) {
this.weiterlaufen = false;
}
}
if (this.zeichnenErlaubt) {
this.neuZeichnen();
this.zeichnenErlaubt = false;
}
}
}
/**
* Beendet diesen Lauf.
*/
public void simBeenden() {
if (this.weiterlaufen) {
this.weiterlaufen = false;
boolean speichern = this.pars.getAufnSp().booleanValue();
String name = this.pars.getAufnDat();
String b1, b2;
b1 = "Ja";
b2 = "Nein";
ArrayList<String> buttons = new ArrayList<String>(2);
buttons.add(b1);
buttons.add(b2);
AllgemeinerDialog allg = new AllgemeinerDialog(this,
null,
"Soll die Aufnahme gespeichert "
+ "werden?",
buttons,
this.pars.getAufnDat(),
1,
50,
true);
if (speichern) {
allg.setVisible(true);
if (allg.getResult().equals(b1)) {
this.pars.setAufnSp(true);
} else {
this.pars.setAufnSp(false);
}
}
this.pars.setAufnDat(allg.getText());
this.simZ.zeitBeenden();
this.pars.setAufnDat(name);
this.pars.setAufnSp(speichern);
if (this.pars.getEinfacheDar()) {
this.pars.setBezier(
fmg.fmg8.graphVis.zeichenModi.Konstanten.BEZ_FANCY);
} else {
this.pars.setBezier(
fmg.fmg8.graphVis.zeichenModi.Konstanten.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.
*/
public void update(final Observable o,
final Object arg) {
if (this.umgebung == null && o.getClass() == Umgebung.class) {
this.umgebung = (Umgebung) o;
}
if (this.umgebung.getNeuerPixel() == null) {
this.setzeSensoren();
this.zeichnenErlaubt = true;
if (this.eltFenst == null && this.zeichnenErlaubt) {
this.neuZeichnen();
this.zeichnenErlaubt = false;
}
} else {
this.neueNormPixel.add(this.umgebung.getNeuerPixel());
this.zeichnenErlaubt = false;
}
}
/**
* Gibt alle zu zeichnenden Pixel zur�ck.
*
* @return Alle zu zeichnenden Pixel.
*/
public LinkedList<Pixel> allePixel() {
this.umgebung.alleFelderNeu();
this.berZuZeichnendePixel();
return this.wartPixel;
}
/**
* Setzt die aktuell berechneten Sensoren in das Sensorfenster ein
* und (TODO!) setzt die im Sensorfenster eingetragenen Sensoren als
* Konstante Sensoren f�r den Roboter.
*/
private void setzeSensoren() {
Iterator<Roboter> it = this.umgebung.getAkteure().iterator();
Roboter aktRob;
while (it.hasNext()) {
aktRob = it.next();
if (aktRob.isSelektiert()
&& this.sensFenster != null
&& this.sensorFenster.isVisible()) {
// Zeigt die berechneten Sensoren im Sensorfenster an.
for (int i = 0; i < Konstanten.SENSOR_ANZAHL_BERECHNET; i++) {
this.sensorFenster.setSensor(i, aktRob.getSensorWert(i));
}
this.sensorFenster.setFitness(aktRob.getFitString());
}
}
// // Setze die konstanten Sensoren auf die im Fenster eingetragenen
// // Werte.
// for (int i = Konstanten.SENSOR_ANZAHL_BERECHNET;
// i < Konstanten.SENSOR_ANZAHL_ANZEIGEN;
// i++) {
// sensoren[i] = this.sensorFenster.getSensor(i);
// }
// }
}
/**
* 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;
}
}