Das Paket java.awt.geom
stellt fertige geometrische Objekte zur Verfügung,
die in ein Graphics2D
-Objekt gezeichnet werden können. Die wichtigsten dieser Objekte möchte
in diesem Kapitel vorstellen.
Erst im nächsten Kapitel werde ich darstellen, wieso es eigentlich
möglich ist, verschiedene geometrische Objekte in ein Graphics2D
-Objekt zu zeichnen, obwohl
dieses gar nichts über Geometrie "weiß".
Wer eigene geometrische Objekte definieren möchte, sei ebenfalls auf das nächste Kapitel
verwiesen.
Die folgende Grafik zeigt die Vererbungshierarchie für die geometrischen Objekte:
Auf dasShape
-Interface, daß alle Objekte dieser Hierarchie implementieren, gehe ich
im nächsten Kapitel genauer ein.
Punkte sind in der Java 2D API hauptsächlich dazu da, um Koordinaten für komplexere geometrische Objekte wie z.B. Rechtecke bereitzustellen. Ein Punkt hat im mathematischen Sinne keine Ausdehnung und diese Vorstellung wurde auch hier übernommen. Deshalb ist ein Punkt nicht mit einem Pixel zu verwechseln.
Punkte werden in der Java 2D API durch die abstrakte Klasse
java.awt.geom.Point2D
repräsentiert.
Point2D
besitzt zwei innere Klassen, die sich nur in der Art der Speicherung der Koordinaten unterscheiden.
Bei diesen Klassen handelt es sich um Point2D.Double
und Point2D.Float
. Nur von diesen
Klassen können Instanzen erstellt werden.
Dieses Konzept der inneren Klassen zur Realisierung unterschiedlicher Speicherungsformate
für Koordinaten (meist float
, double
)
wird in der Java 2D API sehr häufig benutzt. Hierbei gibt es immer eine Menge von Methoden
aus einer abstrakten Superklasse, die für beide innere Klassen verwendet werden und
zwei Mengen von Methoden, die jeweils in den inneren Klassen definiert sind. Diese Methoden
dienen typischerweise zu speicherungsformatabhängigen set-/get-Operationen (z.B.
setLocation(double x, double y)
für Point2D.Double
und
setLocation(float x, float y)
für Point2D.Float
.
Die folgende Grafik zeigt die Vererbungshierarchie der Point2D
-Klassen, das
Schema der inneren Klassen trifft auch für die restlichen Geometrieklassen zu.
Die folgende Tabelle enthält einige wichtige Methoden von Point2D.Double
.
Die Methoden von Point2D.Float
sind sinngemäß identisch, bis auf das sie
float
als Datentyp benutzen. double
bietet eine höhere Präzision.
Für die vollständige Methodenliste beider Klassen verweise ich
auf die Java API Spezifikation.
Methode | Beschreibung |
---|---|
Point2D.Double() |
konstruiert einen neuen Punkt mit den Koordinaten (0,0) |
Point2D.Double(double x, double y) |
konstruiert einen neuen Punkt mit den Koordinaten (x,y )
|
double getX() |
liefert die X-Koordinate des Punktes |
double getY() |
liefert die Y-Koordinate des Punktes |
void setLocation(double x, double y) |
setzt die X-und Y-Koordinate des Punktes |
In Point2D
selbst sind bereits Methoden definiert, die sowohl von Point2D.Double
als auch von Point2D.Float
verwendet bzw. überschrieben werden,
von denen ich hier einige wichtige vorstellen möchte:
Methode | Beschreibung |
---|---|
double distance(double PX, double PY) |
gibt die Distanz zum Punkt (PX,PY ) zurück. |
double distanceSq(double PX, double PY) |
gibt das Quadrat der Distanz zum Punkt (PX,PY ) zurück.
(nützlich z.B. für den Satz des Pythagoras)
|
double distance(Point2D pt) |
gibt die Distanz zum Punkt pt zurück. |
double distanceSq(Point2D pt) |
gibt das Quadrat der Distanz zum Punkt pt zurück. |
double getX() |
liefert die X-Koordinate des Punktes in doppelter Präzision |
double getY() |
liefert die Y-Koordinate des Punktes in doppelter Präzision |
Das folgende Programmbeispiel die Entfernungsmessung zwischen zwei Punkten p1 und p2 (Zugriff auf die Quelle):
import java.awt.geom.*;
public class MyPoint2D {
public static void main(String[] args) {
//Punkt erstellen
Point2D p1 = new Point2D.Float( 10f,10f );
Point2D p2 = new Point2D.Float( 0f,0f );
//Punktkoordinaten ausgeben
System.out.println( "P1: "+p1 );
System.out.println( "P1: "+p2 );
//Distanz ausgeben
System.out.println( "Strecke P1P2: "+p1.distance(p2) );
}
}
Linien werden in der Java 2D API durch die abstrakte Klasse
java.awt.geom.Line2D
repräsentiert.
Analog zu Point2D
besitzt auch Line2D
zwei innere Klassen. Dieses Muster wird sich auch bei
allen noch nicht vorgestellten Geometrieklassen wiederholen.
Die inneren Klassen von Line2D
heißen Line2D.Double
und Line2D.Float
.
Die folgende Tabelle enthält einige wichtige Methoden von Line2D.Double
.
Die Methoden von Line2D.Float
sind sinngemäß identisch, bis auf das sie
float
als Datentyp benutzen. double
bietet eine höhere Präzision.
Für die vollständige Methodenliste beider Klassen verweise ich
auf die Java API Spezifikation.
Methode | Beschreibung |
---|---|
Line2D.Double() |
konstruiert eine neue Linie mit den Koordinaten (0,0) --> (0,0) |
Line2D.Double(double x1, double y1, double x2, double y2) |
konstruiert eine neue Linie mit den Koordinaten (x1,y1 )
--> (x2,y2 )
|
Line2D.Double(Point p1, Point p2) |
konstruiert eine neue Linie vom Punkt p1 zum
Punkt p2 )
|
Rectangle2D getBounds2D() |
gibt das kleinstmögliche Rechteck zurück, daß die gesamte Linie einschließt |
Point2D getP1() |
gibt den Startpunkt der Linie zurück |
Point2D getP2() |
gibt den Endpunkt der Linie zurück |
double getX1() |
gibt die X-Koordinate des Startpunktes der Linie zurück |
double getY1() |
gibt die Y-Koordinate des Startpunktes der Linie zurück |
double getX2() |
gibt die X-Koordinate des Endpunktes der Linie zurück |
double getY2() |
gibt die Y-Koordinate des Endpunktes der Linie zurück |
void setLine(double X1, double Y1, double X2, double Y2) |
setzt die Koordinaten des Start- und Endpunktes der Linie neu |
Die folgende paint
-Routine zeichnet eine Linie und ihr Begrenzungsrechteck in
ein Graphics2D
-Objekt
(Zugriff auf die Quelle):
public void paint(Graphics g) {
//Upcast --> mehr Funktionen in Graphics2D
Graphics2D g2d=(Graphics2D)g;
//Linie erzeugen
Line2D line = new Line2D.Double( 50,50,400,200 );
//Begrenzungsrechteck ermitteln
Rectangle2D boundary = line.getBounds2D();
//Linie malen
g2d.draw( line );
//Begrenzungsrechteck rot
g2d.setPaint( Color.red );
//Begrenzungsrechteck malen
g2d.draw( boundary );
}
Das Ergebnis sieht wie folgt aus:
Eine quadratische Kurve besteht aus zwei Endpunkten p1 und p2 und einem Kontrollpunkt k. An jedem Endpunkt liegt eine Tangente an, die durch p1 und k bzw. durch p2 und k verläuft. Diese Tangenten definieren gerade die Steigungen der quadratischen Kurve in den Punkten p1 und p2. Zwischen p1 und p2 wird auf Grundlage dieser Steigungen eine quadratische Kurve berechnet. Das folgende Bild zeigt eine quadratische Kurve:
Quadratische Kuven werden in der Java 2D API durch die abstrakte Klassejava.awt.geom.QuadCurve2D
repräsentiert.
Die auch hier vorhandenen inneren Klassen von QuadCurve2D
heißen
QuadCurve2D.Double
und QuadCurve2D.Float
.
Die folgende Tabelle enthält die Konstruktoren von QuadCurve2D.Double
.
Die Konstruktoren von QuadCurve2D.Float
sind sinngemäß identisch, bis auf das sie
float
als Datentyp benutzen.
Methode | Beschreibung |
---|---|
QuadCurve2D.Double() |
konstruiert eine quadratische Kurve mit den Endpunkten (0,0) und (0,0) sowie dem Kontrollpunkt (0,0) |
QuadCurve2D.Double(double x1, double y1, double ctrlx, double ctrly, double x2, double y2) |
konstruiert eine quadratische Kurve mit den Endpunkten
(x1,y1 ), (x2,y2 ) sowie dem Kontrollpunkt
(ctrlx,ctrly ).
|
Natürlich können auch hier wieder die Koordinaten der einzelnen Punkte durch entsprechende
get-/set-Methoden ermittelt werden und auch zu den schon aus Line2D
bekannten Methoden
boolean contains(...)
und Rectangle getBounds()
gibt es hier
äquivalente Methoden, die ich hier nicht noch einmal wiederhole.
Einige Methoden zum erneuten Setzen der drei für die Kurve notwendigen Punkte entnehmen Sie bitte
folgender Tabelle (aus Gründen der Komplexität habe ich hier nur die innere Klasse für double
betrachtet, für die float
-Klasse gibt es gleichwertige Methoden):
Methode | Beschreibung |
---|---|
void setCurve(double[] coords, int offset) |
setzt die X- und Y-Koordinaten von Endpunkt1, Kontrollpunkt 1
und Endpunkt 2 (in dieser Reihenfolge) anhand des Arrays coords ,
beginnend beim Index offset
|
void setCurve(double x1, double y1, double ctrlx, double ctrly, double x2, double y2)
|
setzt die X- und Y-Koordinaten der Endpunkte und des Kontrollpunktes anhand der übergebenen Koordinaten. |
void setCurve(Point2D[] pts, int offset) |
setzt die X- und Y-Koordinaten von Endpunkt1, Kontrollpunkt 1 und Endpunkt 2 (in dieser Reihenfolge) anhand der übergebenen Punkte. |
Die folgende paint
-Routine zeichnet eine quadratische Kurve in
ein Graphics2D
-Objekt
(Zugriff auf die Quelle):
public void paint(Graphics g) {
//Upcast --> mehr Funktionen in Graphics2D
Graphics2D g2d=(Graphics2D)g;
//quad.Kurve erstellen:
QuadCurve2D myCurve=new QuadCurve2D.Double(
20,200, //Punkt 1
200,-90, //Kontrollpunkt k
400,110 //Punkt 2
);
//Kurve malen
g2d.draw( myCurve );
}
Das Ergebnis sieht wie folgt aus:
Eine kubische Kurve besteht im Gegensatz zu einer quadratischen Kurve aus zwei Endpunkten p1 und p2 und zwei Kontrollpunkten k1 und k2. An p1 und p2 liegt jeweils eine Tangente an, die durch p1 und k1 bzw. durch p2 und k2 verläuft. Diese unabhängig voneinander definierbaren Tangenten definieren die Steigungen der Kurve in den Punkten p1 und p2. Zwischen p1 und p2 wird auf Grundlage dieser Steigungen eine Parameterkurve dritten Grades berechnet. Das folgende Bild zeigt eine kubische Kurve:
Kubische Kuven werden in der Java 2D API durch die abstrakte Klassejava.awt.geom.CubicCurve2D
repräsentiert.
Die auch hier vorhandenen inneren Klassen von CubicCurve2D
heißen
CubicCurve2D.Double
und CubicCurve2D.Float
.
Die folgende Tabelle enthält die Konstruktoren von CubicCurve2D.Double
.
Die Konstruktoren von CubicCurve2D.Float
sind sinngemäß identisch, bis auf das sie
float
als Datentyp benutzen.
Konstruktor | Beschreibung |
---|---|
CubicCurve2D.Double() |
konstruiert eine kubische Kurve mit den Endpunkten (0,0) und (0,0) sowie den Kontrollpunkten (0,0) und (0,0) |
CubicCurve2D.Double(double x1, double y1, double ctrlx1, double ctrly1,
double ctrlx2, double ctrly2, double x2, double y2) |
konstruiert eine kubische Kurve mit den Endpunkten
(x1,y1 ), (x2,y2 ) sowie den Kontrollpunkten
(ctrlx1,ctrly1 ) und (ctrlx2,ctrly2 ).
|
Einige Methoden zum erneuten Setzen der vier für die Kurve notwendigen Punkte entnehmen Sie bitte
folgender Tabelle (aus Gründen der Komplexität habe ich hier nur die innere Klasse für double
betrachtet, für die float
-Klasse gibt es gleichwertige Methoden):
Methode | Beschreibung |
---|---|
void setCurve(double[] coords, int offset) |
setzt die X- und Y-Koordinaten von Endpunkt1, Kontrollpunkt 1,
Endpunkt 2, Kontrollpunkt 2
(in dieser Reihenfolge) anhand des Arrays coords , beginnend
beim Index offset .
|
void setCurve(double x1, double y1, double ctrlx1, double ctrly1,
double ctrlx2, double ctrly2, double x2, double y2)
|
setzt die X- und Y-Koordinaten der Endpunkte und der Kontrollpunkte anhand der übergebenen Koordinaten. |
void setCurve(Point2D[] pts, int offset) |
setzt die X- und Y-Koordinaten von Endpunkt1, Kontrollpunkt 1, Endpunkt 2, Kontrollpunkt 2 (in dieser Reihenfolge) anhand der übergebenen Punkte. |
Die folgende paint
-Routine zeichnet eine kubische Kurve in
ein Graphics2D
-Objekt
(Zugriff auf die Quelle):
public void paint(Graphics g) {
//Upcast --> mehr Funktionen in Graphics2D
Graphics2D g2d=(Graphics2D)g;
//quad.Kurve erstellen:
CubicCurve2D myCurve=new CubicCurve2D.Double(
20,150, //Punkt 1
60,190, //Kontrollpunkt k1
300,10, //Kontrollpunkt k2
400,60 //Punkt 2
);
//Kurve malen
g2d.draw( myCurve );
}
Das Ergebnis sieht wie folgt aus:
Rechtecke bietet die Java 2D API in zwei Ausführungen an: mit abgerundeten Ecken und
ohne abgerundete Ecken. Sie werden durch die abstrakten Klassen
java.awt.geom.Rectangle2D
java.awt.geom.RoundRectangle2D
repräsentiert. Wieder gibt es je eine innere Klasse für float
- und double
-Koordinaten.
Ich betrachte hier nur die Rechtecke mit normalen Ecken. Rechtecke mit abgerundeten Ecken besitzen lediglich zwei zusätzliche Member-Variablen, die die Höhe und Breite des Bogens darstellen, der statt einer normalen Ecke gezeichnet wird. Im folgenden Beispielprogramm werden aber beide Arten von Rechtecken dargestellt.
Die folgende Tabelle enthält die Konstruktoren von Rectangle2D.Double
.
Die Konstruktoren von Rectangle2D.Float
sind sinngemäß identisch, bis auf das sie
float
als Datentyp benutzen.
Konstruktor | Beschreibung |
---|---|
Rectangle2D.Double() |
konstruiert ein Rechteck der mit der linken oberen Ecke bei (0,0) und einer Breite und Fläche von 0. |
Rectangle2D.Double(double x, double y, double w, double h) |
konstruiert ein Rechteck der mit der linken oberen Ecke bei
(x,y ), einer Breite von w und einer
Höhe von h .
|
Einige Methoden zum erneuten Setzen der Rechteckdaten entnehmen Sie bitte
folgender Tabelle (aus Gründen der Komplexität habe ich hier nur die innere Klasse für double
betrachtet, für die float
-Klasse gibt es gleichwertige Methoden):
Methode | Beschreibung |
---|---|
void setRect(double x, double y, double w, double h) |
reinitialisiert die Rechteckposition und seine Maße anhand der übergebenen Werte |
void setRect(Rectangle2D r) |
übernimmt die Position und Maße des Rechtecks r
für dieses Rechteck. |
Die folgende paint
-Routine zeichnet zwei gefüllte Rechtecke (eines davon mit
abgerundeten Ecken) in ein Graphics2D
-Objekt
(Zugriff auf die Quelle):
public void paint(Graphics g) {
//Upcast --> mehr Funktionen in Graphics2D
Graphics2D g2d=(Graphics2D)g;
// "normales Rechteck" erstellen:
Rectangle2D r1 = new Rectangle2D.Double(
50,60, //Ecke links oben (X,Y)
200,200 //Breite, Höhe
);
// Rechteck mit abgerundeten Ecken erstellen:
RoundRectangle2D r2 = new RoundRectangle2D.Double(
280,60, //Ecke links oben (X,Y)
200,200, //Breite, Höhe
50,30 //Bogenbreite, Bogenhöhe
);
// nun füllen wir die Rechtecke mit Farben:
g2d.setPaint( Color.blue );
g2d.fill( r1 );
g2d.setPaint( Color.red );
g2d.fill( r2 );
}
Das Ergebnis sieht wie folgt aus:
Ellipsen werden durch die abstrakte Klasse java.awt.geom.Ellipse2D
repräsentiert. Wieder gibt es je eine innere Klasse für float
- und double
-Koordinaten.
Eine Ellipse wird durch ein sie umschließendes, minimales Rechteck definiert. Aufgrund dieser
Tatsache ist sie eine Subklasse der Klasse java.awt.geom.RectangularShape
, die
eine Superklasse für alle geometrischen Figuren darstellt, die durch ein sie umgebendes
Rechteck dargestellt werden können.
Es gibt einen Durchmesser in X- und einen Durchmesser in Y-Richtung, hier "width" und "height" genannt.
Die folgende Tabelle enthält die Konstruktoren von Ellipse2D.Double
.
Die Konstruktoren von Ellipse2D.Float
sind sinngemäß identisch, bis auf das sie
float
als Datentyp benutzen.
Konstruktor | Beschreibung |
---|---|
Ellipse2D.Double() |
konstruiert eine neue Ellipse, deren Begrenzungsrechtecke die linke, obere Ecke bei (0,0) hat. Die Ellipse hat die Breite 0 und die Höhe 0. |
Ellipse2D.Double(double x, double y, double w, double h) |
konstruiert eine neue Ellipse anhand des Rechtecks x,y,w,h
|
Einige Methoden zum erneuten Setzen der Ellipsendaten entnehmen Sie bitte
folgender Tabelle. Die Methode setFrame(...)
zum Setzen der Ellipsengröße
und -position stammt in diesem Fall von der Superklasse RectangularShape
.
Aus Gründen der Komplexität habe ich hier nur die innere Klasse für double
betrachtet, für die float
-Klasse gibt es gleichwertige Methoden.
Methode | Beschreibung |
---|---|
void setFrame(Rectangle2D r) |
redefiniert Position und Abmessungen der Ellipse anhand
der Rechtecks r |
setFrame(double x, double y, double w, double h) | redefiniert Position und Abmessungen der Ellipse anhand
des Rechtecks x,y,w,h |
Die folgende paint
-Routine erzielt eine Art "Tunnel-Effekt", indem sie
ineinander verschachtelte Ellipsen,
deren Farbe sich mit abnehmender Größe von schwarz nach weiß ändert,
in ein Graphics2D
-Objekt zeichnet
(Zugriff auf die Quelle):
public void paint(Graphics g) {
//Upcast --> mehr Funktionen in Graphics2D
Graphics2D g2d=(Graphics2D)g;
double w;double h;double x;double y; //Ellipsenrechteck
w=450;h=200;x=10;y=50; //Ellipsenmaße
double f=0.99;//Verkleinerungsfaktor pro Iteration
//Ellipse mit initialen Werten erzeugen
Ellipse2D e = new Ellipse2D.Double( x,y,w,h );
//Rot-,Grün- und Blauanteil d. akt. Farbe
int rd;int gr;int bl;
double limit=100; //Mindestbreite d. Ellipse
double runs=Math.log(limit/w)/Math.log(f); //Anzahl Durchläufe
double count=0; //Zähler
while( w > limit ) //solange w>Mindestbreite
{
//Farbe proportional zum Zähler count heller machen
rd=(int)((count/runs)*255);
gr=(int)((count/runs)*255);
bl=(int)((count/runs)*255);
//Farbe setzen
g2d.setPaint( new Color( rd,gr,bl ) );
g2d.fill(e); //Ellipse malen
//verkleinern d. Ellipse um den Faktor f
y += (h-f*h)/2; //Y-Position anpassen
w *= f; //Höhe, Breite verkleinern
h *= f;
//setzen der neuen Ellipsenmaße
e.setFrame( new Rectangle2D.Double(x,y,w,h) );
//Durchlaufzähler++
count+=1d;
}
Das Ergebnis sieht wie folgt aus:
Außer den vorgestellten Geometrie-Klassen gibt es noch weitere Klassen, z.B.
java.awt.Arc2D
,
die geometrische Formen repräsentieren. Um den Rahmen dieser Ausarbeitung nicht zu sprengen,
habe ich auf die Vorstellung dieser Klassen verzichtet.
Auf konstruktive Flächengeometrie (java.awt.geom.Area
) gehe ich im nächsten Kapitel
ein.