/*
* Dateiname: Polygon2D.java
* Autor(en): Lukas K�nig
* Java-Version: 6.0
* Letzte Aenderung: 19.12.2008
*
* (c) Lukas K�nig, die Datei unterliegt der LGPL
* -> http://www.gnu.de/lgpl-ger.html
*/
package fmg.fmg8.graphVis.zeichenModi;
import java.awt.Polygon;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import fmg.fmg8.sonstiges.SonstMeth;
import fmg.fmg8.statistik.Parametersatz;
import fmg.fmg8.umgebung2D.Vektor2D;
/**
* Implementierung eines Polygons.
*
* @author Lukas K�nig
*/
public class Polygon2D extends ArrayList<Vektor2D> {
/**
* Die X-Werte der Punkte.
*/
private ArrayList<Double> xPoints;
/**
* Die Y-Werte der Punkte.
*/
private ArrayList<Double> yPoints;
/**
* Umschlie�endes Rechteck.
*/
private Rechteck2D boundingBox;
/**
* Flag, mit dem garantiert werden kann, dass ein Punkt nur hinzugef�gt
* werden kann, wenn das Polygon dadurch einfach bleibt.
*/
private boolean garantiereEinfach;
/**
* Die Parameter.
*/
private Parametersatz pars;
/**
* Die UID.
*/
private static final long serialVersionUID = -8522419836028979412L;
/**
* Konstruktor aus Vaterklasse.
*/
public Polygon2D() {
super();
this.xPoints = new ArrayList<Double>();
this.yPoints = new ArrayList<Double>();
}
/**
* Konstruktor aus Vaterklasse.
*
* @param initialCapacity Mit initialer Kapazit�t.
*/
public Polygon2D(final int initialCapacity) {
super(initialCapacity);
this.xPoints = new ArrayList<Double>(initialCapacity);
this.yPoints = new ArrayList<Double>(initialCapacity);
}
/**
* Konstruktor aus Vaterklasse.
*
* @param c Aus Collektion.
*/
public Polygon2D(final Collection< ? extends Vektor2D> c) {
super(c);
throw new RuntimeException("Konstruktor noch nicht definiert.");
}
/**
* Berechnet einen Schnittpunkt der Strecke s1 mit dem Polygon, falls es
* einen gibt.
*
* @param s1 Die Strecke.
*
* @return Ein Schnittpunkt der Strecke mit dem Polygon, falls einer
* vorhanden ist, <code>null</code> sonst.
*/
public Vektor2D schneidet(final Strecke2D s1) {
Strecke2D s2
= new Strecke2D(this.get(this.nPoints() - 1), this.get(0));
boolean flip = false;
Vektor2D schnitt;
for (int i = 1; i <= this.nPoints(); i++) {
schnitt = s1.schnPkt(s2);
if (schnitt != null) {
return schnitt;
}
if (flip && i < this.nPoints()) {
s2.setEndPkt(this.get(i));
} else if (i < this.nPoints()) {
s2.setAnfPkt(this.get(i));
}
flip = !flip;
}
return null;
}
/**
* F�gt einen Punkt ans Ende des Polygons.
*
* @param punkt Der einzuf�gende Punkt.
*
* @return Ob ein Element eingef�gt wurde.
*/
@Override
public boolean add(final Vektor2D punkt) {
this.add(this.nPoints(), punkt);
return true;
}
/**
* F�gt einen Punkt an einer bestimmten Stelle zum Polygon hinzu.
*
* Wenn Einfachheit erzwungen wird, dann wird gepr�ft, ob die beiden
* neuen Strecken, die zum Polygon durch den Punkt hinzukommen w�rden,
* das Polygon schneiden und in diesem Fall der Punkt ignoriert.
*
* @param index Der Index des Elements.
* @param punkt Das einzuf�gende Element.
*/
@Override
public void add(final int index, final Vektor2D punkt) {
Strecke2D s1;
Strecke2D s2;
if (this.garantiereEinfach && this.nPoints() > 2) {
s1 = new Strecke2D(this.get(index - 1), punkt);
if (index < this.nPoints()) {
s2 = new Strecke2D(punkt, this.get(index));
} else {
s2 = new Strecke2D(punkt, this.get(0));
}
if (this.schneidet(s1) != null || this.schneidet(s2) != null) {
if (this.pars != null) {
SonstMeth.log(SonstMeth.LOG_DEBUG,
"Punkt nicht eingefuegt: " + punkt,
this.pars);
}
return;
}
}
super.add(index, punkt);
this.xPoints.add(index, punkt.x);
this.yPoints.add(index, punkt.y);
if (this.getBoundingBox() == null) {
this.createBoundingBox();
} else {
this.erweitereBoundingBox(punkt);
}
}
/**
* Erzeugt den umschlie�enden Rahmen neu.
*/
private void createBoundingBox() {
if (this.nPoints() == 0) {
return;
}
Vektor2D punkt = this.get(0);
this.boundingBox = new Rechteck2D(punkt, punkt);
for (int i = 1; i < this.nPoints(); i++) {
this.erweitereBoundingBox(this.get(i));
}
}
/**
* Erweitert die BoundingBox um einen Punkt.
*
* @param punkt Der einzuschlie�ende Punkt.
*/
private void erweitereBoundingBox(final Vektor2D punkt) {
if (punkt.x < this.boundingBox.liObEcke().x) {
this.boundingBox.setLinks(punkt.x);
} else if (punkt.x > this.boundingBox.getRechtUntEcke().x) {
this.boundingBox.setRechts(punkt.x);
}
if (punkt.y < this.boundingBox.liObEcke().y) {
this.boundingBox.setOben(punkt.y);
} else if (punkt.y > this.boundingBox.getRechtUntEcke().y) {
this.boundingBox.setUnten(punkt.y);
}
}
/**
* Ung�ltige Funktion auf Polygonen.
*
* @param c Kollektion.
*
* @return Ob ge�ndert.
*/
@Override
public boolean addAll(final Collection< ? extends Vektor2D> c) {
throw new RuntimeException("Funktion nicht unterst�tzt f�r Polygone");
}
/**
* Ung�ltige Funktion auf Polygonen.
*
* @param c Kollektion.
* @param index Index.
*
* @return Ob ge�ndert.
*/
@Override
public boolean addAll(final int index,
final Collection< ? extends Vektor2D> c) {
throw new RuntimeException("Funktion nicht unterst�tzt f�r Polygone");
}
/**
* L�scht alle Punkte des Polygons.
*/
@Override
public void clear() {
super.clear();
this.boundingBox = null;
this.xPoints.clear();
this.yPoints.clear();
}
/**
* L�scht den Punkt mit der angegebenen Nummer. Vorsicht, sollte nicht
* h�ufig alternierend mit add-Operationen eingesetzt werden, da nach
* remove beim n�chsten add die boundingBox neu berechnet werden muss.
*
* @param index Die Nummer.
*
* @return Das entfernte Element.
*/
@Override
public Vektor2D remove(final int index) {
this.xPoints.remove(index);
this.yPoints.remove(index);
this.boundingBox = null;
return super.remove(index);
}
/**
* Verschiebt das Polygon um den angegebenen Vektor.
*
* @param vek Der Verschiebevektor.
*/
public void verschiebe(final Vektor2D vek) {
for (Vektor2D pVek : this) {
pVek.add(vek);
}
if (this.boundingBox != null) {
this.boundingBox.verschiebe(vek);
}
}
/**
* Skaliert das Polygon um die angegebene X- und Y-Richtung und den
* angegebenen Mittelpunkt.
*
* @param skal Der Skalierungsvektor.
* @param mitte Der Mittelpunkt des Skalierens.
*/
public void skaliere(final Vektor2D mitte, final Vektor2D skal) {
for (Vektor2D pVek : this) {
pVek.skaliere(mitte, skal);
}
}
/**
* Dreht das Polygon um den angegebenen Winkel und den angegebenen
* Mittelpunkt.
*
* @param mitte Der Mittelpunkt.
* @param winkel Der Drehwinkel.
*/
public void drehe(final Vektor2D mitte, final double winkel) {
for (Vektor2D pVek : this) {
pVek.drehe(mitte, winkel);
}
}
/**
* Ung�ltige Funktion auf Polygonen.
*
* @param o Objekt.
*
* @return Ob ge�ndert.
*/
@Override
public boolean remove(final Object o) {
throw new RuntimeException("Funktion nicht unterst�tzt f�r Polygone");
}
/**
* L�scht Objekte in einem Bereich.
*
* @param fromIndex Von.
* @param toIndex Bis.
*/
@Override
protected void removeRange(final int fromIndex, final int toIndex) {
for (int i = fromIndex; i < toIndex; i++) {
this.remove(fromIndex);
}
}
/**
* Setzt einen Punkt neu. Vorsicht, ineffizient; sollte nur zu Testzwecken
* oder aus Bequemlichkeit, wenn Zeit keine Rolle spielt, eingesetzt
* werden (siehe auch Kommentar zu remove).
*
* @param index Der neu zu setzende Index.
* @param element Das neue Element.
*
* @return Das Element, das vorher an der Position war. Wenn das Element
* nicht gesetzt werden konnte, wird <code>null</code>
* zur�ckgegeben.
*/
@Override
public Vektor2D set(final int index, final Vektor2D element) {
Vektor2D alt = this.get(index);
this.remove(index);
this.add(index, element);
if (this.garantiereEinfach) {
if (!this.isEinfach()) {
this.remove(index);
this.add(index, alt);
return null;
}
}
return alt;
}
/**
* @return Returns the xPoints.
*/
public ArrayList<Double> xPoints() {
return this.xPoints;
}
/**
* @return Returns the yPoints.
*/
public ArrayList<Double> yPoints() {
return this.yPoints;
}
/**
* @return Die Anzahl der Punkte.
*/
public int nPoints() {
return this.size();
}
/**
* @return Die BoundingBox.
*/
public Rechteck2D getBoundingBox() {
if (this.boundingBox == null) {
this.createBoundingBox();
}
return this.boundingBox;
}
/**
* Erzeugt ein Java-Polygon aus <code>this</code> ohne zu skalieren oder
* zu verschieben.
*
* @return Polygon.
*/
public Polygon toPol() {
return this.toPol(1, Vektor2D.NULL_VEKTOR);
}
/**
* Erzeugt ein Java-Polygon aus <code>this</code>.
*
* @param skalierung Die Skalierung des Polygons.
* @param versch Die Verschiebung des Polygons NACH der Skalierung.
*
* @return Polygon.
*/
public Polygon toPol(final double skalierung, final Vektor2D versch) {
Polygon pol = new Polygon();
Iterator<Vektor2D> it = this.iterator();
Vektor2D punkt;
while (it.hasNext()) {
punkt = it.next();
pol.addPoint((int) (punkt.x * skalierung + versch.x),
(int) (punkt.y * skalierung + versch.y));
}
return pol;
}
/**
* Normalisiert das aktuelle Objekt, so dass der Abstand
* zwischen zwei in der Liste benachbarten Punkten immer dem
* Durchschnittsabstand der Punkte in der urspr�nglichen Liste entspricht,
* wobei der Kurvenverlauf "m�glichst nah" am Verlauf der urspr�nglichen
* Kurve liegt.
*/
public void normalisiere2() {
Polygon2D normArr = new Polygon2D(this.nPoints());
ArrayList<Double> abstaende = new ArrayList<Double>(this.nPoints());
Iterator<Vektor2D> it = this.iterator();
Vektor2D letztV;
Vektor2D aktV = new Vektor2D(Vektor2D.NULL_VEKTOR);
double aktAbst;
double letztAbst;
Vektor2D aktRicht;
double durchAbst;
int i = 0;
if (this.nPoints() <= 2) {
return;
}
letztV = this.get(0);
letztAbst = 0;
while (it.hasNext()) {
aktV = it.next();
aktAbst = aktV.distanz(letztV) + letztAbst;
abstaende.add(aktAbst);
letztAbst = aktAbst;
letztV = aktV;
}
durchAbst = abstaende.get(abstaende.size() - 1)
/ (abstaende.size() - 1);
aktV = new Vektor2D(this.get(0));
normArr.add(aktV);
aktAbst = 0;
while (i < this.nPoints()) {
if (i + 1 < this.nPoints()) {
aktRicht = new Vektor2D(this.get(i + 1));
aktRicht.sub(this.get(i));
if (this.get(i).distanz(this.get(i + 1)) >= durchAbst) {
aktRicht.laengeFestlegen(durchAbst);
while (aktAbst < abstaende.get(i + 1)) {
aktV = new Vektor2D(aktV);
aktV.add(aktRicht);
normArr.add(aktV);
aktAbst += durchAbst;
}
} else {
double zwischAbst = 0;
double aktDist;
while (i + 1 < nPoints()) {
aktDist = this.get(i).distanz(this.get(i + 1));
if (zwischAbst + aktDist
< durchAbst) {
zwischAbst += aktDist;
i++;
} else {
aktRicht = new Vektor2D(this.get(i + 1));
aktRicht.sub(this.get(i));
aktRicht.laengeFestlegen(durchAbst - zwischAbst);
aktV = new Vektor2D(this.get(i));
aktV.add(aktRicht);
normArr.add(aktV);
aktAbst += durchAbst;
break;
}
}
}
}
i++;
}
this.clear();
for (int j = 0; j < normArr.size(); j++) {
this.add(normArr.get(j));
}
}
/**
* Normalisiert das aktuelle Objekt, so dass der Abstand
* zwischen zwei in der Liste benachbarten Punkten immer dem
* Durchschnittsabstand der Punkte in der urspr�nglichen Liste entspricht,
* wobei der Kurvenverlauf "m�glichst nah" am Verlauf der urspr�nglichen
* Kurve liegt. Dabei kann die Anzahl der Punkte sich um 1 erh�hen.
*
* @return Die normalisierte Liste.
*/
public Polygon2D normalisiere() {
Polygon2D normArr = new Polygon2D(this.nPoints());
ArrayList<Double> abstaende = new ArrayList<Double>(this.nPoints());
Iterator<Vektor2D> it = this.iterator();
Vektor2D letztV;
Vektor2D aktV = new Vektor2D(Vektor2D.NULL_VEKTOR);
double aktAbst;
double letztAbst;
Vektor2D aktRicht;
double durchAbst;
int i = 0;
if (this.nPoints() <= 1) {
return this;
}
letztV = this.get(0);
letztAbst = 0;
while (it.hasNext()) {
aktV = it.next();
aktAbst = aktV.distanz(letztV) + letztAbst;
abstaende.add(aktAbst);
letztAbst = aktAbst;
letztV = aktV;
}
durchAbst = abstaende.get(abstaende.size() - 1)
/ (abstaende.size() - 1);
aktV = new Vektor2D(this.get(0));
normArr.add(aktV);
aktAbst = 0;
while (i < this.nPoints()) {
if (i + 1 < this.nPoints()) {
aktRicht = new Vektor2D(this.get(i + 1));
aktRicht.sub(this.get(i));
aktRicht.laengeFestlegen(durchAbst);
while (aktAbst < abstaende.get(i + 1)) {
aktV = new Vektor2D(aktV);
aktV.add(aktRicht);
normArr.add(aktV);
aktAbst += durchAbst;
}
}
i++;
}
if (normArr.size() - this.nPoints() != 1
&& normArr.size() - this.nPoints() != 0) {
throw new RuntimeException(
"Normalisierung lieferte zu viele / wenige Punkte: "
+ normArr.size() + " vs. " + this.nPoints());
}
return normArr;
}
/**
* Berechnet, ob der �bergebene Punkt sich innerhalb der Grenzen des
* Polygons befindet.
*
* @param punkt Der Punkt, dessen Eigenschaft �berpr�ft werden soll.
*
* @return Ob der Punkt innerhalb des Polygons liegt.
*/
public boolean istPunktInnen(final Vektor2D punkt) {
boolean inside = false;
double x1 = this.xPoints.get(this.nPoints() - 1);
double y1 = this.yPoints.get(this.nPoints() - 1);
double x2 = this.xPoints().get(0);
double y2 = this.yPoints().get(0);
boolean startUeber;
boolean endeUeber;
if (y1 >= punkt.y) {
startUeber = true;
} else {
startUeber = false;
}
for (int i = 1; i <= this.nPoints(); i++) {
if (y2 >= punkt.y) {
endeUeber = true;
} else {
endeUeber = false;
}
if (startUeber != endeUeber) {
if ((y2 - punkt.y) * (x2 - x1) <= (y2 - y1) * (x2 - punkt.x)) {
if (endeUeber) {
inside = !inside;
}
} else {
if (!endeUeber) {
inside = !inside;
}
}
}
startUeber = endeUeber;
y1 = y2;
x1 = x2;
if (i < this.nPoints()) {
x2 = this.xPoints.get(i);
y2 = this.yPoints.get(i);
}
}
return inside;
}
/**
* @return Eine einfache Variante von <code>this</code>.
*/
public Polygon2D toEinfach() {
Polygon2D neu = new Polygon2D(this.nPoints());
Vektor2D akt;
Iterator<Vektor2D> it = this.iterator();
neu.erzwingeEinfach(true);
while (it.hasNext()) {
akt = it.next();
neu.add(akt);
}
return neu;
}
/**
* @return Ob das Polygon einfach ist.
*/
public boolean isEinfach() {
// TODO:
return false;
}
/**
* Wenn Einfachheit erzwungen wird, dann wird gepr�ft, ob die beiden
* neuen Strecken, die zum Polygon durch den Punkt hinzukommen w�rden,
* das Polygon schneiden und in diesem Fall der Punkt ignoriert.
*
* Zu beachten ist, dass das Polygon in jedem Aufbauschritt einfach
* bleiben muss und es nicht m�glich ist, ein zwischenzeitlich komplexes
* Polygon zu erzeugen und nachtr�glich einfach zu machen.
*
* @param setzen The garantiereEinfach to set.
*
* @return Ob Einfachheit erzwungen werden konnte.
*/
public boolean erzwingeEinfach(final boolean setzen) {
this.garantiereEinfach = setzen;
if (this.nPoints() > 0 && !this.isEinfach()) {
this.garantiereEinfach = false;
}
return this.garantiereEinfach;
}
/**
* @param params The pars to set.
*/
public void setPars(final Parametersatz params) {
this.pars = params;
}
/**
* @return Mittelpunkt des Polygons.
*/
public Vektor2D mittelpunkt() {
return new Vektor2D(
this.getBoundingBox().liObEcke().x
+ this.getBoundingBox().getBreite() / 2,
this.getBoundingBox().liObEcke().y
+ this.getBoundingBox().getHoehe() / 2);
}
/**
* Gibt die Position des (ersten) Vektors zur�ck, der genau dem
* �bergebenen Objekt entspricht.
*
* @param vek Der zu vergleichende Vektor.
*
* @return Die (erste) Position des Vektors. Falls nicht vorhanden: -1.
*/
public int realIndexOf(final Vektor2D vek) {
for (int i = 0; i < this.size(); i++) {
if (this.get(i) == vek) {
return i;
}
}
return -1;
}
}