diff --git a/src/main/java/schule/ngb/zm/BasicDrawable.java b/src/main/java/schule/ngb/zm/BasicDrawable.java new file mode 100644 index 0000000..ee11fea --- /dev/null +++ b/src/main/java/schule/ngb/zm/BasicDrawable.java @@ -0,0 +1,239 @@ +package schule.ngb.zm; + +import schule.ngb.zm.shapes.Fillable; +import schule.ngb.zm.shapes.Strokeable; + +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Stroke; + +/** + * Basisimplementierung der {@link Strokeable} und {@link Fillable} APIs. + * + * Die Klasse bietet eine Grundlage zur Implementierung eigener Zeichenobjekte, + * die eine Füllung und eine Konturlinie haben können. + */ +public abstract class BasicDrawable extends Constants implements Strokeable, Fillable { + + /** + * Ob das Objekt gezeichnet werden soll. + */ + protected boolean visible = true; + + /** + * Aktuelle Farbe der Konturlinie oder {@code null}, wenn das Objekt ohne + * Kontur dargestellt werden soll. + */ + protected schule.ngb.zm.Color strokeColor = DEFAULT_STROKECOLOR; + + /** + * Die Dicke der Konturlinie. Wird nicht kleiner als 0. + */ + protected double strokeWeight = DEFAULT_STROKEWEIGHT; + + /** + * Die Art der Konturlinie. + */ + protected Options.StrokeType strokeType = SOLID; + + /** + * Cache für den aktuellen {@code Stroke} der Kontur. Wird nach Änderung + * einer der Kontureigenschaften auf {@code null} gesetzt und beim nächsten + * Zeichnen neu erstellt. + */ + protected Stroke stroke = null; + + /** + * Die aktuelle Füllfarbe der Form oder {@code null}, wenn das Objekt nicht + * gefüllt werden soll. + */ + protected Color fillColor = DEFAULT_FILLCOLOR; + + /** + * Der aktuelle Farbverlauf des Objektes oder {@code null}, wenn es keinen + * Farbverlauf besitzt. + */ + protected GradientPaint fill = null; + + + // Implementierung Drawable Interface + + /** + * Ob das Objekt angezeigt bzw. gezeichnet werden soll. + * + * @return {@code true}, wenn das Objekt angezeigt werden soll, + * {@code false} sonst. + */ + public boolean isVisible() { + return visible; + } + + /** + * Versteckt das Objekt. + */ + public void hide() { + visible = false; + } + + /** + * Zeigt das Objekt an. + */ + public void show() { + visible = true; + } + + /** + * Versteckt da Objekt, wenn es derzeit angezeigt wird und zeigt es + * andernfalls an. + */ + public void toggle() { + visible = !visible; + } + + public abstract void draw( Graphics2D graphics ); + + + // Implementierung Fillable Interface + + /** + * {@inheritDoc} + */ + @Override + public void setFill( Paint fill ) { + if( fill instanceof Color ) { + this.setFillColor((Color) fill); + } else if( fill instanceof GradientPaint ) { + this.fillColor = null; + this.fill = (GradientPaint) fill; + } + } + + /** + * {@inheritDoc} + */ + @Override + public Paint getFill() { + if( fill != null ) { + return fill; + } else if( fillColor != null && fillColor.getAlpha() > 0 ) { + return fillColor; + } else { + return null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasFillColor() { + return fillColor != null; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasGradient() { + return fill != null; + } + + /** + * {@inheritDoc} + */ + @Override + public Color getFillColor() { + return fillColor; + } + + /** + * {@inheritDoc} + */ + @Override + public void setFillColor( Color color ) { + fillColor = color; + fill = null; + } + + /** + * {@inheritDoc} + */ + @Override + public GradientPaint getGradient() { + return fill; + } + + + // Implementierung Strokeable Interface + + /** + * {@inheritDoc} + */ + @Override + public void setStroke( Stroke stroke ) { + this.stroke = stroke; + } + + /** + * {@inheritDoc} + */ + @Override + public Stroke getStroke() { + if( stroke == null ) { + stroke = Strokeable.createStroke(strokeType, strokeWeight); + } + return stroke; + } + + /** + * {@inheritDoc} + */ + @Override + public Color getStrokeColor() { + return strokeColor; + } + + /** + * {@inheritDoc} + */ + @Override + public void setStrokeColor( Color color ) { + strokeColor = color; + } + + /** + * {@inheritDoc} + */ + @Override + public double getStrokeWeight() { + return strokeWeight; + } + + /** + * {@inheritDoc} + */ + @Override + public void setStrokeWeight( double weight ) { + strokeWeight = weight; + this.stroke = null; + } + + /** + * {@inheritDoc} + */ + @Override + public Options.StrokeType getStrokeType() { + return strokeType; + } + + /** + * {@inheritDoc} + */ + @Override + public void setStrokeType( Options.StrokeType type ) { + strokeType = type; + this.stroke = null; + } + +} diff --git a/src/main/java/schule/ngb/zm/layers/DrawingLayer.java b/src/main/java/schule/ngb/zm/layers/DrawingLayer.java index 8e6d4f9..5ef24f5 100644 --- a/src/main/java/schule/ngb/zm/layers/DrawingLayer.java +++ b/src/main/java/schule/ngb/zm/layers/DrawingLayer.java @@ -3,13 +3,12 @@ package schule.ngb.zm.layers; import schule.ngb.zm.Color; import schule.ngb.zm.Layer; import schule.ngb.zm.Options; +import schule.ngb.zm.shapes.Fillable; +import schule.ngb.zm.shapes.Strokeable; import schule.ngb.zm.shapes.Text; import schule.ngb.zm.util.io.ImageLoader; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Image; -import java.awt.Shape; +import java.awt.*; import java.awt.geom.*; import java.util.Stack; @@ -20,7 +19,7 @@ import java.util.Stack; * {@link schule.ngb.zm.Zeichenmaschine}. */ @SuppressWarnings( "unused" ) -public class DrawingLayer extends Layer { +public class DrawingLayer extends Layer implements Strokeable, Fillable { /** * Wiederverwendbarer Speicher für eine Linie. @@ -199,6 +198,16 @@ public class DrawingLayer extends Layer { shapeDelegate.resetFill(); } + @Override + public void setFill( Paint fill ) { + shapeDelegate.setFill(fill); + } + + @Override + public Paint getFill() { + return shapeDelegate.getFill(); + } + /** * Setzt die Füllung auf einen linearen Farbverlauf, der am Punkt * ({@code fromX}, {@code fromY}) mit der Farbe {@code from} startet und am @@ -232,6 +241,18 @@ public class DrawingLayer extends Layer { shapeDelegate.setGradient(centerX, centerY, radius, from, to); } + @Override + public void setGradient( Color from, Color to, Options.Direction dir ) { + Point2D.Double anchorPoint = schule.ngb.zm.shapes.Shape.getAnchorPoint(getWidth(), getHeight(), dir); + Point2D.Double inversePoint = schule.ngb.zm.shapes.Shape.getAnchorPoint(getWidth(), getHeight(), dir.inverse()); + shapeDelegate.setGradient(inversePoint.x, inversePoint.y, from, anchorPoint.x, anchorPoint.y, to); + } + + @Override + public void setGradient( Color from, Color to ) { + shapeDelegate.setGradient(getWidth()/2.0, getHeight()/2.0, min(getWidth(), getHeight())/2.0, from, to); + } + /** * Entfernt den Farbverlauf von der Form. */ @@ -239,6 +260,11 @@ public class DrawingLayer extends Layer { shapeDelegate.noGradient(); } + @Override + public void setStroke( Stroke stroke ) { + shapeDelegate.setStroke(stroke); + } + /** * Gibt die aktuelle Farbe der Konturlinie zurück. * @@ -386,6 +412,11 @@ public class DrawingLayer extends Layer { shapeDelegate.setStrokeType(type); } + @Override + public Stroke getStroke() { + return shapeDelegate.getStroke(); + } + public void setAnchor( Options.Direction anchor ) { shapeDelegate.setAnchor(anchor); } @@ -579,8 +610,8 @@ public class DrawingLayer extends Layer { } protected void fillShape( Shape shape ) { - if( shapeDelegate.getPaint() != null ) { - drawing.setPaint(shapeDelegate.getPaint()); + if( shapeDelegate.getFill() != null ) { + drawing.setPaint(shapeDelegate.getFill()); drawing.fill(shape); } } @@ -732,6 +763,8 @@ public class DrawingLayer extends Layer { public void resetStyle() { Text newDelegate = new Text(0, 0, ""); + newDelegate.setFillColor(DEFAULT_FILLCOLOR); + newDelegate.setStrokeColor(DEFAULT_STROKECOLOR); if( shapeDelegate != null ) { newDelegate.setAnchor(shapeDelegate.getAnchor()); } diff --git a/src/main/java/schule/ngb/zm/layers/TurtleLayer.java b/src/main/java/schule/ngb/zm/layers/TurtleLayer.java index 1eadffe..a72a231 100644 --- a/src/main/java/schule/ngb/zm/layers/TurtleLayer.java +++ b/src/main/java/schule/ngb/zm/layers/TurtleLayer.java @@ -1,12 +1,15 @@ package schule.ngb.zm.layers; -import schule.ngb.zm.Color; -import schule.ngb.zm.Layer; -import schule.ngb.zm.Options; -import schule.ngb.zm.Vector; -import schule.ngb.zm.shapes.FilledShape; +import schule.ngb.zm.*; +import schule.ngb.zm.shapes.Fillable; +import schule.ngb.zm.shapes.Rectangle; +import schule.ngb.zm.shapes.Shape; +import schule.ngb.zm.shapes.Strokeable; +import java.awt.GradientPaint; import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Path2D; import java.util.ArrayList; @@ -14,7 +17,7 @@ import java.util.List; import java.util.Stack; @SuppressWarnings( "unused" ) -public class TurtleLayer extends Layer { +public class TurtleLayer extends Layer implements Strokeable, Fillable { // Rotating by the clock public static final int H1 = 30; @@ -119,7 +122,20 @@ public class TurtleLayer extends Layer { } } - // Begin of delegate methods (auto-generated) + // begin of delegate methods (auto-generated) + + @Override + public boolean isVisible() { + return mainTurtle.isVisible(); + } + + public void beginPath() { + mainTurtle.beginPath(); + } + + public void closePath() { + mainTurtle.closePath(); + } public void fill() { mainTurtle.fill(); @@ -177,101 +193,194 @@ public class TurtleLayer extends Layer { mainTurtle.moveTo(x, y); } - public Color getFillColor() { - return mainTurtle.getFillColor(); + @Override + public void setFill( Paint fill ) { + mainTurtle.setFill(fill); } + @Override + public Paint getFill() { + return mainTurtle.getFill(); + } + + @Override public void setFillColor( Color color ) { mainTurtle.setFillColor(color); } - public void setFillColor( int gray ) { - mainTurtle.setFillColor(gray); - } - - public void noFill() { - mainTurtle.noFill(); - } - - public void setFillColor( int gray, int alpha ) { - mainTurtle.setFillColor(gray, alpha); - } - - public void setFillColor( int red, int green, int blue ) { - mainTurtle.setFillColor(red, green, blue); - } - - public void setFillColor( int red, int green, int blue, int alpha ) { - mainTurtle.setFillColor(red, green, blue, alpha); - } - - public void resetFill() { - mainTurtle.resetFill(); - } - - public Color getStrokeColor() { - return mainTurtle.getStrokeColor(); + @Override + public Color getFillColor() { + return mainTurtle.getFillColor(); } + @Override public void setStrokeColor( Color color ) { mainTurtle.setStrokeColor(color); } - public void setStrokeColor( int gray ) { - mainTurtle.setStrokeColor(gray); - } - - public void noStroke() { - mainTurtle.noStroke(); - } - - public void setStrokeColor( int gray, int alpha ) { - mainTurtle.setStrokeColor(gray, alpha); - } - - public void setStrokeColor( int red, int green, int blue ) { - mainTurtle.setStrokeColor(red, green, blue); - } - - public void setStrokeColor( int red, int green, int blue, int alpha ) { - mainTurtle.setStrokeColor(red, green, blue, alpha); - } - - public double getStrokeWeight() { - return mainTurtle.getStrokeWeight(); - } - + @Override public void setStrokeWeight( double weight ) { mainTurtle.setStrokeWeight(weight); } + @Override public Options.StrokeType getStrokeType() { return mainTurtle.getStrokeType(); } + @Override public void setStrokeType( Options.StrokeType type ) { mainTurtle.setStrokeType(type); } + @Override + public void setGradient( Color from, Color to, Options.Direction dir ) { + mainTurtle.setGradient(from, to, dir); + } + + @Override + public void setGradient( Color from, Color to ) { + mainTurtle.setGradient(from, to); + } + + @Override + public boolean hasFill() { + return mainTurtle.hasFill(); + } + + @Override + public boolean hasFillColor() { + return mainTurtle.hasFillColor(); + } + + @Override + public boolean hasGradient() { + return mainTurtle.hasGradient(); + } + + @Override + public void setFillColor( Color color, int alpha ) { + mainTurtle.setFillColor(color, alpha); + } + + @Override + public void setFillColor( int gray ) { + mainTurtle.setFillColor(gray); + } + + @Override + public void setFillColor( int gray, int alpha ) { + mainTurtle.setFillColor(gray, alpha); + } + + @Override + public void setFillColor( int red, int green, int blue ) { + mainTurtle.setFillColor(red, green, blue); + } + + @Override + public void setFillColor( int red, int green, int blue, int alpha ) { + mainTurtle.setFillColor(red, green, blue, alpha); + } + + @Override + public void noFill() { + mainTurtle.noFill(); + } + + @Override + public void resetFill() { + mainTurtle.resetFill(); + } + + @Override + public GradientPaint getGradient() { + return mainTurtle.getGradient(); + } + + @Override + public void setGradient( double fromX, double fromY, Color from, double toX, double toY, Color to ) { + mainTurtle.setGradient(fromX, fromY, from, toX, toY, to); + } + + @Override + public void setGradient( double centerX, double centerY, double radius, Color from, Color to ) { + mainTurtle.setGradient(centerX, centerY, radius, from, to); + } + + @Override + public void noGradient() { + mainTurtle.noGradient(); + } + + @Override + public void setStroke( Stroke stroke ) { + mainTurtle.setStroke(stroke); + } + + @Override + public Stroke getStroke() { + return mainTurtle.getStroke(); + } + + @Override + public boolean hasStroke() { + return mainTurtle.hasStroke(); + } + + @Override + public Color getStrokeColor() { + return mainTurtle.getStrokeColor(); + } + + @Override + public void setStrokeColor( Color color, int alpha ) { + mainTurtle.setStrokeColor(color, alpha); + } + + @Override + public void setStrokeColor( int gray ) { + mainTurtle.setStrokeColor(gray); + } + + @Override + public void setStrokeColor( int gray, int alpha ) { + mainTurtle.setStrokeColor(gray, alpha); + } + + @Override + public void setStrokeColor( int red, int green, int blue ) { + mainTurtle.setStrokeColor(red, green, blue); + } + + @Override + public void setStrokeColor( int red, int green, int blue, int alpha ) { + mainTurtle.setStrokeColor(red, green, blue, alpha); + } + + @Override + public void noStroke() { + mainTurtle.noStroke(); + } + + @Override public void resetStroke() { mainTurtle.resetStroke(); } - public void beginPath() { - mainTurtle.beginPath(); + @Override + public double getStrokeWeight() { + return mainTurtle.getStrokeWeight(); } - public void closePath() { - mainTurtle.closePath(); - } - // End of delegate methods (auto-generated) + // end of delegate methods (auto-generated) /** * Die Turtle der Zeichenmaschine. */ - public class Turtle extends FilledShape { + public class Turtle extends BasicDrawable { private static final int DEFAULT_SIZE = 12; @@ -287,9 +396,18 @@ public class TurtleLayer extends Layer { boolean pathOpen = false; + /** + * Path-Objekt für die Darstellung der Turtle. + */ + Path2D.Double turtlePath; + Turtle() { } + public boolean isVisible() { + return visible; + } + public void beginPath() { pathOpen = false; addPosToPath(); @@ -317,34 +435,29 @@ public class TurtleLayer extends Layer { public void fill() { closePath(); - if( fillColor != null && fillColor.getAlpha() > 0 ) { - drawing.setColor(fillColor.getJavaColor()); + if( hasFill() ) { + drawing.setPaint(getFill()); drawing.fill(path); } } - public boolean isVisible() { - return visible; - } - @Override public void draw( Graphics2D graphics ) { - /*Shape shape = new RoundRectangle2D.Double( - -12, -5, 16, 10, 5, 3 - );*/ - Path2D path = new Path2D.Double(); - path.moveTo(DEFAULT_SIZE, 0); - path.lineTo(-DEFAULT_SIZE, -DEFAULT_SIZE / 2.0); - path.lineTo(-DEFAULT_SIZE, DEFAULT_SIZE / 2.0); - path.lineTo(DEFAULT_SIZE, 0); + if( turtlePath == null ) { + turtlePath = new Path2D.Double(); + path.moveTo(DEFAULT_SIZE, 0); + path.lineTo(-DEFAULT_SIZE, -DEFAULT_SIZE / 2.0); + path.lineTo(-DEFAULT_SIZE, DEFAULT_SIZE / 2.0); + path.lineTo(DEFAULT_SIZE, 0); + } AffineTransform verzerrung = new AffineTransform(); verzerrung.translate(position.x, position.y); verzerrung.rotate(Math.toRadians(direction.angle())); - java.awt.Shape shape = verzerrung.createTransformedShape(path); + java.awt.Shape shape = verzerrung.createTransformedShape(turtlePath); - if( strokeColor != null ) { + if( hasStroke() ) { graphics.setColor(strokeColor.getJavaColor()); } else { graphics.setColor(DEFAULT_STROKECOLOR.getJavaColor()); @@ -365,7 +478,7 @@ public class TurtleLayer extends Layer { Vector positionStart = position.copy(); position.add(Vector.setLength(direction, dist)); - if( penDown && strokeColor != null ) { + if( penDown && hasStroke() ) { drawing.setColor(strokeColor.getJavaColor()); drawing.setStroke(getStroke()); drawing.drawLine((int) positionStart.x, (int) positionStart.y, (int) position.x, (int) position.y); @@ -418,7 +531,7 @@ public class TurtleLayer extends Layer { position.x = x; position.y = y; - if( penDown && strokeColor != null ) { + if( penDown && hasStroke() ) { drawing.setColor(strokeColor.getJavaColor()); drawing.setStroke(getStroke()); drawing.drawLine((int) x, (int) y, (int) position.x, (int) position.y); diff --git a/src/main/java/schule/ngb/zm/shapes/Fillable.java b/src/main/java/schule/ngb/zm/shapes/Fillable.java new file mode 100644 index 0000000..fc7ec8e --- /dev/null +++ b/src/main/java/schule/ngb/zm/shapes/Fillable.java @@ -0,0 +1,282 @@ +package schule.ngb.zm.shapes; + +import schule.ngb.zm.Color; +import schule.ngb.zm.Constants; +import schule.ngb.zm.Drawable; +import schule.ngb.zm.Options; + +import java.awt.GradientPaint; +import java.awt.Paint; +import java.awt.RadialGradientPaint; + +/** + * {@link Drawable} Klassen, die mit einer Füllung versehen werden können. + *

+ * Das {@code Fillable} Interface dient hauptsächlich zur Vereinheitlichung der + * API für Füllungen. Durch Implementation wird sichergestellt, dass alle + * Objekte, die eine Füllung haben können, dieselben Methoden zur Verfügung + * stellen. Wenn eine {@link Shape} eine + * {@link Fillable#setFillColor(Color, int)} Methode hat, dann sollte auch eine + * {@link schule.ngb.zm.layers.TurtleLayer.Turtle} dieselbe Methode anbieten. Im + * Einzelfall kann es sinnvoll sein, weitere Methoden für Füllungen zur + * Verfügung zu stellen. Allerdings sollte davon nach Möglichkeit zugunsten + * einer einheitlichen API abgesehen werden. + *

+ * Das Äquivalent für Konturlinien stellt {@link Strokeable} dar. + *

+ * Im einfachsten Fall reicht es {@link #setFill(Paint)} und {@link #getFill()} + * zu implementieren. Die anderen Methoden besitzen Standardimplementierungen, + * die sich auf die beiden Methoden beziehen. Allerdings ist es in vielen Fällen + * sinnvoll, einige der Methoden gezielt zu überschreiben, um sie an spezifische + * Situationen anzupassen. + */ +public interface Fillable extends Drawable { + + /** + * Setzt die Füllung direkt auf das angegebene {@code Paint}-Objekt. + * + * @param fill Die neue Füllung. + */ + void setFill( Paint fill ); + + /** + * Gibt die aktuell gesetzte Füllung zurück. + *

+ * Die Art der Füllung kann anhand der Abfragen {@link #hasFillColor()} und + * {@link #hasGradient()} ermittelt werden. + * + * @return Die aktuelle Füllung. + */ + Paint getFill(); + + /** + * Gibt an, ob aktuell eine sichtbare Füllung konfiguriert ist. + *

+ * Eine Füllung gilt als sichtbar, wenn eine nciht transparente Füllfarbe + * oder ein Farbverlauf eingestellt ist. + * + * @return {@code true}, wenn die Füllung sichtbar ist, {@code false} sonst. + */ + default boolean hasFill() { + return (hasFillColor() && getFillColor().getAlpha() > 0) || hasGradient(); + } + + /** + * Gibt an, ob eine Füllfarbe konfiguriert ist. + *

+ * Im Gegensatz zu {@link #hasFill()} prüft die Methode nicht, ob + * die Füllfarbe transparent ist. + * + * @return {@code true}, wenn eine Füllfarbe gesetzt ist. + */ + default boolean hasFillColor() { + Paint fill = getFill(); + return fill instanceof Color; + } + + /** + * Gibt an, ob ein Farbverlauf konfiguriert ist. + * + * @return {@code true}, wenn ein Farbverlauf gesetzt ist. + */ + default boolean hasGradient() { + Paint fill = getFill(); + return fill instanceof GradientPaint; + } + + /** + * Gibt die aktuelle Füllfarbe der Form zurück. + * + * @return Die aktuelle Füllfarbe oder {@code null}. + */ + default Color getFillColor() { + if( hasFillColor() ) { + return (Color) getFill(); + } else { + return null; + } + } + + /** + * Setzt die Füllfarbe auf die angegebene Farbe. + * + * @param color Die neue Füllfarbe oder {@code null}. + * @see Color + */ + default void setFillColor( Color color ) { + setFill(color); + } + + /** + * Setzt die Füllfarbe auf die angegebene Farbe und setzt die Transparenz + * auf den angegebenen Wert. 0 is komplett durchsichtig und 255 komplett + * deckend. + * + * @param color Die neue Füllfarbe oder {@code null}. + * @param alpha Ein Transparenzwert zwischen 0 und 255. + * @see Color#Color(Color, int) + */ + default void setFillColor( Color color, int alpha ) { + setFillColor(new Color(color, alpha)); + } + + /** + * Setzt die Füllfarbe auf einen Grauwert mit der angegebenen Intensität. 0 + * entspricht schwarz, 255 entspricht weiß. + * + * @param gray Ein Grauwert zwischen 0 und 255. + * @see Color#Color(int) + */ + default void setFillColor( int gray ) { + setFillColor(gray, gray, gray, 255); + } + + /** + * Setzt die Füllfarbe auf einen Grauwert mit der angegebenen Intensität und + * dem angegebenen Transparenzwert. Der Grauwert 0 entspricht schwarz, 255 + * entspricht weiß. + * + * @param gray Ein Grauwert zwischen 0 und 255. + * @param alpha Ein Transparenzwert zwischen 0 und 255. + * @see Color#Color(int, int) + */ + default void setFillColor( int gray, int alpha ) { + setFillColor(gray, gray, gray, alpha); + } + + /** + * Setzt die Füllfarbe auf die Farbe mit den angegebenen Rot-, Grün- und + * Blauanteilen. + * + * @param red Der Rotanteil der Farbe zwischen 0 und 255. + * @param green Der Grünanteil der Farbe zwischen 0 und 255. + * @param blue Der Blauanteil der Farbe zwischen 0 und 255. + * @see Color#Color(int, int, int) + * @see https://de.wikipedia.org/wiki/RGB-Farbraum + */ + default void setFillColor( int red, int green, int blue ) { + setFillColor(red, green, blue, 255); + } + + /** + * Setzt die Füllfarbe auf die Farbe mit den angegebenen Rot-, Grün- und + * Blauanteilen und dem angegebenen Transparenzwert. + * + * @param red Der Rotanteil der Farbe zwischen 0 und 255. + * @param green Der Grünanteil der Farbe zwischen 0 und 255. + * @param blue Der Blauanteil der Farbe zwischen 0 und 255. + * @param alpha Ein Transparenzwert zwischen 0 und 25 + * @see Color#Color(int, int, int, int) + * @see https://de.wikipedia.org/wiki/RGB-Farbraum + */ + default void setFillColor( int red, int green, int blue, int alpha ) { + setFillColor(new Color(red, green, blue, alpha)); + } + + /** + * Entfernt die Füllung der Form. + */ + default void noFill() { + setFillColor(null); + noGradient(); + } + + /** + * Setzt die Füllfarbe auf den Standardwert zurück. + * + * @see schule.ngb.zm.Constants#DEFAULT_FILLCOLOR + */ + default void resetFill() { + setFillColor(Constants.DEFAULT_FILLCOLOR); + noGradient(); + } + + /** + * Gibt den aktuellen Farbverlauf der Form zurück. + * + * @return Der aktuelle Farbverlauf oder {@code null}. + */ + default GradientPaint getGradient() { + if( hasGradient() ) { + return (GradientPaint) getFill(); + } else { + return null; + } + } + + /** + * Setzt die Füllung auf einen linearen Farbverlauf, der am Punkt + * ({@code fromX}, {@code fromY}) mit der Farbe {@code from} startet und am + * Punkt (({@code toX}, {@code toY}) mit der Farbe {@code to} endet. + * + * @param fromX x-Koordinate des Startpunktes. + * @param fromY y-Koordinate des Startpunktes. + * @param from Farbe am Startpunkt. + * @param toX x-Koordinate des Endpunktes. + * @param toY y-Koordinate des Endpunktes. + * @param to Farbe am Endpunkt. + */ + default void setGradient( double fromX, double fromY, Color from, double toX, double toY, Color to ) { + setFill(new GradientPaint( + (float) fromX, (float) fromY, from.getJavaColor(), + (float) toX, (float) toY, to.getJavaColor() + )); + } + + /** + * Setzt die Füllung auf einen linearen Farbverlauf, der in die angegebene + * Richtung verläuft. + * + * @param from Farbe am Startpunkt. + * @param to Farbe am Endpunkt. + * @param dir Richtung des Farbverlaufs. + */ + default void setGradient( Color from, Color to, Options.Direction dir ) { + int whalf = (Constants.canvasWidth / 2); + int hhalf = (Constants.canvasHeight / 2); + setGradient(whalf - dir.x * whalf, hhalf - dir.y * hhalf, from, whalf + dir.x * whalf, hhalf + dir.y * hhalf, to); + } + + /** + * Setzt die Füllung auf einen kreisförmigen (radialen) Farbverlauf, mit dem + * Zentrum im Punkt ({@code centerX}, {@code centerY}) und dem angegebenen + * Radius. Der Verlauf starte im Zentrum mit der Farbe {@code from} und + * endet am Rand des durch den Radius beschriebenen Kreises mit der Farbe + * {@code to}. + * + * @param centerX x-Koordinate des Kreismittelpunktes. + * @param centerY y-Koordinate des Kreismittelpunktes. + * @param radius Radius des Kreises. + * @param from Farbe im Zentrum des Kreises. + * @param to Farbe am Rand des Kreises. + */ + default void setGradient( double centerX, double centerY, double radius, Color from, Color to ) { + setFill(new RadialGradientPaint( + (float) centerX, (float) centerY, (float) radius, + new float[]{0f, 1f}, + new java.awt.Color[]{from.getJavaColor(), to.getJavaColor()})); + } + + /** + * Setzt die Füllung auf einen kreisförmigen (radialen) Farbverlauf, der im + * Zentrum beginnt. + * + * @param from Farbe im Zentrum. + * @param to Farbe am Rand. + */ + default void setGradient( Color from, Color to ) { + int whalf = (Constants.canvasWidth / 2); + int hhalf = (Constants.canvasHeight / 2); + setGradient(whalf, hhalf, Math.min(whalf, hhalf), from, to); + } + + /** + * Entfernt den Farbverlauf von der Form. + */ + default void noGradient() { + setFill(null); + } + +} diff --git a/src/main/java/schule/ngb/zm/shapes/FilledShape.java b/src/main/java/schule/ngb/zm/shapes/FilledShape.java deleted file mode 100644 index 39c3ea9..0000000 --- a/src/main/java/schule/ngb/zm/shapes/FilledShape.java +++ /dev/null @@ -1,213 +0,0 @@ -package schule.ngb.zm.shapes; - -import schule.ngb.zm.Color; - -import java.awt.GradientPaint; -import java.awt.Graphics2D; -import java.awt.Paint; -import java.awt.RadialGradientPaint; - -/** - * Basisklasse für Formen, die eine Füllung besitzen können. - *

- * Formen mit einer Füllung können auch immer eine Konturlinie besitzen. - */ -public abstract class FilledShape extends StrokedShape { - - /** - * Die aktuelle Füllfarbe der Form oder {@code null}, wenn die Form nicht - * gefüllt werden soll. - */ - protected Color fillColor = DEFAULT_FILLCOLOR; - - /** - * Der aktuelle Farbverlauf der Form oder {@code null}, wenn die Form keinen - * Farbverlauf besitzt. - */ - protected Paint fill = null; - - /** - * Gibt die aktuelle Füllfarbe der Form zurück. - * - * @return Die aktuelle Füllfarbe oder {@code null}. - */ - public Color getFillColor() { - return fillColor; - } - - /** - * Setzt die Füllfarbe auf die angegebene Farbe. - * - * @param color Die neue Füllfarbe oder {@code null}. - * @see Color - */ - public void setFillColor( Color color ) { - fillColor = color; - } - - /** - * Setzt die Füllfarbe auf die angegebene Farbe und setzt die Transparenz - * auf den angegebenen Wert. 0 is komplett durchsichtig und 255 komplett - * deckend. - * - * @param color Die neue Füllfarbe oder {@code null}. - * @param alpha Ein Transparenzwert zwischen 0 und 255. - * @see Color#Color(Color, int) - */ - public void setFillColor( Color color, int alpha ) { - setFillColor(new Color(color, alpha)); - } - - /** - * Setzt die Füllfarbe auf einen Grauwert mit der angegebenen Intensität. 0 - * entspricht schwarz, 255 entspricht weiß. - * - * @param gray Ein Grauwert zwischen 0 und 255. - * @see Color#Color(int) - */ - public void setFillColor( int gray ) { - setFillColor(gray, gray, gray, 255); - } - - /** - * Setzt die Füllfarbe auf einen Grauwert mit der angegebenen Intensität und - * dem angegebenen Transparenzwert. Der Grauwert 0 entspricht schwarz, 255 - * entspricht weiß. - * - * @param gray Ein Grauwert zwischen 0 und 255. - * @param alpha Ein Transparenzwert zwischen 0 und 255. - * @see Color#Color(int, int) - */ - public void setFillColor( int gray, int alpha ) { - setFillColor(gray, gray, gray, alpha); - } - - /** - * Setzt die Füllfarbe auf die Farbe mit den angegebenen Rot-, Grün- und - * Blauanteilen. - * - * @param red Der Rotanteil der Farbe zwischen 0 und 255. - * @param green Der Grünanteil der Farbe zwischen 0 und 255. - * @param blue Der Blauanteil der Farbe zwischen 0 und 255. - * @see Color#Color(int, int, int) - * @see https://de.wikipedia.org/wiki/RGB-Farbraum - */ - public void setFillColor( int red, int green, int blue ) { - setFillColor(red, green, blue, 255); - } - - /** - * Setzt die Füllfarbe auf die Farbe mit den angegebenen Rot-, Grün- und - * Blauanteilen und dem angegebenen Transparenzwert. - * - * @param red Der Rotanteil der Farbe zwischen 0 und 255. - * @param green Der Grünanteil der Farbe zwischen 0 und 255. - * @param blue Der Blauanteil der Farbe zwischen 0 und 255. - * @param alpha Ein Transparenzwert zwischen 0 und 25 - * @see Color#Color(int, int, int, int) - * @see https://de.wikipedia.org/wiki/RGB-Farbraum - */ - public void setFillColor( int red, int green, int blue, int alpha ) { - setFillColor(new Color(red, green, blue, alpha)); - } - - /** - * Entfernt die Füllung der Form. - */ - public void noFill() { - setFillColor(null); - noGradient(); - } - - /** - * Setzt die Füllfarbe auf den Standardwert zurück. - * - * @see schule.ngb.zm.Constants#DEFAULT_FILLCOLOR - */ - public void resetFill() { - setFillColor(DEFAULT_FILLCOLOR); - noGradient(); - } - - /** - * Setzt die Füllung auf einen linearen Farbverlauf, der am Punkt - * ({@code fromX}, {@code fromY}) mit der Farbe {@code from} startet und am - * Punkt (({@code toX}, {@code toY}) mit der Farbe {@code to} endet. - * - * @param fromX x-Koordinate des Startpunktes. - * @param fromY y-Koordinate des Startpunktes. - * @param from Farbe am Startpunkt. - * @param toX x-Koordinate des Endpunktes. - * @param toY y-Koordinate des Endpunktes. - * @param to Farbe am Endpunkt. - */ - public void setGradient( double fromX, double fromY, Color from, double toX, double toY, Color to ) { - setFillColor(from); - fill = new GradientPaint( - (float) fromX, (float) fromY, from.getJavaColor(), - (float) toX, (float) toY, to.getJavaColor() - ); - } - - /** - * Setzt die Füllung auf einen kreisförmigen (radialen) Farbverlauf, mit dem - * Zentrum im Punkt ({@code centerX}, {@code centerY}) und dem angegebenen - * Radius. Der Verlauf starte im Zentrum mit der Farbe {@code from} und - * endet am Rand des durch den Radius beschriebenen Kreises mit der Farbe - * {@code to}. - * - * @param centerX x-Koordinate des Kreismittelpunktes. - * @param centerY y-Koordinate des Kreismittelpunktes. - * @param radius Radius des Kreises. - * @param from Farbe im Zentrum des Kreises. - * @param to Farbe am Rand des Kreises. - */ - public void setGradient( double centerX, double centerY, double radius, Color from, Color to ) { - setFillColor(from); - fill = new RadialGradientPaint( - (float) centerX, (float) centerY, (float) radius, - new float[]{0f, 1f}, - new java.awt.Color[]{from.getJavaColor(), to.getJavaColor()}); - } - - /** - * Entfernt den Farbverlauf von der Form. - */ - public void noGradient() { - fill = null; - } - - - public Paint getPaint() { - if( fill != null ) { - return fill; - } else if( fillColor != null && fillColor.getAlpha() > 0 ) { - return fillColor; - } else { - return null; - } - } - - /** - * Hilfsmethode für Unterklassen, um die angegebene Form mit der aktuellen - * Füllung auf den Grafik-Kontext zu zeichnen. Die Methode verändert - * gegebenenfalls die aktuelle Farbe des Grafikobjekts und setzt sie nicht - * auf den Ursprungswert zurück, wie von {@link #draw(Graphics2D)} - * gefordert. Dies sollte die aufrufende Unterklasse übernehmen. - * - * @param shape Die zu zeichnende Java-AWT Form - * @param graphics Das Grafikobjekt. - */ - protected void fillShape( java.awt.Shape shape, Graphics2D graphics ) { - if( fill != null ) { - graphics.setPaint(fill); - graphics.fill(shape); - } else if( fillColor != null && fillColor.getAlpha() > 0 ) { - graphics.setColor(fillColor.getJavaColor()); - graphics.fill(shape); - } - } - -} diff --git a/src/main/java/schule/ngb/zm/shapes/Shape.java b/src/main/java/schule/ngb/zm/shapes/Shape.java index 2e5b0a2..e009fed 100644 --- a/src/main/java/schule/ngb/zm/shapes/Shape.java +++ b/src/main/java/schule/ngb/zm/shapes/Shape.java @@ -1,9 +1,14 @@ package schule.ngb.zm.shapes; +import schule.ngb.zm.BasicDrawable; +import schule.ngb.zm.Color; +import schule.ngb.zm.Constants; import schule.ngb.zm.Options; -import java.awt.Color; +import java.awt.GradientPaint; import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; @@ -34,7 +39,7 @@ import java.awt.geom.Point2D; * eine {@link #equals(Object)} Methode. */ @SuppressWarnings( "unused" ) -public abstract class Shape extends FilledShape { +public abstract class Shape extends BasicDrawable { /** * x-Koordinate der Form. @@ -56,11 +61,6 @@ public abstract class Shape extends FilledShape { */ protected double scale = 1.0; - /** - * Ob die Form angezeigt werden soll. - */ - protected boolean visible = true; - /** * Ankerpunkt der Form. */ @@ -84,38 +84,6 @@ public abstract class Shape extends FilledShape { this.y = y; } - /** - * 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. * @@ -180,6 +148,25 @@ public abstract class Shape extends FilledShape { */ public abstract double getHeight(); + /** + * {@inheritDoc} + */ + @Override + public void setGradient( schule.ngb.zm.Color from, schule.ngb.zm.Color to, Options.Direction dir ) { + Point2D apDir = getAbsAnchorPoint(dir); + Point2D apInv = getAbsAnchorPoint(dir.inverse()); + setGradient(apInv.getX(), apInv.getY(), from, apDir.getX(), apDir.getY(), to); + } + + /** + * {@inheritDoc} + */ + @Override + public void setGradient( schule.ngb.zm.Color from, schule.ngb.zm.Color to ) { + Point2D ap = getAbsAnchorPoint(CENTER); + setGradient(ap.getX(), ap.getY(), Math.min(ap.getX(), ap.getY()), from, to); + } + /** * Gibt die Rotation in Grad zurück. * @@ -237,17 +224,6 @@ public abstract class Shape extends FilledShape { scale(scale * factor); } - public void setGradient( schule.ngb.zm.Color from, schule.ngb.zm.Color to, Options.Direction dir ) { - Point2D apDir = getAbsAnchorPoint(dir); - Point2D apInv = getAbsAnchorPoint(dir.inverse()); - setGradient(apInv.getX(), apInv.getY(), from, apDir.getX(), apDir.getY(), to); - } - - public void setGradient( schule.ngb.zm.Color from, schule.ngb.zm.Color to ) { - Point2D ap = getAbsAnchorPoint(CENTER); - setGradient(ap.getX(), ap.getY(), Math.min(ap.getX(), ap.getY()), from, to); - } - public Options.Direction getAnchor() { return anchor; } @@ -548,7 +524,7 @@ public abstract class Shape extends FilledShape { shape = transform.createTransformedShape(shape); } - Color currentColor = graphics.getColor(); + java.awt.Color currentColor = graphics.getColor(); fillShape(shape, graphics); strokeShape(shape, graphics); graphics.setColor(currentColor); @@ -561,8 +537,8 @@ public abstract class Shape extends FilledShape { * verglichen. Unterklassen überschreiben die Methode, um weitere * Eigenschaften zu berücksichtigen. *

- * Die Eigenschaften von {@link FilledShape} und {@link StrokedShape} werden - * nicht verglichen. + * Die Eigenschaften, die durch {@link Fillable} und {@link Strokeable} + * impliziert werden, werden nicht verglichen. * * @param o Ein anderes Objekt. * @return @@ -578,4 +554,43 @@ public abstract class Shape extends FilledShape { Double.compare(pShape.scale, scale) == 0; } + /** + * Hilfsmethode für Unterklassen, um die angegebene Form mit den aktuellen + * Kontureigenschaften auf den Grafik-Kontext zu zeichnen. Die Methode + * verändert gegebenenfalls die aktuelle Farbe des Grafikobjekts und setzt + * sie nicht auf den Ursprungswert zurück, wie von {@link #draw(Graphics2D)} + * gefordert. Dies sollte die aufrufende Unterklasse übernehmen. + * + * @param shape Die zu zeichnende Java-AWT Form + * @param graphics Das Grafikobjekt. + */ + protected void strokeShape( java.awt.Shape shape, Graphics2D graphics ) { + if( strokeColor != null && strokeColor.getAlpha() > 0 + && strokeWeight > 0.0 ) { + graphics.setColor(strokeColor.getJavaColor()); + graphics.setStroke(getStroke()); + graphics.draw(shape); + } + } + + /** + * Hilfsmethode für Unterklassen, um die angegebene Form mit der aktuellen + * Füllung auf den Grafik-Kontext zu zeichnen. Die Methode verändert + * gegebenenfalls die aktuelle Farbe des Grafikobjekts und setzt sie nicht + * auf den Ursprungswert zurück, wie von {@link #draw(Graphics2D)} + * gefordert. Dies sollte die aufrufende Unterklasse übernehmen. + * + * @param shape Die zu zeichnende Java-AWT Form + * @param graphics Das Grafikobjekt. + */ + protected void fillShape( java.awt.Shape shape, Graphics2D graphics ) { + if( fill != null ) { + graphics.setPaint(fill); + graphics.fill(shape); + } else if( fillColor != null && fillColor.getAlpha() > 0 ) { + graphics.setColor(fillColor.getJavaColor()); + graphics.fill(shape); + } + } + } diff --git a/src/main/java/schule/ngb/zm/shapes/StrokedShape.java b/src/main/java/schule/ngb/zm/shapes/Strokeable.java similarity index 52% rename from src/main/java/schule/ngb/zm/shapes/StrokedShape.java rename to src/main/java/schule/ngb/zm/shapes/Strokeable.java index 509cb6e..04cbd2b 100644 --- a/src/main/java/schule/ngb/zm/shapes/StrokedShape.java +++ b/src/main/java/schule/ngb/zm/shapes/Strokeable.java @@ -6,46 +6,66 @@ import schule.ngb.zm.Drawable; import schule.ngb.zm.Options; import java.awt.BasicStroke; -import java.awt.Graphics2D; import java.awt.Stroke; - /** - * Basisklasse für Formen, die eine Konturlinie besitzen. + * {@link Drawable} Klassen, die mit einer Konturlinie versehen werden können. + *

+ * Das {@code Strokeable} Interface dient hauptsächlich zur Vereinheitlichung + * der API für Konturlinien. Durch Implementation wird sichergestellt, dass alle + * Objekte, die eine Konturlinie haben können, dieselben Methoden zur Verfügung + * stellen. Wenn eine {@link Shape} eine + * {@link Strokeable#setStrokeColor(Color, int)} Methode hat, dann sollte auch + * eine {@link schule.ngb.zm.layers.TurtleLayer.Turtle} dieselbe Methode + * anbieten. Im Einzelfall kann es sinnvoll sein, weitere Methoden für + * Konturlinien zur erfügung zu stellen. Allerdings sollte davon nach + * Möglichkeit zugunsten einer einheitlichen API abgesehen werden. + *

+ * Das Äquivalent für Füllungen stellt {@link Fillable} dar. */ -public abstract class StrokedShape extends Constants implements Drawable { +public interface Strokeable extends Drawable { /** - * Aktuelle Farbe der Konturlinie oder {@code null}, wenn die Form ohne - * kontur dargestellt werden soll. + * Setzt den {@code Stroke} für die Konturlinie direkt. + * + * @param stroke Ein {@code Stroke}-Objekt. */ - protected Color strokeColor = DEFAULT_STROKECOLOR; + void setStroke( Stroke stroke ); /** - * Die Dicke der Konturlinie. Wird nicht kleiner als 0. + * Gibt ein {@code Stroke}-Objekt mit den aktuell gesetzten Eigenschaften + * zurück. + * + * @return Ein {@code Stroke} mit den passenden Kontureigenschaften. */ - protected double strokeWeight = DEFAULT_STROKEWEIGHT; + Stroke getStroke(); /** - * Die Art der Konturlinie. + * Gibt an, ob die aktuell gesetzten Eigenschaften eine sichtbare + * Konturlinie erzeugen. + *

+ * Die Konturlinie gilt als sichtbar, wenn sie eine nicht transparente Farbe + * und eine Dicke größer 0 besitzt. + *

+ * Das bedeutet, falls die Methode {@code false} zurückgibt, dann kann + * {@link #getStroke()} trotzdem ein gültiges {@link Stroke}-Objekt + * zurückgeben, beispielsweise wenn keine Farbe gesetzt wurde. + * + * @return {@code true}, wenn die Konturlinie sichtbar ist, {@code false} + * sonst. */ - protected Options.StrokeType strokeType = SOLID; - - /** - * Cache für den aktuellen {@code Stroke} der Kontur. Wird nach Änderung - * einer der Kontureigenschaften auf {@code null} gesetzt und beim nächsten - * Zeichnen neu erstellt. - */ - protected Stroke stroke = null; + default boolean hasStroke() { + Color strokeColor = getStrokeColor(); + double strokeWeight = getStrokeWeight(); + return strokeColor != null && strokeColor.getAlpha() > 0 && strokeWeight > 0; + } /** * Gibt die aktuelle Farbe der Konturlinie zurück. * * @return Die Konturfarbe oder {@code null}. */ - public Color getStrokeColor() { - return strokeColor; - } + Color getStrokeColor(); /** * Setzt die Farbe der Konturlinie auf die angegebene Farbe. @@ -53,9 +73,7 @@ public abstract class StrokedShape extends Constants implements Drawable { * @param color Die neue Farbe der Konturlinie. * @see Color */ - public void setStrokeColor( Color color ) { - this.strokeColor = color; - } + void setStrokeColor( Color color ); /** * Setzt die Farbe der Konturlinie auf die angegebene Farbe und setzt die @@ -66,7 +84,7 @@ public abstract class StrokedShape extends Constants implements Drawable { * @param alpha Ein Transparenzwert zwischen 0 und 255. * @see Color#Color(Color, int) */ - public void setStrokeColor( Color color, int alpha ) { + default void setStrokeColor( Color color, int alpha ) { setStrokeColor(new Color(color, alpha)); } @@ -77,7 +95,7 @@ public abstract class StrokedShape extends Constants implements Drawable { * @param gray Ein Grauwert zwischen 0 und 255. * @see Color#Color(int) */ - public void setStrokeColor( int gray ) { + default void setStrokeColor( int gray ) { setStrokeColor(gray, gray, gray, 255); } @@ -90,7 +108,7 @@ public abstract class StrokedShape extends Constants implements Drawable { * @param alpha Ein Transparenzwert zwischen 0 und 255. * @see Color#Color(int, int) */ - public void setStrokeColor( int gray, int alpha ) { + default void setStrokeColor( int gray, int alpha ) { setStrokeColor(gray, gray, gray, alpha); } @@ -105,7 +123,7 @@ public abstract class StrokedShape extends Constants implements Drawable { * @see https://de.wikipedia.org/wiki/RGB-Farbraum */ - public void setStrokeColor( int red, int green, int blue ) { + default void setStrokeColor( int red, int green, int blue ) { setStrokeColor(red, green, blue, 255); } @@ -121,14 +139,14 @@ public abstract class StrokedShape extends Constants implements Drawable { * @see https://de.wikipedia.org/wiki/RGB-Farbraum */ - public void setStrokeColor( int red, int green, int blue, int alpha ) { + default void setStrokeColor( int red, int green, int blue, int alpha ) { setStrokeColor(new Color(red, green, blue, alpha)); } /** * Entfernt die Kontur der Form. */ - public void noStroke() { + default void noStroke() { setStrokeColor(null); } @@ -139,10 +157,10 @@ public abstract class StrokedShape extends Constants implements Drawable { * @see schule.ngb.zm.Constants#DEFAULT_STROKEWEIGHT * @see schule.ngb.zm.Constants#SOLID */ - public void resetStroke() { - setStrokeColor(DEFAULT_STROKECOLOR); - setStrokeWeight(DEFAULT_STROKEWEIGHT); - setStrokeType(SOLID); + default void resetStroke() { + setStrokeColor(Constants.DEFAULT_STROKECOLOR); + setStrokeWeight(Constants.DEFAULT_STROKEWEIGHT); + setStrokeType(Constants.SOLID); } /** @@ -150,9 +168,7 @@ public abstract class StrokedShape extends Constants implements Drawable { * * @return Die aktuelle Dicke der Linie. */ - public double getStrokeWeight() { - return strokeWeight; - } + double getStrokeWeight(); /** * Setzt die Dicke der Konturlinie. Die Dicke muss größer 0 sein. Wird 0 @@ -160,9 +176,8 @@ public abstract class StrokedShape extends Constants implements Drawable { * * @param weight Die Dicke der Konturlinie. */ - public void setStrokeWeight( double weight ) { - this.strokeWeight = max(0.0, weight); - this.stroke = null; + default void setStrokeWeight( double weight ) { + setStroke(createStroke(getStrokeType(), weight)); } /** @@ -171,25 +186,19 @@ public abstract class StrokedShape extends Constants implements Drawable { * @return Die aktuelle Art der Konturlinie. * @see Options.StrokeType */ - public Options.StrokeType getStrokeType() { - return strokeType; - } + Options.StrokeType getStrokeType(); /** - * Setzt den Typ der Kontur. Erlaubte Werte sind {@link #DASHED}, - * {@link #DOTTED} und {@link #SOLID}. + * Setzt den Typ der Kontur. Erlaubte Werte sind {@link Constants#DASHED}, + * {@link Constants#DOTTED} und {@link Constants#SOLID}. * * @param type Eine der möglichen Konturarten. * @see Options.StrokeType */ - public void setStrokeType( Options.StrokeType type ) { - this.strokeType = type; - this.stroke = null; + default void setStrokeType( Options.StrokeType type ) { + setStroke(createStroke(type, getStrokeWeight())); } - @Override - public abstract void draw( Graphics2D graphics ); - /** * Hilfsmethode, um ein {@link Stroke} Objekt mit den aktuellen * Kontureigenschaften zu erstellen. Der aktuelle {@code Stroke} wird @@ -197,52 +206,26 @@ public abstract class StrokedShape extends Constants implements Drawable { * * @return Ein {@code Stroke} mit den passenden Kontureigenschaften. */ - public Stroke getStroke() { - // TODO: Used global cached Stroke Objects? - if( stroke == null ) { - switch( strokeType ) { - case DOTTED: - stroke = new BasicStroke( - (float) strokeWeight, - BasicStroke.CAP_ROUND, - BasicStroke.JOIN_ROUND, - 10.0f, new float[]{1.0f, 5.0f}, 0.0f); - break; - case DASHED: - stroke = new BasicStroke( - (float) strokeWeight, - BasicStroke.CAP_ROUND, - BasicStroke.JOIN_ROUND, - 10.0f, new float[]{5.0f}, 0.0f); - break; - case SOLID: - default: - stroke = new BasicStroke( - (float) strokeWeight, - BasicStroke.CAP_ROUND, - BasicStroke.JOIN_ROUND); - break; - } - } - return stroke; - } - - /** - * Hilfsmethode für Unterklassen, um die angegebene Form mit den aktuellen - * Kontureigenschaften auf den Grafik-Kontext zu zeichnen. Die Methode - * verändert gegebenenfalls die aktuelle Farbe des Grafikobjekts und setzt - * sie nicht auf den Ursprungswert zurück, wie von {@link #draw(Graphics2D)} - * gefordert. Dies sollte die aufrufende Unterklasse übernehmen. - * - * @param shape Die zu zeichnende Java-AWT Form - * @param graphics Das Grafikobjekt. - */ - protected void strokeShape( java.awt.Shape shape, Graphics2D graphics ) { - if( strokeColor != null && strokeColor.getAlpha() > 0 - && strokeWeight > 0.0 ) { - graphics.setColor(strokeColor.getJavaColor()); - graphics.setStroke(getStroke()); - graphics.draw(shape); + static Stroke createStroke( Options.StrokeType strokeType, double strokeWeight ) { + switch( strokeType ) { + case DOTTED: + return new BasicStroke( + (float) strokeWeight, + BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND, + 10.0f, new float[]{1.0f, 5.0f}, 0.0f); + case DASHED: + return new BasicStroke( + (float) strokeWeight, + BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND, + 10.0f, new float[]{5.0f}, 0.0f); + case SOLID: + default: + return new BasicStroke( + (float) strokeWeight, + BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND); } }