/*
* Datei: Geometrie2D.java
* Autor(en): Lukas K�nig
* Java-Version: 6.0
* Letzte Aenderung: 23.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.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.imageio.ImageIO;
import fmg.fmg8.sonstiges.SonstMeth;
import fmg.fmg8.statistik.Parametersatz;
import fmg.fmg8.umgebung2D.Vektor2D;
/**
* Kollektion allgemeiner geometrischer Methoden f�r den 2-dimensionalen
* Raum.
*
* @author Lukas K�nig
*/
public class Geometrie2D {
/**
* Berechnet eine Bezierkurve im Polygon (p1, p2, p3, p4). Der Abstand der
* Punkte wird durch <code>step</code> (0..1) gesteuert.
*
* @param p0 Punkt.
* @param p1 Punkt.
* @param p2 Punkt.
* @param p3 Punkt.
* @param step Schrittgr��e.
*
* @return Die Punkte der Bezierkurve.
*/
public static Polygon2D bezierKurve(
final Vektor2D p0,
final Vektor2D p1,
final Vektor2D p2,
final Vektor2D p3,
final double step) {
Polygon2D kurve = new Polygon2D((int) (1 / step) + 1);
Vektor2D punkt;
Vektor2D dreiP0 = (new Vektor2D(p0));
Vektor2D dreiP1 = (new Vektor2D(p1));
Vektor2D dreiP2 = (new Vektor2D(p2));
Vektor2D sechsP1 = (new Vektor2D(p1));
dreiP0.mult(3);
dreiP1.mult(3);
dreiP2.mult(3);
sechsP1.mult(6);
Vektor2D punkt2, punkt3;
double tQuad, tKub;
for (double t = 0; t <= 1; t += step) {
tQuad = t * t;
tKub = t * tQuad;
punkt = new Vektor2D(dreiP1);
punkt.sub(p0);
punkt.sub(dreiP2);
punkt.add(p3);
punkt.mult(tKub);
punkt2 = new Vektor2D(dreiP0);
punkt2.sub(sechsP1);
punkt2.add(dreiP2);
punkt2.mult(tQuad);
punkt3 = new Vektor2D(dreiP1);
punkt3.sub(dreiP0);
punkt3.mult(t);
punkt.add(punkt2);
punkt.add(punkt3);
punkt.add(p0);
kurve.add(punkt);
}
return kurve;
}
/**
* Gibt ein Kreis-Polygon zur�ck.
*
* @param mitte Der Mittelpunkt.
* @param radius Der Radius.
* @param anzahl Die Schrittzahl.
*
* @return Das Kreispolygon.
*/
public static Polygon kreis(final Vektor2D mitte,
final double radius,
final long anzahl) {
double schritt = 2 * Math.PI / anzahl;
Polygon kreis = new Polygon();
double winkel;
double x;
double y;
for (winkel = 0; winkel <= 2 * Math.PI; winkel += schritt) {
x = mitte.x + radius * Math.sin(winkel);
y = mitte.y + radius * Math.cos(winkel);
kreis.addPoint((int) x, (int) y);
}
return kreis;
}
/**
* Zeichnet eine Menge von Polygonen und (entsprechend der Vereinbarung
* im Interface ZeichenKit codierten) Strings, die als Liste �bergeben
* werden.
*
* @param g Das Grafikobjekt, in das gezeichnet werden soll.
* @param polListe Die Liste der Polygone.
* @param params Die Parameter.
*/
public static void maleObjListe(
final Graphics g,
final List<Object> polListe,
final Parametersatz params) {
Iterator<Object> it;
Object obj;
int x;
int y;
String str;
AusgMerkm ausgabe;
Kreis2D kreis;
boolean fuellungDrucken = Konstanten.STANDARD_FUELL_DRUCKEN;
boolean rahmenDrucken = Konstanten.STANDARD_RAHMEN_DRUCKEN;
Color fuellungFarbe = Konstanten.STANDARD_FUELL_FARBE;
Color rahmenFarbe = Konstanten.STANDARD_RAHMEN_FARBE;
String schriftart;
int schriftstil;
int schriftgroesse;
GradientPaint gp = null;
Graphics2D g2 = (Graphics2D) g;
BasicStroke s1 = new BasicStroke(BasicStroke.JOIN_MITER);
BasicStroke s2 = new BasicStroke(BasicStroke.CAP_BUTT);
if (params.getEinfacheDar()) {
g2.setStroke(s2);
} else {
g2.setStroke(s1);
}
it = polListe.iterator();
while (it.hasNext()) {
obj = it.next();
if (obj.getClass().equals(Polygon.class)) {
if (fuellungDrucken) {
if (gp == null) {
g2.setColor(fuellungFarbe);
} else {
g2.setPaint(gp);
}
g2.fillPolygon((Polygon) obj);
}
if (rahmenDrucken) {
g2.setColor(rahmenFarbe);
g2.drawPolygon((Polygon) obj);
}
} else if (obj.getClass().equals(Kreis2D.class)) {
kreis = (Kreis2D) obj;
if (fuellungDrucken) {
if (gp == null) {
g2.setColor(fuellungFarbe);
} else {
g2.setPaint(gp);
}
g2.fillOval(
(int) (kreis.holeMitte().x - kreis.holeRadius()),
(int) (kreis.holeMitte().y - kreis.holeRadius()),
(int) (kreis.holeRadius() * 2),
(int) (kreis.holeRadius() * 2));
}
if (rahmenDrucken) {
g2.setColor(rahmenFarbe);
g2.drawOval(
(int) (kreis.holeMitte().x - kreis.holeRadius()),
(int) (kreis.holeMitte().y - kreis.holeRadius()),
(int) (kreis.holeRadius() * 2),
(int) (kreis.holeRadius() * 2));
}
} else if (obj.getClass().equals(AusgMerkm.class)) {
ausgabe = (AusgMerkm) obj;
fuellungDrucken = ausgabe.holeFuellungDrucken();
rahmenDrucken = ausgabe.holeRahmenDrucken();
fuellungFarbe = ausgabe.getFuellFarbe();
rahmenFarbe = ausgabe.getRahmenFarbe();
schriftart = ausgabe.getFontName();
schriftstil = ausgabe.getFontStyle();
schriftgroesse = ausgabe.getFontSize();
g2.setFont(new Font(schriftart, schriftstil, schriftgroesse));
gp = ausgabe.getGradPaint();
} else {
x = ((Integer) obj).intValue();
obj = it.next();
y = ((Integer) obj).intValue();
obj = it.next();
str = (String) obj;
g2.setColor(rahmenFarbe);
g2.drawString(str, x, y);
}
}
// R�cksetzen der Vordergrundfarbe.
g2.setColor(Konstanten.STANDARD_RAHMEN_FARBE);
}
/**
* Gibt die St�tzpunkte einer Kurve zur�ck, die eine Ann�herung an eine
* Kurve aus beliebig vielen Einzelst�cken aus Bezierkurven dritten Grades
* ist.
*
* @param p Die St�tzpunkte der Bezierkurven.
* @param v Die Vektoren an den inneren St�tzpunkten.
* @param k Die Multiplikatoren f�r die inneren Vektoren.
* @param step Die Schrittweite bei der Bezierberechnung..
* @param params Die Parameter.
*
* @return Die Kurve aus Beziersegmenten.
*/
public static Polygon2D multiBezier(
final Polygon2D p,
final Polygon2D v,
final ArrayList<Double> k,
final double step,
final Parametersatz params) {
Polygon2D kurve
= new Polygon2D((int) ((p.size() - 1) / (step + 1)));
Polygon2D bezier;
Vektor2D v1, v2;
if (p.size() < 2 || p.size() != v.size() || v.size() != k.size() + 2) {
SonstMeth.log(SonstMeth.LOG_ERROR,
"Falsche Angaben bei Aufruf von multiBezier",
params);
}
v1 = new Vektor2D(v.get(0));
v2 = new Vektor2D(p.get(1));
v1.add(p.get(0));
v2.sub(v.get(1));
bezier = fmg.fmg8.graphVis.zeichenModi.Geometrie2D.bezierKurve(
p.get(0), v1, v2, p.get(1), step);
kurve.addAll(bezier);
for (int i = 1; i < p.size() - 1; i++) {
v1 = new Vektor2D(v.get(i));
v2 = new Vektor2D(p.get(i + 1));
v1.mult(k.get(i - 1));
v1.add(p.get(i));
v2.sub(v.get(i + 1));
bezier = fmg.fmg8.graphVis.zeichenModi.Geometrie2D.bezierKurve(
p.get(i), v1, v2, p.get(i + 1), step);
kurve.addAll(bezier);
}
return kurve;
}
/**
* Ermittelt einen umschlie�enden Rahmen um alle Objekte in der Liste.
*
* @param objekte Die Objekte.
*
* @return Die linke obere und rechte untere Koordinate des
* Rahmens.
*/
public static Polygon2D rahmen(final List<Object> objekte) {
Vektor2D maxPkt = new Vektor2D(Double.NEGATIVE_INFINITY,
Double.NEGATIVE_INFINITY);
Vektor2D minPkt = new Vektor2D(Double.POSITIVE_INFINITY,
Double.POSITIVE_INFINITY);
Vektor2D zwischMax = new Vektor2D(Vektor2D.NULL_VEKTOR);
Vektor2D zwischMin = new Vektor2D(Vektor2D.NULL_VEKTOR);
Polygon2D rahmen = new Polygon2D(2);
int stringPos = 0;
int stringX = 0;
int stringY = 0;
for (Object o : objekte) {
if (o.getClass().equals(Polygon.class)) {
Polygon p = (Polygon) o;
zwischMax.x = p.getBounds().getMaxX();
zwischMax.y = p.getBounds().getMaxY();
zwischMin.x = p.getBounds().getMinX();
zwischMin.y = p.getBounds().getMinY();
} else if (o.getClass().equals(Kreis2D.class)) {
Kreis2D k = (Kreis2D) o;
zwischMax = new Vektor2D(k.holeMitte());
zwischMin = new Vektor2D(k.holeMitte());
zwischMax.add(new Vektor2D(k.holeRadius(), k.holeRadius()));
zwischMin.sub(new Vektor2D(k.holeRadius(), k.holeRadius()));
} else if (o.getClass().equals(Integer.class)) {
if (stringPos == 0) {
stringX = (Integer) o;
stringPos++;
} else {
stringY = (Integer) o;
stringPos = 0;
}
} else if (o.getClass().equals(String.class)) {
String s = (String) o;
double stringHeight = 10;
double stringWidth = s.length() * 6;
zwischMin = new Vektor2D(stringX, stringY - stringHeight);
zwischMax = new Vektor2D(stringX + stringWidth, stringY);
} else {
zwischMax = new Vektor2D(maxPkt);
zwischMin = new Vektor2D(minPkt);
}
if (zwischMax.x > maxPkt.x) {
maxPkt.x = zwischMax.x;
}
if (zwischMax.y > maxPkt.y) {
maxPkt.y = zwischMax.y;
}
if (zwischMin.x < minPkt.x) {
minPkt.x = zwischMin.x;
}
if (zwischMin.y < minPkt.y) {
minPkt.y = zwischMin.y;
}
}
rahmen.add(minPkt);
rahmen.add(maxPkt);
return rahmen;
}
/**
* Erzeugt eine ausgabef�hige Objektliste aus einer Liste von Polygonen
* mit zugeh�rigen Ausgabemerkmalen.
*
* @param polAusg Die Polygone mit Ausgabemerkmalen.
* @param skalierung Die Skalierung.
* @param versch Die Verschiebung.
*
* @return Die ausgabef�hige Objektliste.
*/
public static List<Object> erzeugeObjList(
final Pol2DMitAusgMerkm[] polAusg,
final double skalierung,
final Vektor2D versch) {
List<Object> oL = new ArrayList<Object>(2 * polAusg.length);
AusgMerkm aktAusg;
Polygon2D aktPol;
Point2D p1;
Point2D p2;
GradientPaint grad;
Color fuell, rahmen;
for (int i = 0; i < polAusg.length; i++) {
if (polAusg[i] != null) {
aktAusg = polAusg[i].getAusg();
aktPol = polAusg[i].getPol();
if (aktAusg != null) {
if (aktAusg.getGradPaint() != null) {
p1 = aktAusg.getGradPaint().getPoint1();
p2 = aktAusg.getGradPaint().getPoint2();
grad = new GradientPaint(
(int) (p1.getX() * skalierung),
(int) (p1.getY() * skalierung),
aktAusg.getGradPaint().getColor1(),
(int) (p2.getX() * skalierung),
(int) (p2.getY() * skalierung),
aktAusg.getGradPaint().getColor2());
fuell = aktAusg.getFuellFarbe();
rahmen = aktAusg.getRahmenFarbe();
aktAusg = new AusgMerkm(
grad,
aktAusg.holeRahmenDrucken(),
aktAusg.holeFuellungDrucken());
aktAusg.setFuellFarbe(fuell);
aktAusg.setRahmenFarbe(rahmen);
}
oL.add(aktAusg);
}
oL.add(aktPol.toPol(skalierung, versch));
}
}
return oL;
}
/**
* Speichert eine Liste von Grafikobjekten als JPG-Image ab.
* Die Liste kann Objekte folgenden Typs enthalten:<BR>
* - Polygon (wird direkt gezeichnet mit den aktuellen Einstellungen
* f�r Hintergrund-, Vordergrundfarbe und Rahmeneigenschaften),<BR>
* - Kreisklasse (zeichnet wie oben direkt einen Kreis),<BR>
* - Ausgabemerkmale (setzt Farben, Rahmeneigenschaften usw. f�r alle
* NACHFOLGENDEN Objekte) und<BR>
* - aufeinanderfolgend zwei Integer-Objekte x und y und ein String-Objekt
* (wird als String gezeichnet, wobei die linke untere Ecke an den
* Koordinaten (x / y) ist).
*
* @param oL Zu speichernde Liste von Objekten.
* @param filename Dateiname (ohne Erweiterung) unter dem das Bild
* abgelegt werden soll.
* @param modus Der Speichermodus ("jpg", "png", etc.)
* @param params Die Parameter.
**/
public static void saveObjectsToFile(
final List<Object> oL,
final String filename,
final String modus,
final Parametersatz params) {
boolean vorhanden = false;
String datNam;
if (filename.substring(filename.length() - 4).toLowerCase()
.equals("." + modus.toLowerCase())) {
datNam = params.getStdPfad()
+ File.separator
+ filename;
} else {
datNam = params.getStdPfad()
+ File.separator
+ filename
+ "." + modus;
}
for (int i = 0; i < ImageIO.getWriterFormatNames().length; i++) {
if (modus.equals(ImageIO.getWriterFormatNames()[i])) {
vorhanden = true;
}
}
if (!vorhanden) {
SonstMeth.log(SonstMeth.LOG_ERROR,
"Grafikmodus nicht vorhanden: " + modus,
params);
SonstMeth.log(SonstMeth.LOG_ERROR,
"Grafik nicht gespeichert: " + datNam,
params);
return;
}
Polygon2D rahmen = fmg.fmg8.graphVis.zeichenModi.Geometrie2D.rahmen(oL);
Vektor2D rahmenMin = rahmen.get(0);
Vektor2D rahmenMax = rahmen.get(1);
rahmenMin.sub(new Vektor2D(10, 10));
rahmenMax.add(new Vektor2D(10, 10));
double breite = rahmenMax.x - rahmenMin.x;
double hoehe = rahmenMax.y - rahmenMin.y;
if (breite <= 0) {
breite = 1;
}
if (hoehe <= 0) {
hoehe = 1;
}
BufferedImage buffImg = new BufferedImage(
(int) breite,
(int) hoehe,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = (Graphics2D) buffImg.getGraphics();
g2.translate(-rahmenMin.x, -rahmenMin.y);
g2.setStroke(new BasicStroke(BasicStroke.JOIN_MITER));
g2.setColor(Color.white);
g2.fillRect((int) rahmenMin.x,
(int) rahmenMin.y,
(int) (rahmenMax.x - rahmenMin.x),
(int) (rahmenMax.y - rahmenMin.y));
fmg.fmg8.graphVis.zeichenModi.Geometrie2D.maleObjListe(g2, oL, params);
try {
ImageIO.write(buffImg, modus, new File(datNam));
} catch (final Exception e) {
SonstMeth.log(SonstMeth.LOG_ERROR,
"Datei konnte nicht gespeichert werden: "
+ datNam,
params);
return;
}
SonstMeth.log(SonstMeth.LOG_INFO,
"Graphik gespeichert in: "
+ datNam,
params);
}
/**
* Speichert ein Polygon als Grafik ab.
*
* @param p Zu speicherndes Polygon.
* @param filename Dateiname (ohne oder mit Erweiterung) unter dem das
* Bild abgelegt werden soll.
* @param modus Der Speichermodus ("jpg", "png", etc.)
* @param params Die Parameter.
*/
public static void saveSinglePolygonToFile(
final Polygon p,
final String filename,
final String modus,
final Parametersatz params) {
ArrayList<Object> oL = new ArrayList<Object>(1);
oL.add(p);
fmg.fmg8.graphVis.zeichenModi.Geometrie2D.saveObjectsToFile(
oL,
filename,
modus,
params);
}
/**
* Berechnet jeweils auf beiden Seiten einer Kurve aus zwei
* Streckensegmenten Schnittpunkte der vier die Kurve im Abstand "dicke"
* umh�llenden, zu den Teilstrecken parallelen Geraden.
*
* @param p1 Der erste Punkt.
* @param p2 Der zweite Punkt.
* @param p3 Der dritte Punkt.
* @param dicke Die Dicke des Pfeils an der Stelle.
*
* @return Die beiden Schnittpunkte.
*/
public static Polygon2D schnPkte(
final Vektor2D p1,
final Vektor2D p2,
final Vektor2D p3,
final double dicke) {
Polygon2D pkte = new Polygon2D(2);
Vektor2D v1 = new Vektor2D(p2.x - p1.x, p2.y - p1.y);
Vektor2D v2 = new Vektor2D(p3.x - p2.x, p3.y - p2.y);
Vektor2D c1 = new Vektor2D(v1.y, -v1.x);
Vektor2D c2 = new Vektor2D(v2.y, -v2.x);
Vektor2D p11;
Vektor2D p12;
Vektor2D p01;
Vektor2D p02;
Gerade2D g1, h1, g2, h2;
c1.mult(dicke / (c1.laenge() * 2));
c2.mult(dicke / (c2.laenge() * 2));
p11 = new Vektor2D(p1);
p12 = new Vektor2D(p3);
p01 = new Vektor2D(p1);
p02 = new Vektor2D(p3);
p11.add(c1);
p12.add(c2);
p01.sub(c1);
p02.sub(c2);
v1.laengeFestlegen(p1.distanz(p2));
v2.laengeFestlegen(p2.distanz(p3));
g1 = new Gerade2D(p11, v1);
h1 = new Gerade2D(p12, v2);
g2 = new Gerade2D(p01, v1);
h2 = new Gerade2D(p02, v2);
pkte.add(g1.schnPktSpezial(h1));
pkte.add(g2.schnPktSpezial(h2));
return pkte;
}
}