diff --git a/src/main/java/schule/ngb/zm/shapes/Rectangle.java b/src/main/java/schule/ngb/zm/shapes/Rectangle.java index bea31fb..ce8cdb9 100644 --- a/src/main/java/schule/ngb/zm/shapes/Rectangle.java +++ b/src/main/java/schule/ngb/zm/shapes/Rectangle.java @@ -24,9 +24,25 @@ public class Rectangle extends Shape { copyFrom(pRechteck); } + public Rectangle( Bounds pBounds ) { + this( + pBounds.x, pBounds.y, + pBounds.width, pBounds.height); + } + + /** + * Erstellt ein Rechteck zur Darstellung der + * @param pShape + */ public Rectangle( Shape pShape ) { + this(pShape, true); + } + + public Rectangle( Shape pShape, boolean transformed ) { java.awt.Shape s = pShape.getShape(); - s = pShape.getTransform().createTransformedShape(s); + if( transformed ) { + s = pShape.getTransform().createTransformedShape(s); + } Rectangle2D bounds = s.getBounds2D(); x = bounds.getX(); y = bounds.getY(); @@ -48,10 +64,12 @@ public class Rectangle extends Shape { public void setWidth( double width ) { this.width = width; + invalidate(); } public void setHeight( double height ) { this.height = height; + invalidate(); } @Override @@ -74,6 +92,7 @@ public class Rectangle extends Shape { super.scale(factor); width *= factor; height *= factor; + invalidate(); } @Override diff --git a/src/main/java/schule/ngb/zm/shapes/Shape.java b/src/main/java/schule/ngb/zm/shapes/Shape.java index 01da571..e2bd2e0 100644 --- a/src/main/java/schule/ngb/zm/shapes/Shape.java +++ b/src/main/java/schule/ngb/zm/shapes/Shape.java @@ -7,41 +7,152 @@ import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; +/** + * Basisklasse für alle Formen in der Zeichenmaschine. + *
+ * Alle Formen sind als Unterklassen von {@code Shape} implementiert. + *
+ * Neben den abstrakten Methoden implementieren Unterklassen mindestens zwei + * Konstruktoren. Einen Konstruktor, der die Form mit vom Nutzer gegebenen + * Parametern initialisiert und einen, der die Werten einer anderen Form + * desselben Typs übernimmt. In der Klasse {@link Circle} sind die Konstruktoren + * beispielsweise so implementiert: + * + *
+ * public Circle( double x, double y, double radius ) {
+ * super(x, y);
+ * this.radius = radius;
+ * }
+ *
+ * public Circle( Circle circle ) {
+ * super(circle.x, circle.y);
+ * copyFrom(circle);
+ * }
+ *
+ * + * Außerdem implementieren Unterklassen eine passende {@link #toString()} und + * eine {@link #equals(Object)} Methode. + */ +@SuppressWarnings( "unused" ) public abstract class Shape extends FilledShape { + /** + * x-Koordinate der Form. + */ protected double x; + /** + * y-Koordinate der Form. + */ protected double y; + /** + * Rotation in Grad um den Punkt (x, y). + */ protected double rotation = 0.0; + /** + * Skalierungsfaktor. + */ protected double scale = 1.0; + /** + * Ob die Form angezeigt werden soll. + */ protected boolean visible = true; + /** + * Ankerpunkt der Form. + */ protected Options.Direction anchor = Options.Direction.CENTER; + /** + * Zwischenspeicher für die AWT-Shape zu dieser Form. + */ + protected java.awt.Shape awtShape = null; + + /** + * Setzt die x- und y-Koordinate der Form auf 0. + */ + public Shape() { + this(0.0, 0.0); + } + + /** + * Setzt die x- und y-Koordinate der Form. + * + * @param x + * @param y + */ public Shape( double x, double y ) { this.x = x; this.y = y; } - public Shape() { - this(0.0, 0.0); + /** + * Ob die Form angezeigt wird oder nicht. + * + * @return {@code true}, wenn die From angezeigt werden soll, {@code false} + * sonst. + */ + public boolean isVisible() { + return visible; } + /** + * Versteckt die Form. + */ + public void hide() { + visible = false; + } + + /** + * Zeigt die Form an. + */ + public void show() { + visible = true; + } + + /** + * Versteckt die Form, wenn sie derzeit angezeigt wird und zeigt sie + * andernfalls an. + */ + public void toggle() { + visible = !visible; + } + + /** + * Gibt die x-Koordinate der Form zurück. + * + * @return Die x-Koordinate. + */ public double getX() { return x; } + /** + * Setzt die x-Koordinate der Form. + * + * @param x Die neue x-Koordinate. + */ public void setX( double x ) { this.x = x; } + /** + * Gibt die y-Koordinate der Form zurück. + * + * @return Die y-Koordinate. + */ public double getY() { return y; } + /** + * Setzt die y-Koordinate der Form. + * + * @param y Die neue y-Koordinate. + */ public void setY( double y ) { this.y = y; } @@ -49,8 +160,9 @@ public abstract class Shape extends FilledShape { /** * Gibt die Breite dieser Form zurück. *
- * Die Breite einer Form ist immer die Breite ihrer Begrenzung, bevor - * Drehungen und andere Transformationen auf sei angewandt wurden. + * Die Breite einer Form ist immer die Breite ihrer Begrenzung, + * bevor Drehungen und andere Transformationen auf sie + * angewandt wurden. *
* Die Begrenzungen der tatsächlich gezeichneten Form kann mit * {@link #getBounds()} abgerufen werden. @@ -62,8 +174,9 @@ public abstract class Shape extends FilledShape { /** * Gibt die Höhe dieser Form zurück. *
- * Die Höhe einer Form ist immer die Höhe ihrer Begrenzung, bevor - * Drehungen und andere Transformationen auf sei angewandt wurden. + * Die Höhe einer Form ist immer die Höhe ihrer Begrenzung, + * bevor Drehungen und andere Transformationen auf sie + * angewandt wurden. *
* Die Begrenzungen der tatsächlich gezeichneten Form kann mit * {@link #getBounds()} abgerufen werden. @@ -72,13 +185,60 @@ public abstract class Shape extends FilledShape { */ public abstract double getHeight(); + /** + * Gibt die Rotation in Grad zurück. + * + * @return Rotation in Grad. + */ public double getRotation() { return rotation; } + /** + * Dreht die Form um den angegebenen Winkel um ihren Ankerpunkt. + * @param angle Drehwinkel in Grad. + */ + public void rotate( double angle ) { + this.rotation += angle % 360; + } + + public void rotateTo( double angle ) { + this.rotation = angle % 360; + } + + public void rotate( Point2D center, double angle ) { + rotate(center.getX(), center.getY(), angle); + } + + public void rotate( double x, double y, double angle ) { + this.rotation += angle % 360; + + // Rotate x/y position + double x1 = this.x - x, y1 = this.y - y; + + double rad = Math.toRadians(angle); + double x2 = x1 * Math.cos(rad) - y1 * Math.sin(rad); + double y2 = x1 * Math.sin(rad) + y1 * Math.cos(rad); + + this.x = x2 + x; + this.y = y2 + y; + } + + /** + * Gibt den aktuellen Skalierungsfaktor zurück. + * + * @return Der Skalierungsfaktor. + */ public double getScale() { return scale; } + public void scale( double factor ) { + scale = factor; + } + + public void scaleBy( double factor ) { + scale(scale * factor); + } public void setGradient( schule.ngb.zm.Color from, schule.ngb.zm.Color to, Options.Direction dir ) { Point2D apDir = getAbsAnchorPoint(dir); @@ -91,22 +251,6 @@ public abstract class Shape extends FilledShape { setGradient(ap.getX(), ap.getY(), Math.min(ap.getX(), ap.getY()), from, to); } - public boolean isVisible() { - return visible; - } - - public void hide() { - visible = false; - } - - public void show() { - visible = true; - } - - public void toggle() { - visible = !visible; - } - public Options.Direction getAnchor() { return anchor; } @@ -240,8 +384,44 @@ public abstract class Shape extends FilledShape { */ public abstract Shape copy(); + /** + * Gibt eine {@link java.awt.Shape Java-AWT Shape} Version dieser Form + * zurück. Intern werden die AWT Shapes benutzt, um sie auf den + * {@link Graphics2D Grafikkontext} zu zeichnen. + *
+ * Da die AWT-Shape bei jedem Zeichnen (also mindestens einmal pro Frame) + * benötigt wird, wird das aktuelle Shape-Objekt in {@link #awtShape} + * zwischengespeichert. Bei Änderungen der Objekteigenschaften muss daher + * intern {@link #invalidate()} aufgerufen werden, damit beim nächsten + * Aufruf von {@link #draw(Graphics2D)} die Shape mit einem Aufurf von + * {@code getShape()} neu erzeugt wird. Unterklassen können aber auch die + * zwischengespeicherte Shape direkt modifizieren, um eine Neugenerierung zu + * vermeiden. + *
+ * Wenn diese Form nicht durch eine AWT-Shape dargstellt wird, kann die + * Methode {@code null} zurückgeben. + * + * @return Eine Java-AWT {@code Shape} die diess Form repräsentiert oder + * {@code null}. + */ public abstract java.awt.Shape getShape(); + /** + * Interne Methode, um den Zwischenspeicher der Java-AWT Shape zu löschen. + */ + protected void invalidate() { + awtShape = null; + } + + /** + * Gibt die Begrenzungen der Form zurück. + *
+ * Ein {@code Bounds}-Objekt beschreibt eine "Axis + * Aligned Bounding Box" (AABB). + * + * @return Die Abgrenzungen der Form nach Anwendung der Transformationen. + */ public Bounds getBounds() { return new Bounds(this); } @@ -346,40 +526,6 @@ public abstract class Shape extends FilledShape { this.y += (anchorShape.getY() - anchorThis.getY()) + dir.y * buff; } - public void scale( double factor ) { - scale = factor; - } - - public void scaleBy( double factor ) { - scale(scale * factor); - } - - public void rotate( double angle ) { - this.rotation += angle % 360; - } - - public void rotateTo( double angle ) { - this.rotation = angle % 360; - } - - public void rotate( Point2D center, double angle ) { - rotate(center.getX(), center.getY(), angle); - } - - public void rotate( double x, double y, double angle ) { - this.rotation += angle % 360; - - // Rotate x/y position - double x1 = this.x - x, y1 = this.y - y; - - double rad = Math.toRadians(angle); - double x2 = x1 * Math.cos(rad) - y1 * Math.sin(rad); - double y2 = x1 * Math.sin(rad) + y1 * Math.cos(rad); - - this.x = x2 + x; - this.y = y2 + y; - } - /*public void shear( double dx, double dy ) { verzerrung.shear(dx, dy); }*/ @@ -420,8 +566,12 @@ public abstract class Shape extends FilledShape { return; } - java.awt.Shape shape = getShape(); - if( shape != null ) { + if( awtShape == null ) { + awtShape = getShape(); + } + + if( awtShape != null ) { + java.awt.Shape shape = awtShape; if( transform != null ) { shape = transform.createTransformedShape(shape); } @@ -429,18 +579,6 @@ public abstract class Shape extends FilledShape { Color currentColor = graphics.getColor(); fillShape(shape, graphics); strokeShape(shape, graphics); - /* - if( fillColor != null && fillColor.getAlpha() > 0 ) { - graphics.setColor(fillColor.getJavaColor()); - graphics.fill(shape); - } - if( strokeColor != null && strokeColor.getAlpha() > 0 - && strokeWeight > 0.0 ) { - graphics.setColor(strokeColor.getJavaColor()); - graphics.setStroke(createStroke()); - graphics.draw(shape); - } - */ graphics.setColor(currentColor); } }