From 36a44162b72a24e9a7fdea59bd08130c6ff69928 Mon Sep 17 00:00:00 2001 From: "J. Neugebauer" Date: Thu, 30 Dec 2021 16:33:06 +0100 Subject: [PATCH] Changed language to english After some soulsearching I decided to change the language (back) to English. The documentation and comments will still be in German, but method names and variables are completely in English now. Besides that, a lot of imprvoements have been made in many areas and the framework is coming together niecely. --- src/schule/ngb/zm/Color.java | 280 ++++++++++ src/schule/ngb/zm/ColorLayer.java | 18 + src/schule/ngb/zm/Constants.java | 208 +++++++ .../ngb/zm/{Zeichenbar.java => Drawable.java} | 8 +- src/schule/ngb/zm/DrawingLayer.java | 525 ++++++++++++++++++ src/schule/ngb/zm/Ebene.java | 106 ---- src/schule/ngb/zm/Farbe.java | 95 ---- src/schule/ngb/zm/ImageLayer.java | 75 +++ src/schule/ngb/zm/Konstanten.java | 145 ----- src/schule/ngb/zm/Layer.java | 138 +++++ src/schule/ngb/zm/Leinwand.java | 118 ---- src/schule/ngb/zm/Options.java | 67 +++ src/schule/ngb/zm/Shape2DLayer.java | 166 ++++++ .../{Aktualisierbar.java => Updatable.java} | 8 +- src/schule/ngb/zm/Vector.java | 253 +++++++++ src/schule/ngb/zm/Vektor.java | 251 --------- src/schule/ngb/zm/Zeichenebene.java | 417 -------------- src/schule/ngb/zm/Zeichenleinwand.java | 195 +++++++ src/schule/ngb/zm/Zeichenmaschine.java | 471 ++++++++++++++++ .../ngb/zm/formen/AbgerundetesRechteck.java | 59 -- src/schule/ngb/zm/formen/Arc.java | 111 ++++ src/schule/ngb/zm/formen/Arrow.java | 178 ++++++ src/schule/ngb/zm/formen/Bild.java | 147 ----- src/schule/ngb/zm/formen/Bogen.java | 110 ---- src/schule/ngb/zm/formen/Circle.java | 76 +++ src/schule/ngb/zm/formen/Curve.java | 182 ++++++ src/schule/ngb/zm/formen/CustomShape.java | 53 ++ src/schule/ngb/zm/formen/Drache.java | 117 ---- src/schule/ngb/zm/formen/Dreieck.java | 32 -- src/schule/ngb/zm/formen/Ellipse.java | 80 +-- src/schule/ngb/zm/formen/FilledShape.java | 41 ++ src/schule/ngb/zm/formen/Form.java | 225 -------- src/schule/ngb/zm/formen/FormGruppe.java | 103 ---- src/schule/ngb/zm/formen/Formenebene.java | 70 --- src/schule/ngb/zm/formen/Freiform.java | 40 -- src/schule/ngb/zm/formen/Fuellform.java | 49 -- src/schule/ngb/zm/formen/Kite.java | 118 ++++ src/schule/ngb/zm/formen/Konturform.java | 114 ---- src/schule/ngb/zm/formen/Kreis.java | 67 --- src/schule/ngb/zm/formen/Kurve.java | 183 ------ src/schule/ngb/zm/formen/Line.java | 108 ++++ src/schule/ngb/zm/formen/Linie.java | 92 --- src/schule/ngb/zm/formen/Pfeil.java | 137 ----- src/schule/ngb/zm/formen/Picture.java | 128 +++++ src/schule/ngb/zm/formen/Point.java | 58 ++ src/schule/ngb/zm/formen/Polygon.java | 82 +++ src/schule/ngb/zm/formen/Punkt.java | 34 -- src/schule/ngb/zm/formen/Quad.java | 36 ++ src/schule/ngb/zm/formen/Raute.java | 38 -- src/schule/ngb/zm/formen/Rechteck.java | 107 ---- src/schule/ngb/zm/formen/Rectangle.java | 108 ++++ src/schule/ngb/zm/formen/Rhombus.java | 32 ++ .../ngb/zm/formen/RoundedRectangle.java | 57 ++ src/schule/ngb/zm/formen/Shape.java | 291 ++++++++++ src/schule/ngb/zm/formen/ShapeGroup.java | 138 +++++ src/schule/ngb/zm/formen/ShapesLayer.java | 69 +++ src/schule/ngb/zm/formen/StrokedShape.java | 104 ++++ src/schule/ngb/zm/formen/Text.java | 92 +-- src/schule/ngb/zm/formen/Triangle.java | 36 ++ src/schule/ngb/zm/formen/Vieleck.java | 72 --- src/schule/ngb/zm/formen/Viereck.java | 34 -- src/schule/ngb/zm/util/ImageLoader.java | 141 +++++ src/schule/ngb/zm/util/List.java | 349 ++++++++++++ 63 files changed, 4987 insertions(+), 3055 deletions(-) create mode 100644 src/schule/ngb/zm/Color.java create mode 100644 src/schule/ngb/zm/ColorLayer.java create mode 100644 src/schule/ngb/zm/Constants.java rename src/schule/ngb/zm/{Zeichenbar.java => Drawable.java} (85%) create mode 100644 src/schule/ngb/zm/DrawingLayer.java delete mode 100644 src/schule/ngb/zm/Ebene.java delete mode 100644 src/schule/ngb/zm/Farbe.java create mode 100644 src/schule/ngb/zm/ImageLayer.java delete mode 100644 src/schule/ngb/zm/Konstanten.java create mode 100644 src/schule/ngb/zm/Layer.java delete mode 100644 src/schule/ngb/zm/Leinwand.java create mode 100644 src/schule/ngb/zm/Options.java create mode 100644 src/schule/ngb/zm/Shape2DLayer.java rename src/schule/ngb/zm/{Aktualisierbar.java => Updatable.java} (72%) create mode 100644 src/schule/ngb/zm/Vector.java delete mode 100644 src/schule/ngb/zm/Vektor.java delete mode 100644 src/schule/ngb/zm/Zeichenebene.java create mode 100644 src/schule/ngb/zm/Zeichenleinwand.java create mode 100644 src/schule/ngb/zm/Zeichenmaschine.java delete mode 100644 src/schule/ngb/zm/formen/AbgerundetesRechteck.java create mode 100644 src/schule/ngb/zm/formen/Arc.java create mode 100644 src/schule/ngb/zm/formen/Arrow.java delete mode 100644 src/schule/ngb/zm/formen/Bild.java delete mode 100644 src/schule/ngb/zm/formen/Bogen.java create mode 100644 src/schule/ngb/zm/formen/Circle.java create mode 100644 src/schule/ngb/zm/formen/Curve.java create mode 100644 src/schule/ngb/zm/formen/CustomShape.java delete mode 100644 src/schule/ngb/zm/formen/Drache.java delete mode 100644 src/schule/ngb/zm/formen/Dreieck.java create mode 100644 src/schule/ngb/zm/formen/FilledShape.java delete mode 100644 src/schule/ngb/zm/formen/Form.java delete mode 100644 src/schule/ngb/zm/formen/FormGruppe.java delete mode 100644 src/schule/ngb/zm/formen/Formenebene.java delete mode 100644 src/schule/ngb/zm/formen/Freiform.java delete mode 100644 src/schule/ngb/zm/formen/Fuellform.java create mode 100644 src/schule/ngb/zm/formen/Kite.java delete mode 100644 src/schule/ngb/zm/formen/Konturform.java delete mode 100644 src/schule/ngb/zm/formen/Kreis.java delete mode 100644 src/schule/ngb/zm/formen/Kurve.java create mode 100644 src/schule/ngb/zm/formen/Line.java delete mode 100644 src/schule/ngb/zm/formen/Linie.java delete mode 100644 src/schule/ngb/zm/formen/Pfeil.java create mode 100644 src/schule/ngb/zm/formen/Picture.java create mode 100644 src/schule/ngb/zm/formen/Point.java create mode 100644 src/schule/ngb/zm/formen/Polygon.java delete mode 100644 src/schule/ngb/zm/formen/Punkt.java create mode 100644 src/schule/ngb/zm/formen/Quad.java delete mode 100644 src/schule/ngb/zm/formen/Raute.java delete mode 100644 src/schule/ngb/zm/formen/Rechteck.java create mode 100644 src/schule/ngb/zm/formen/Rectangle.java create mode 100644 src/schule/ngb/zm/formen/Rhombus.java create mode 100644 src/schule/ngb/zm/formen/RoundedRectangle.java create mode 100644 src/schule/ngb/zm/formen/Shape.java create mode 100644 src/schule/ngb/zm/formen/ShapeGroup.java create mode 100644 src/schule/ngb/zm/formen/ShapesLayer.java create mode 100644 src/schule/ngb/zm/formen/StrokedShape.java create mode 100644 src/schule/ngb/zm/formen/Triangle.java delete mode 100644 src/schule/ngb/zm/formen/Vieleck.java delete mode 100644 src/schule/ngb/zm/formen/Viereck.java create mode 100644 src/schule/ngb/zm/util/ImageLoader.java create mode 100644 src/schule/ngb/zm/util/List.java diff --git a/src/schule/ngb/zm/Color.java b/src/schule/ngb/zm/Color.java new file mode 100644 index 0000000..bc61db5 --- /dev/null +++ b/src/schule/ngb/zm/Color.java @@ -0,0 +1,280 @@ +package schule.ngb.zm; + +public class Color { + + public static final Color BLACK = new Color(java.awt.Color.BLACK); + + public static final Color WHITE = new Color(java.awt.Color.WHITE); + + public static final Color GRAY = new Color(java.awt.Color.GRAY); + + public static final Color DARKGRAY = new Color(java.awt.Color.DARK_GRAY); + + public static final Color LIGHTGRAY = new Color(java.awt.Color.LIGHT_GRAY); + + public static final Color RED = new Color(java.awt.Color.RED); + + public static final Color GREEN = new Color(java.awt.Color.GREEN); + + public static final Color BLUE = new Color(java.awt.Color.BLUE); + + public static final Color YELLOW = new Color(java.awt.Color.YELLOW); + + public static final Color ORANGE = new Color(java.awt.Color.ORANGE); + + public static final Color CYAN = new Color(java.awt.Color.CYAN); + + public static final Color MAGENTA = new Color(java.awt.Color.MAGENTA); + + public static final Color PINK = new Color(java.awt.Color.PINK); + + public static final Color HGGREEN = new Color(0, 165, 81); + + public static final Color HGRED = new Color(151, 54, 60); + + + private int rgba; + + public Color() { + rgba = 0xFF000000; + } + + public Color( int gray ) { + this(gray, gray, gray, 255); + } + + public Color( int gray, int alpha ) { + this(gray, gray, gray, alpha); + } + + public Color( int red, int green, int blue ) { + this(red, green, blue, 255); + } + + public Color( int red, int green, int blue, int alpha ) { + rgba = (alpha << 24) | (red << 16) | (green << 8) | blue; + } + + public Color( Color color ) { + this(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); + } + + public Color( Color color, int alpha ) { + this(color.getRed(), color.getGreen(), color.getBlue(), alpha); + } + + public Color( java.awt.Color color ) { + this(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); + } + + public Color( java.awt.Color color, int alpha ) { + this(color.getRed(), color.getGreen(), color.getBlue(), alpha); + } + + public static Color parseRGB( int rgba ) { + Color c = new Color(); + c.rgba = rgba; + return c; + } + + public static Color parseHexcode( String hexcode ) { + if( hexcode.startsWith("#") ) { + hexcode = hexcode.substring(1); + } + if( hexcode.length() != 3 && hexcode.length() != 6 && hexcode.length() != 8 ) { + throw new IllegalArgumentException("color hexcodes have to be 3, 6 or 8 digits long, got " + hexcode.length()); + } + + // normalize hexcode to 8 digits + int alpha = 255; + if( hexcode.length() == 3 ) { + hexcode = "" + hexcode.charAt(0) + hexcode.charAt(0) + hexcode.charAt(1) + hexcode.charAt(1) + hexcode.charAt(2) + hexcode.charAt(2); + } else if( hexcode.length() == 8 ) { + alpha = Integer.valueOf(hexcode.substring(6, 8), 16); + hexcode = hexcode.substring(0, 6); + } else { + hexcode = hexcode; + } + + Color c = new Color(); + c.rgba = (alpha << 24) | Integer.valueOf(hexcode, 16); + return c; + } + + public static Color morph( java.awt.Color pFarbe1, java.awt.Color pFarbe2, double pFactor ) { + if( pFactor < 0.0 || pFarbe2 == null ) { + return new Color(pFarbe1); + } + if( pFactor > 1.0 || pFarbe1 == null ) return new Color(pFarbe2); + double pFactorInv = 1 - pFactor; + return new Color((int) (pFactorInv * pFarbe1.getRed() + pFactor * pFarbe2.getRed()), (int) (pFactorInv * pFarbe1.getGreen() + pFactor * pFarbe2.getGreen()), (int) (pFactorInv * pFarbe1.getBlue() + pFactor * pFarbe2.getBlue()), (int) (pFactorInv * pFarbe1.getAlpha() + pFactor * pFarbe2.getAlpha())); + } + + public static float[] RGBtoHSL( int rgb, float[] hsl ) { + float r = ((rgb >> 16) & 255) / 255.0f; + float g = ((rgb >> 8) & 255) / 255.0f; + float b = (rgb & 255) / 255.0f; + float max = Math.max(Math.max(r, g), b); + float min = Math.min(Math.min(r, g), b); + float c = max - min; + + if( hsl == null ) { + hsl = new float[3]; + } + + float h_ = 0.f; + if( c == 0 ) { + h_ = 0; + } else if( max == r ) { + h_ = (float) (g - b) / c; + if( h_ < 0 ) h_ += 6.f; + } else if( max == g ) { + h_ = (float) (b - r) / c + 2.f; + } else if( max == b ) { + h_ = (float) (r - g) / c + 4.f; + } + float h = 60.f * h_; + + float l = (max + min) * 0.5f; + + float s; + if( c == 0 ) { + s = 0.f; + } else { + s = c / (1 - Math.abs(2.f * l - 1.f)); + } + + hsl[0] = h; + hsl[1] = s; + hsl[2] = l; + return hsl; + } + + public static int HSLtoRGB( float[] hsl ) { + return HSLtoRGB(hsl, 255); + } + + public static int HSLtoRGB( float[] hsl, int alpha ) { + float h = hsl[0]; + float s = hsl[1]; + float l = hsl[2]; + + float c = (1 - Math.abs(2.f * l - 1.f)) * s; + float h_ = h / 60.f; + float h_mod2 = h_; + if( h_mod2 >= 4.f ) h_mod2 -= 4.f; + else if( h_mod2 >= 2.f ) h_mod2 -= 2.f; + + float x = c * (1 - Math.abs(h_mod2 - 1)); + float r_, g_, b_; + if( h_ < 1 ) { + r_ = c; + g_ = x; + b_ = 0; + } else if( h_ < 2 ) { + r_ = x; + g_ = c; + b_ = 0; + } else if( h_ < 3 ) { + r_ = 0; + g_ = c; + b_ = x; + } else if( h_ < 4 ) { + r_ = 0; + g_ = x; + b_ = c; + } else if( h_ < 5 ) { + r_ = x; + g_ = 0; + b_ = c; + } else { + r_ = c; + g_ = 0; + b_ = x; + } + + float m = l - (0.5f * c); + int r = (int) ((r_ + m) * (255.f) + 0.5f); + int g = (int) ((g_ + m) * (255.f) + 0.5f); + int b = (int) ((b_ + m) * (255.f) + 0.5f); + return (alpha & 255) << 24 | r << 16 | g << 8 | b; + } + + /*public void setRed( int red ) { + rgba = (red << 16) | (0xFF00FFFF & rgba); + }*/ + + public Color copy() { + return new Color(this); + } + + /*public void setGreen( int green ) { + rgba = (green << 8) | (0xFFFF00FF & rgba); + }*/ + + public int getRGBA() { + return rgba; + } + + /*public void setBlue( int blue ) { + rgba = blue | (0xFFFFFF00 & rgba); + }*/ + + public int getRed() { + return (rgba >> 16) & 255; + } + + /*public void setAlpha( int alpha ) { + rgba = (alpha << 24) | (0x00FFFFFF & rgba); + }*/ + + public int getGreen() { + return (rgba >> 8) & 255; + } + + public int getBlue() { + return rgba & 255; + } + + public int getAlpha() { + return (rgba >> 24) & 255; + } + + public java.awt.Color getColor() { + return new java.awt.Color(rgba, true); + } + + @Override + public boolean equals( Object po ) { + if( this == po ) return true; + if( po == null || getClass() != po.getClass() ) return false; + Color ppColor = (Color) po; + return rgba == ppColor.rgba; + } + + @Override + public String toString() { + return getClass().getCanonicalName() + '[' + "r=" + getRed() + ',' + "g=" + getGreen() + ',' + "b=" + getBlue() + ',' + "a=" + getAlpha() + ']'; + } + + public Color brighter() { + return brighter(30); + } + + public Color brighter( int percent ) { + float[] hsl = RGBtoHSL(rgba, null); + hsl[2] = Math.min(Math.max(hsl[2] * (1.0f + percent / 100.0f), 0.0f), 1.0f); + return Color.parseRGB(HSLtoRGB(hsl, getAlpha())); + } + + public Color darker() { + return darker(30); + } + + public Color darker( int percent ) { + float[] hsl = RGBtoHSL(rgba, null); + hsl[2] = Math.min(Math.max(hsl[2] * (1.0f - percent / 100.0f), 0.0f), 1.0f); + return Color.parseRGB(HSLtoRGB(hsl, getAlpha())); + } + +} diff --git a/src/schule/ngb/zm/ColorLayer.java b/src/schule/ngb/zm/ColorLayer.java new file mode 100644 index 0000000..5ef5f6d --- /dev/null +++ b/src/schule/ngb/zm/ColorLayer.java @@ -0,0 +1,18 @@ +package schule.ngb.zm; + +public class ColorLayer extends Layer { + + private Color background; + + public ColorLayer( Color color ) { + this.background = color; + clear(); + } + + @Override + public void clear() { + drawing.setColor(background.getColor()); + drawing.fillRect(0, 0, getWidth(), getHeight()); + } + +} diff --git a/src/schule/ngb/zm/Constants.java b/src/schule/ngb/zm/Constants.java new file mode 100644 index 0000000..d79e9ee --- /dev/null +++ b/src/schule/ngb/zm/Constants.java @@ -0,0 +1,208 @@ +package schule.ngb.zm; + +import java.util.Random; + +public class Constants { + + public static final String APP_NAME = "Zeichenmaschine"; + + public static final int APP_VERSION_MAJ = 0; + + public static final int APP_VERSION_MIN = 1; + + public static final int APP_VERSION_REV = 5; + + public static final String APP_VERSION = APP_VERSION_MAJ + "." + APP_VERSION_MIN + "." + APP_VERSION_REV; + + public static final int STD_WIDTH = 400; + + public static final int STD_HEIGHT = 400; + + public static final int STD_FPS = 60; + + public static final Color STD_FILLCOLOR = Color.WHITE; + + public static final Color STD_STROKECOLOR = Color.BLACK; + + public static final double STD_STROKEWEIGHT = 1.0; + + public static final int STD_FONTSIZE = 14; + + public static final Options.StrokeType SOLID = Options.StrokeType.SOLID; + + public static final Options.StrokeType DASHED = Options.StrokeType.DASHED; + + public static final Options.StrokeType DOTTED = Options.StrokeType.DOTTED; + + public static final Options.ArrowHead LINES = Options.ArrowHead.LINES; + + public static final Options.ArrowHead FILLED = Options.ArrowHead.FILLED; + + public static final Options.PathType OPEN = Options.PathType.OPEN; + + public static final Options.PathType CLOSED = Options.PathType.CLOSED; + + public static final Options.PathType PIE = Options.PathType.PIE; + + public static final Options.Direction CENTER = Options.Direction.CENTER; + + public static final Options.Direction NORTH = Options.Direction.NORTH; + + public static final Options.Direction EAST = Options.Direction.EAST; + + public static final Options.Direction SOUTH = Options.Direction.SOUTH; + + public static final Options.Direction WEST = Options.Direction.WEST; + + public static final Options.Direction NORTHEAST = Options.Direction.NORTHEAST; + + public static final Options.Direction SOUTHEAST = Options.Direction.SOUTHEAST; + + public static final Options.Direction NORTHWEST = Options.Direction.NORTHWEST; + + public static final Options.Direction SOUTHWEST = Options.Direction.SOUTHWEST; + + public static final Options.Direction MIDDLE = Options.Direction.MIDDLE; + + public static final Options.Direction UP = Options.Direction.UP; + + public static final Options.Direction RIGHT = Options.Direction.RIGHT; + + public static final Options.Direction DOWN = Options.Direction.DOWN; + + public static final Options.Direction LEFT = Options.Direction.LEFT; + + public static final Color BLACK = Color.BLACK; + + public static final Color WHITE = Color.WHITE; + + public static final Color RED = Color.RED; + + public static final Color BLUE = Color.BLUE; + + public static final Color GREEN = Color.GREEN; + + public static final Color YELLOW = Color.YELLOW; + + public static Color STD_BACKGROUND = new Color(200, 200, 200); + + public static Color color( int gray ) { + return color(gray, gray, gray, 255); + } + + public static Color color( int gray, int alpha ) { + return color(gray, gray, gray, alpha); + } + + public static Color color( int red, int green, int blue ) { + return color(red, green, blue, 255); + } + + public static Color color( int red, int green, int blue, int alpha ) { + if( red < 0 || red >= 256 ) + throw new IllegalArgumentException("red must be between 0 and 255"); + if( green < 0 || green >= 256 ) + throw new IllegalArgumentException("green must be between 0 and 255"); + if( blue < 0 || blue >= 256 ) + throw new IllegalArgumentException("blue must be between 0 and 255"); + if( alpha < 0 || alpha >= 256 ) + throw new IllegalArgumentException("alpha must be between 0 and 255"); + + return new Color(red, green, blue, alpha); + } + + + public static double abs( double x ) { + return Math.abs(x); + } + + public static double sign( double x ) { + return Math.signum(x); + } + + public static double round( double x ) { + return Math.round(x); + } + + public static double floor( double x ) { + return Math.floor(x); + } + + public static double ceil( double x ) { + return Math.ceil(x); + } + + public static double sin( double x ) { + return Math.sin(x); + } + + public static double cos( double x ) { + return Math.cos(x); + } + + public static double tan( double x ) { + return Math.tan(x); + } + + public static double arcsin( double x ) { + return Math.asin(x); + } + + public static double arccos( double x ) { + return Math.acos(x); + } + + public static double arctan( double x ) { + return Math.atan(x); + } + + public static double limit( double x, double max ) { + if( x > max ) { + return max; + } + return x; + } + + public static double limit( double x, double min, double max ) { + if( x > max ) { + return max; + } + if( x < min ) { + return min; + } + return x; + } + + public static double morph( double from, double to, double t ) { + return from - t * (from + to); + } + + public static double random( double min, double max ) { + return Math.random() * (max - min) + min; + } + + public static int random( int min, int max ) { + return (int) (Math.random() * (max - min) + min); + } + + public static boolean randomBool() { + return randomBool(.5); + } + + public static boolean randomBool( int percent ) { + return randomBool(percent / 100.0); + } + + public static boolean randomBool( double weight ) { + return Math.random() < weight; + } + + public static double randomGuassian() { + return new Random().nextGaussian(); + } + + public static double noise() { + return 0.0; + } + +} diff --git a/src/schule/ngb/zm/Zeichenbar.java b/src/schule/ngb/zm/Drawable.java similarity index 85% rename from src/schule/ngb/zm/Zeichenbar.java rename to src/schule/ngb/zm/Drawable.java index 6aa79fa..3511f73 100644 --- a/src/schule/ngb/zm/Zeichenbar.java +++ b/src/schule/ngb/zm/Drawable.java @@ -6,7 +6,7 @@ import java.awt.*; * Zeichenbare Objekte können auf eine Zeichenfläche gezeichnet werden. * In der Regel werden sie einmal pro Frame gezeichnet. */ -public interface Zeichenbar { +public interface Drawable { /** * Gibt an, ob das Objekt derzeit sichtbar ist (also gezeichnet werden @@ -14,11 +14,11 @@ public interface Zeichenbar { * * @return true, wenn das Objekt sichtbar ist. */ - public boolean istSichtbar(); + boolean isVisible(); /** * Wird aufgerufen, um das Objekt auf die Zeichenfläche graphics - * zu zeichnen. + * zu draw. *

* Das Objekt muss dafür Sorge tragen, dass der Zustand der Zeichenfläche * (Transformationsmatrix, Farbe, ...) erhalten bleibt. Das Objekt sollte @@ -26,6 +26,6 @@ public interface Zeichenbar { * * @param graphics Die Zeichenfläche. */ - public void zeichnen( Graphics2D graphics ); + void draw( Graphics2D graphics ); } diff --git a/src/schule/ngb/zm/DrawingLayer.java b/src/schule/ngb/zm/DrawingLayer.java new file mode 100644 index 0000000..34b8548 --- /dev/null +++ b/src/schule/ngb/zm/DrawingLayer.java @@ -0,0 +1,525 @@ +package schule.ngb.zm; + +import schule.ngb.zm.util.ImageLoader; + +import java.awt.*; +import java.awt.geom.*; +import java.util.Stack; + +public class DrawingLayer extends Layer { + + protected Color strokeColor; + + protected Color fillColor; + + protected double strokeWeight; + + protected Options.StrokeType strokeType = SOLID; + + private Options.Direction default_anchor = CENTER; + + protected Line2D.Double line = new Line2D.Double(); + protected Ellipse2D.Double ellipse = new Ellipse2D.Double(); + protected Rectangle2D.Double rect = new Rectangle2D.Double(); + protected Arc2D.Double arc = new Arc2D.Double(); + + private Stack transformStack = new Stack<>(); + + private FontMetrics fontMetrics = null; + + public DrawingLayer() { + super(); + transformStack.push(new AffineTransform()); + + strokeColor = Color.BLACK; + fillColor = Color.WHITE; + strokeWeight = 1.0; + + fontMetrics = drawing.getFontMetrics(); + } + + public DrawingLayer( int width, int height ) { + super(width, height); + } + + public Color getColor() { + return fillColor; + } + + public void setColor( int gray ) { + setColor(gray, gray, gray, 255); + } + + public void setColor( Color color ) { + fillColor = color; + drawing.setColor(color.getColor()); + } + + public void noFill() { + fillColor = null; + } + + public void setColor( int gray, int alpha ) { + setColor(gray, gray, gray, alpha); + } + + public void setColor( int red, int green, int blue ) { + setColor(red, green, blue, 255); + } + + public void setColor( int red, int green, int blue, int alpha ) { + setColor(new Color(red, green, blue, alpha)); + } + + public Color getStrokeColor() { + return strokeColor; + } + + public void setStrokeColor( int gray ) { + setStrokeColor(gray, gray, gray, 255); + } + + public void setStrokeColor( Color color ) { + strokeColor = color; + drawing.setColor(color.getColor()); + } + + public void noStroke() { + strokeColor = null; + } + + public void setStrokeColor( int gray, int alpha ) { + setStrokeColor(gray, gray, gray, alpha); + } + + public void setStrokeColor( int red, int green, int blue ) { + setStrokeColor(red, green, blue, 255); + } + + public void setStrokeColor( int red, int green, int blue, int alpha ) { + setStrokeColor(new Color(red, green, blue, alpha)); + } + + public void setStrokeWeight( double pWeight ) { + strokeWeight = pWeight; + drawing.setStroke(createStroke()); + } + + protected Stroke createStroke() { + 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); + default: + return new BasicStroke( + (float) strokeWeight, + BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND); + } + } + + public Options.StrokeType getStrokeType() { + return strokeType; + } + + public void setStrokeType( Options.StrokeType type ) { + switch( type ) { + case DASHED: + this.strokeType = DASHED; + break; + case DOTTED: + this.strokeType = DOTTED; + break; + default: + this.strokeType = SOLID; + break; + } + } + + public void resetStroke() { + setStrokeColor(STD_STROKECOLOR); + setStrokeWeight(STD_STROKEWEIGHT); + setStrokeType(SOLID); + } + + public void setAnchor( Options.Direction anchor ) { + default_anchor = anchor; + } + + public void clear( int gray ) { + clear(gray, gray, gray, 255); + } + + public void clear( int gray, int alpha ) { + clear(gray, gray, gray, alpha); + } + + public void clear( int red, int green, int blue ) { + clear(red, green, blue, 255); + } + + public void clear( int red, int green, int blue, int alpha ) { + clear(new Color(red, green, blue, alpha)); + } + + public void clear( Color pColor ) { + /*graphics.setBackground(pColor); + graphics.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());*/ + java.awt.Color currentColor = drawing.getColor(); + pushMatrix(); + resetMatrix(); + drawing.setColor(pColor.getColor()); + drawing.fillRect(0, 0, buffer.getWidth(), buffer.getHeight()); + drawing.setColor(currentColor); + popMatrix(); + } + + public void line( double x1, double y1, double x2, double y2 ) { + //Shape line = new Line2D.Double(x1, y1, x2, y2); + line.setLine(x1, y1, x2, y2); + drawShape(line); + } + + public void pixel( double x, double y ) { + square(x, y, 1); + } + + public void square( double x, double y, double w ) { + rect(x, y, w, w); + } + + public void square( double x, double y, double w, Options.Direction anchor ) { + rect(x, y, w, w, anchor); + } + + public void rect( double x, double y, double w, double h ) { + rect(x, y, w, h, default_anchor); + } + + public void rect( double x, double y, double w, double h, Options.Direction anchor ) { + Point2D.Double anchorPoint = getAnchorPoint(x, y, w, h, anchor); + // Shape rect = new Rectangle2D.Double(anchorPoint.getX(), anchorPoint.getY(), w, h); + rect.setRect(anchorPoint.getX(), anchorPoint.getY(), w, h); + fillShape(rect); + drawShape(rect); + } + + public void point( double x, double y ) { + circle(x - 1, y - 1, 2); + } + + public void circle( double x, double y, double d ) { + ellipse(x, y, d, d, default_anchor); + } + + public void circle( double x, double y, double d, Options.Direction anchor ) { + ellipse(x, y, d, d, anchor); + } + + public void ellipse( double x, double y, double w, double h ) { + ellipse(x, y, w, h, default_anchor); + } + + public void ellipse( double x, double y, double w, double h, Options.Direction anchor ) { + Point2D.Double anchorPoint = getAnchorPoint(x, y, w, h, anchor); + // Shape ellipse = new Ellipse2D.Double(anchorPoint.x, anchorPoint.y, w, h); + ellipse.setFrame(anchorPoint.x, anchorPoint.y, w, h); + fillShape(ellipse); + drawShape(ellipse); + } + + public void arc( double x, double y, double d, double angle1, double angle2 ) { + while( angle2 < angle1 ) angle2 += 360.0; + + Point2D.Double anchorPoint = getAnchorPoint(x, y, d, d, CENTER); + /*Shape arc = new Arc2D.Double( + anchorPoint.x, + anchorPoint.y, + d, d, + angle1, angle2 - angle1, + Arc2D.OPEN + );*/ + arc.setArc( + anchorPoint.x, anchorPoint.y, + d, d, + angle1, angle2 - angle1, + Arc2D.OPEN + ); + + drawShape(arc); + } + + public void pie( double x, double y, double d, double angle1, double angle2 ) { + while( angle2 < angle1 ) angle2 += 360.0; + + Point2D.Double anchorPoint = getAnchorPoint(x, y, d, d, CENTER); + /*Shape arc = new Arc2D.Double( + anchorPoint.x, + anchorPoint.y, + d, d, + angle1, angle2 - angle1, + Arc2D.PIE + );*/ + arc.setArc( + anchorPoint.x, anchorPoint.y, + d, d, + angle1, angle2 - angle1, + Arc2D.PIE + ); + + fillShape(arc); + drawShape(arc); + } + + public void curve( double x1, double y1, double x2, double y2, double x3, double y3 ) { + QuadCurve2D curve = new QuadCurve2D.Double(x1, y1, x2, y2, x3, y3); + drawShape(curve); + } + + public void curve( double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4 ) { + CubicCurve2D curve = new CubicCurve2D.Double(x1, y1, x2, y2, x3, y3, x4, y4); + drawShape(curve); + } + + public void triangle( double x1, double y1, double x2, double y2, double x3, double y3 ) { + Path2D path = new Path2D.Double(); + path.moveTo(x1, y1); + path.lineTo(x2, y2); + path.lineTo(x3, y3); + path.lineTo(x1, y1); + + fillShape(path); + drawShape(path); + } + + public void rhombus( double x, double y, double width, double height ) { + rhombus(x, y, width, height, default_anchor); + } + + public void rhombus( double x, double y, double width, double height, Options.Direction anchor ) { + double whalf = width / 2.0, hhalf = height / 2.0; + Point2D.Double anchorPoint = getAnchorPoint(x, y, width, height, anchor); + polygon(anchorPoint.x + whalf, anchorPoint.y, anchorPoint.x + width, anchorPoint.y + hhalf, anchorPoint.x + whalf, anchorPoint.y + height, anchorPoint.x, anchorPoint.y + hhalf); + } + + public void polygon( double... coordinates ) { + if( coordinates.length < 4 ) { + return; + } + + Path2D path = new Path2D.Double(); + path.moveTo(coordinates[0], coordinates[1]); + for( int i = 2; i < coordinates.length; i += 2 ) { + if( i + 1 < coordinates.length ) { + path.lineTo(coordinates[i], coordinates[i + 1]); + } + } + + int len = coordinates.length; + if( coordinates[len - 2] != coordinates[0] || coordinates[len - 1] != coordinates[1] ) { + path.lineTo(coordinates[0], coordinates[1]); + } + + fillShape(path); + drawShape(path); + } + + public void polygon( Point2D... points ) { + if( points.length < 2 ) { + return; + } + + Path2D path = new Path2D.Double(); + path.moveTo(points[0].getX(), points[0].getY()); + for( int i = 1; i < points.length; i += 1 ) { + path.lineTo(points[i].getX(), points[i].getY()); + } + + int len = points.length; + if( points[len - 1].equals(points[0]) ) { + path.moveTo(points[0].getX(), points[0].getY()); + } + + fillShape(path); + drawShape(path); + } + + public void image( String imageSource, double x, double y ) { + image(ImageLoader.loadImage(imageSource), x, y, 1.0, default_anchor); + } + + public void image( String imageSource, double x, double y, Options.Direction anchor ) { + image(ImageLoader.loadImage(imageSource), x, y, 1.0, anchor); + } + + public void image( String imageSource, double x, double y, double scale ) { + image(ImageLoader.loadImage(imageSource), x, y, scale, default_anchor); + } + + public void image( String imageSource, double x, double y, double scale, Options.Direction anchor ) { + image(ImageLoader.loadImage(imageSource), x, y, scale, anchor); + } + + public void image( Image image, double x, double y ) { + image(image, x, y, 1.0, default_anchor); + } + + public void image( Image image, double x, double y, double scale ) { + image(image, x, y, scale, default_anchor); + } + + public void image( Image image, double x, double y, double scale, Options.Direction anchor ) { + if( image != null ) { + double neww = image.getWidth(null) * scale; + double newh = image.getHeight(null) * scale; + Point2D.Double anchorPoint = getAnchorPoint(x, y, neww, newh, anchor); + drawing.drawImage(image, (int) anchorPoint.x, (int) anchorPoint.y, (int) neww, (int) newh, null); + } + } + + public void text( String text, double x, double y ) { + text(text, x, y, default_anchor); + } + + public void text( String text, double x, double y, Options.Direction anchor ) { + FontMetrics fm = drawing.getFontMetrics(); + Point2D.Double anchorPoint = getAnchorPoint(x, y + fm.getAscent(), fm.stringWidth(text), fm.getHeight(), anchor); + + drawing.drawString(text, (float) anchorPoint.x, (float) anchorPoint.y); + } + + public void setFontSize( int size ) { + setFont(drawing.getFont().deriveFont((float)size)); + } + + public void setFont( String fontName ) { + Font font = new Font(fontName, drawing.getFont().getStyle(), drawing.getFont().getSize()); + setFont(font); + } + + public void setFont( String fontName, int size ) { + Font font = new Font(fontName, drawing.getFont().getStyle(), size); + setFont(font); + } + + public void setFont( String fontName, int size, int style ) { + Font font = new Font(fontName, style, size); + setFont(font); + } + + public void setFont( Font font ) { + drawing.setFont(font); + fontMetrics = drawing.getFontMetrics(); + } + + + private Point2D.Double transformToCanvas( double x, double y ) { + return transformToCanvas(new Point2D.Double(x,y)); + } + + private Point2D.Double transformToCanvas( Point2D.Double pPoint ) { + AffineTransform matrix = getMatrix(); + matrix.transform(pPoint, pPoint); + return pPoint; + } + + private Point2D.Double transformToUser( double x, double y ) { + return transformToUser(new Point2D.Double(x,y)); + } + + private Point2D.Double transformToUser( Point2D.Double pPoint ) { + AffineTransform matrix = getMatrix(); + + try { + matrix.inverseTransform(pPoint, pPoint); + } catch( NoninvertibleTransformException e ) { + e.printStackTrace(); + } + + return pPoint; + } + + private Point2D.Double getAnchorPoint( double x, double y, double w, double h, Options.Direction anchor ) { + double whalf = w * .5, hhalf = h * .5; + + // anchor == CENTER + Point2D.Double anchorPoint = new Point2D.Double(x - whalf, y - hhalf); + + if( NORTH.is(anchor) ) { + anchorPoint.y += hhalf; + } + if( SOUTH.is(anchor) ) { + anchorPoint.y -= hhalf; + } + if( WEST.is(anchor) ) { + anchorPoint.x += whalf; + } + if( EAST.is(anchor) ) { + anchorPoint.x -= whalf; + } + + return anchorPoint; + } + + private void fillShape( Shape pShape ) { + if( fillColor != null && fillColor.getAlpha() > 0.0 ) { + drawing.setColor(fillColor.getColor()); + drawing.fill(pShape); + } + } + + private void drawShape( Shape pShape ) { + if( strokeColor != null && strokeColor.getAlpha() > 0.0 + && strokeWeight > 0.0 ) { + drawing.setColor(strokeColor.getColor()); + drawing.setStroke(createStroke()); + drawing.draw(pShape); + } + } + + public void translate( double dx, double dy ) { + drawing.translate(dx, dy); + } + + public void scale( double factor ) { + drawing.scale(factor, factor); + } + + public void rotate( double pAngle ) { + drawing.rotate(Math.toRadians(pAngle)); + } + + public void shear( double dx, double dy ) { + drawing.shear(dx, dy); + } + + public AffineTransform getMatrix() { + return drawing.getTransform(); + } + + public void pushMatrix() { + transformStack.push(drawing.getTransform()); + } + + public void popMatrix() { + if( transformStack.isEmpty() ) { + resetMatrix(); + } else { + drawing.setTransform(transformStack.pop()); + } + } + + public void resetMatrix() { + drawing.setTransform(new AffineTransform()); + } + +} diff --git a/src/schule/ngb/zm/Ebene.java b/src/schule/ngb/zm/Ebene.java deleted file mode 100644 index fd64ccf..0000000 --- a/src/schule/ngb/zm/Ebene.java +++ /dev/null @@ -1,106 +0,0 @@ -package schule.ngb.zm; - -import java.awt.*; -import java.awt.image.BufferedImage; - -public abstract class Ebene extends Konstanten implements Zeichenbar, Aktualisierbar { - - protected BufferedImage puffer; - - protected Graphics2D zeichnung; - - protected boolean sichtbar = true; - - protected boolean aktiv = true; - - - public Ebene() { - this(STD_BREITE, STD_HOEHE); - } - - public Ebene( int pBreite, int pHoehe ) { - zeichnungErstellen(pBreite, pHoehe); - } - - public int getBreite() { - return puffer.getWidth(); - } - - public int getHoehe() { - return puffer.getHeight(); - } - - public void setGroesse( int pBreite, int pHoehe ) { - if( puffer != null ) { - zeichnenWiederherstellen(pBreite, pHoehe); - } else { - zeichnungErstellen(pBreite, pHoehe); - } - } - - private void zeichnungErstellen( int pBreite, int pHoehe ) { - puffer = new BufferedImage(pBreite, pHoehe, BufferedImage.TYPE_INT_ARGB); - zeichnung = puffer.createGraphics(); - - // add antialiasing - RenderingHints hints = new RenderingHints( - RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON - ); - hints.put( - RenderingHints.KEY_RENDERING, - RenderingHints.VALUE_RENDER_QUALITY - ); - zeichnung.addRenderingHints(hints); - } - - private void zeichnenWiederherstellen( int pBreite, int pHoehe ) { - BufferedImage oldCanvas = puffer; - zeichnungErstellen(pBreite, pHoehe); - zeichnung.drawImage(oldCanvas, 0, 0, null); - } - - /** - * Leert die Ebene und löscht alles bisher gezeichnete. - */ - public abstract void leeren(); - - /** - * Zeichnet den Puffer auf die Grafik-Instanz. - * - * @param pGraphics - */ - @Override - public void zeichnen( Graphics2D pGraphics ) { - if( sichtbar ) { - pGraphics.drawImage(puffer, 0, 0, null); - } - } - - @Override - public boolean istSichtbar() { - return sichtbar; - } - - public void verstecken() { - sichtbar = false; - } - - public void zeigen() { - sichtbar = true; - } - - public void umschalten() { - sichtbar = !sichtbar; - } - - @Override - public void aktualisieren( double delta ) { - } - - @Override - public boolean istAktiv() { - return aktiv; - } - -} diff --git a/src/schule/ngb/zm/Farbe.java b/src/schule/ngb/zm/Farbe.java deleted file mode 100644 index 2c2327f..0000000 --- a/src/schule/ngb/zm/Farbe.java +++ /dev/null @@ -1,95 +0,0 @@ -package schule.ngb.zm; - -import java.awt.*; - -public class Farbe extends Color { - - public static final Farbe SCHWARZ = new Farbe(Color.BLACK); - public static final Farbe WEISS = new Farbe(Color.WHITE); - public static final Farbe GRAU = new Farbe(Color.GRAY); - public static final Farbe DUNKELGRAU = new Farbe(Color.DARK_GRAY); - public static final Farbe HELLGRAU = new Farbe(Color.LIGHT_GRAY); - - public static final Farbe ROT = new Farbe(Color.RED); - public static final Farbe GRUEN = new Farbe(Color.GREEN); - public static final Farbe BLAU = new Farbe(Color.BLUE); - public static final Farbe GELB = new Farbe(Color.YELLOW); - public static final Farbe ORANGE = new Farbe(Color.ORANGE); - public static final Farbe CYAN = new Farbe(Color.CYAN); - public static final Farbe MAGENTA = new Farbe(Color.MAGENTA); - public static final Farbe PINK = new Farbe(Color.PINK); - - public static final Farbe HGGRUEN = new Farbe(0, 165, 81); - public static final Farbe HGROT = new Farbe(151, 54, 60); - - - public Farbe( int pGrau ) { - super(pGrau, pGrau, pGrau, 255); - } - - public Farbe( int pGrau, int pAlpha ) { - super(pGrau, pGrau, pGrau, pAlpha); - } - - - public Farbe( int pRot, int pGruen, int pBlau ) { - super(pRot, pGruen, pBlau); - } - - public Farbe( int pRot, int pGruen, int pBlau, int pAlpha ) { - super(pRot, pGruen, pBlau, pAlpha); - } - - public Farbe( Color pColor ) { - super(pColor.getRed(), pColor.getGreen(), pColor.getBlue(), pColor.getAlpha()); - } - - public Farbe( Color pColor, int pAlpha ) { - super(pColor.getRed(), pColor.getGreen(), pColor.getBlue(), pAlpha); - } - - public static Farbe vonRGB( int pRGB ) { - return new Farbe( - (pRGB >> 16) & 255, - (pRGB >> 8) & 255, - pRGB & 255, - (pRGB >> 24) & 255); - } - - public static Farbe vonHexcode( String pHexcode ) { - if( pHexcode.startsWith("#") ) { - pHexcode = pHexcode.substring(1); - } - - int red = Integer.valueOf(pHexcode.substring(0, 2), 16); - int green = Integer.valueOf(pHexcode.substring(2, 4), 16); - int blue = Integer.valueOf(pHexcode.substring(4, 6), 16); - - int alpha = 255; - if( pHexcode.length() == 8 ) { - alpha = Integer.valueOf(pHexcode.substring(6, 8), 16); - } - - return new Farbe(red, green, blue, alpha); - } - - public static Farbe morphen( Color pFarbe1, Color pFarbe2, double pFactor ) { - if( pFactor < 0.0 || pFarbe2 == null ) { - return new Farbe(pFarbe1); - } - if( pFactor > 1.0 || pFarbe1 == null ) - return new Farbe(pFarbe2); - double pFactorInv = 1 - pFactor; - return new Farbe( - (int) (pFactorInv * pFarbe1.getRed() + pFactor * pFarbe2.getRed()), - (int) (pFactorInv * pFarbe1.getGreen() + pFactor * pFarbe2.getGreen()), - (int) (pFactorInv * pFarbe1.getBlue() + pFactor * pFarbe2.getBlue()), - (int) (pFactorInv * pFarbe1.getAlpha() + pFactor * pFarbe2.getAlpha()) - ); - } - - public Farbe kopie() { - return new Farbe(this); - } - -} diff --git a/src/schule/ngb/zm/ImageLayer.java b/src/schule/ngb/zm/ImageLayer.java new file mode 100644 index 0000000..e64c87d --- /dev/null +++ b/src/schule/ngb/zm/ImageLayer.java @@ -0,0 +1,75 @@ +package schule.ngb.zm; + +import schule.ngb.zm.util.ImageLoader; + +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.image.ImageObserver; + +public class ImageLayer extends Layer { + + protected Image image; + + protected double x = 0; + + protected double y = 0; + + protected boolean redraw = true; + + public ImageLayer() { + + } + + + public ImageLayer( String source ) { + image = ImageLoader.loadImage(source); + } + + public ImageLayer( Image image ) { + this.image = image; + } + + public ImageLayer( int width, int height, Image image ) { + super(width, height); + this.image = image; + } + + public void setImage( Image image ) { + this.image = image; + redraw = true; + } + + public double getX() { + return x; + } + + public void setX( double pX ) { + this.x = pX; + redraw = true; + } + + public double getY() { + return y; + } + + public void setY( double pY ) { + this.y = pY; + redraw = true; + } + + @Override + public void clear() { + super.clear(); + redraw = true; + } + + @Override + public void draw( Graphics2D graphics ) { + if( redraw && visible ) { + drawing.drawImage(image, (int)x, (int)y, null); + redraw = false; + } + super.draw(graphics); + } + +} diff --git a/src/schule/ngb/zm/Konstanten.java b/src/schule/ngb/zm/Konstanten.java deleted file mode 100644 index 87b6d23..0000000 --- a/src/schule/ngb/zm/Konstanten.java +++ /dev/null @@ -1,145 +0,0 @@ -package schule.ngb.zm; - -import schule.ngb.zm.formen.Konturform; - -import java.awt.*; -import java.awt.geom.Arc2D; - -public class Konstanten { - - public static final String APP_NAME = "Zeichenmaschine"; - - public static final int STD_BREITE = 400; - public static final int STD_HOEHE = 400; - public static final int STD_FPS = 60; - - public static Color STD_HINTERGRUND = new Color(200, 200, 200); - - - public static final int DURCHGEZOGEN = Konturform.DURCHGEZOGEN; - public static final int GESTRICHELT = Konturform.GESTRICHELT; - public static final int GEPUNKTET = Konturform.GEPUNKTET; - - public static final int OFFEN = Arc2D.OPEN; - public static final int GESCHLOSSEN = Arc2D.CHORD; - public static final int KREISTEIL = Arc2D.PIE; - - - public static final byte ZENTRUM = 0; - public static final byte NORDEN = 1 << 0; - public static final byte OSTEN = 1 << 2; - public static final byte SUEDEN = 1 << 3; - public static final byte WESTEN = 1 << 4; - - public static final byte NORDOSTEN = NORDEN | OSTEN; - public static final byte SUEDOSTEN = SUEDEN | OSTEN; - public static final byte NORDWESTEN = NORDEN | WESTEN; - public static final byte SUEDWESTEN = SUEDEN | WESTEN; - - public static final byte MITTE = ZENTRUM; - public static final byte OBEN = NORDEN; - public static final byte RECHTS = OSTEN; - public static final byte UNTEN = SUEDEN; - public static final byte LINKS = WESTEN; - - - public static final Farbe SCHWARZ = Farbe.SCHWARZ; - public static final Farbe WEISS = Farbe.WEISS; - public static final Farbe ROT = Farbe.ROT; - public static final Farbe BLAU = Farbe.BLAU; - public static final Farbe GRUEN = Farbe.GRUEN; - public static final Farbe GELB = Farbe.GELB; - - public Farbe farbe( int pGrau ) { - return farbe(pGrau, pGrau, pGrau, 255); - } - - public Farbe farbe( int pGrau, int pAlpha ) { - return farbe(pGrau, pGrau, pGrau, pAlpha); - } - - public Farbe farbe(int red, int green, int blue) { - return farbe(red, green, blue, 255); - } - - public Farbe farbe(int red, int green, int blue, int alpha) { - if (red < 0 || red >= 256) - throw new IllegalArgumentException("red must be between 0 and 255"); - if (green < 0 || green >= 256) - throw new IllegalArgumentException("green must be between 0 and 255"); - if (blue < 0 || blue >= 256) - throw new IllegalArgumentException("blue must be between 0 and 255"); - if (alpha < 0 || alpha >= 256) - throw new IllegalArgumentException("alpha must be between 0 and 255"); - - return new Farbe(red, green, blue, alpha); - } - - - - public double abs( double x ) { - return Math.abs(x); - } - - public double vorzeichen( double x ) { - return Math.signum(x); - } - - public double runden( double x ) { - return Math.round(x); - } - - public double abrunden( double x ) { - return Math.floor(x); - } - - public double aufrunden( double x ) { - return Math.ceil(x); - } - - public double sin( double x ) { - return Math.sin(x); - } - - public double cos( double x ) { - return Math.cos(x); - } - - public double tan( double x ) { - return Math.tan(x); - } - - public double arcsin( double x ) { - return Math.asin(x); - } - - public double arccos( double x ) { - return Math.acos(x); - } - - public double arctan( double x ) { - return Math.atan(x); - } - - public double beschraenken( double x, double max ) { - if( x > max ) { - return max; - } - return x; - } - - public double beschraenken( double x, double min, double max ) { - if( x > max ) { - return max; - } - if( x < min ) { - return min; - } - return x; - } - - public double morphen( double pVon, double pNach, double pFaktor ) { - return pVon - pFaktor*(pVon+pNach); - } - -} diff --git a/src/schule/ngb/zm/Layer.java b/src/schule/ngb/zm/Layer.java new file mode 100644 index 0000000..d81f702 --- /dev/null +++ b/src/schule/ngb/zm/Layer.java @@ -0,0 +1,138 @@ +package schule.ngb.zm; + +import java.awt.Color; +import java.awt.*; +import java.awt.image.BufferedImage; + +public abstract class Layer extends Constants implements Drawable, Updatable { + + protected BufferedImage buffer; + + protected Graphics2D drawing; + + protected boolean visible = true; + + protected boolean active = true; + + + public Layer() { + this(STD_WIDTH, STD_HEIGHT); + } + + public Layer( int width, int height ) { + createCanvas(width, height); + } + + public int getWidth() { + return buffer.getWidth(); + } + + public int getHeight() { + return buffer.getHeight(); + } + + public void setSize( int width, int height ) { + if( buffer != null ) { + if( buffer.getWidth() != width || buffer.getHeight() != height ) { + recreateCanvas(width, height); + } + } else { + createCanvas(width, height); + } + + } + + public void dispose() { + drawing.dispose(); + } + + /** + * Erstellt einen neuen Puffer für die Ebene und konfiguriert diesen. + * + * @param width Width des neuen Puffers. + * @param height Höhe des neuen Puffers. + */ + private void createCanvas( int width, int height ) { + buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + drawing = buffer.createGraphics(); + + // add antialiasing + RenderingHints hints = new RenderingHints( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON + ); + hints.put( + RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON + ); + hints.put( + RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY + ); + drawing.addRenderingHints(hints); + } + + /** + * Erstellt einen neuen Puffer für dei Ebene mit der angegebenen Größe + * und kopiert den Inhalt des alten Puffers in den Neuen. + * + * @param width Width des neuen Puffers. + * @param height Höhe des neuen Puffers. + */ + private void recreateCanvas( int width, int height ) { + BufferedImage oldCanvas = buffer; + createCanvas(width, height); + drawing.drawImage(oldCanvas, 0, 0, null); + } + + /** + * Leert die Ebene und löscht alles bisher gezeichnete. Alle Pixel der + * Ebene werden transparent, damit unterliegende Ebenen durchscheinen können. + */ + public void clear() { + // https://stackoverflow.com/questions/31149206/set-pixels-of-bufferedimage-as-transparent + drawing.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR)); + drawing.setColor(new Color(255, 255, 255, 255)); + drawing.fillRect(0, 0, buffer.getWidth(), buffer.getHeight()); + drawing.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); + } + + /** + * Zeichnet den Puffer auf die Grafik-Instanz. + * + * @param pGraphics + */ + @Override + public void draw( Graphics2D pGraphics ) { + if( visible ) { + pGraphics.drawImage(buffer, 0, 0, null); + } + } + + @Override + public boolean isVisible() { + return visible; + } + + public void hide() { + visible = false; + } + + public void view() { + visible = true; + } + + public void toggle() { + visible = !visible; + } + + @Override + public void update( double delta ) { + } + + @Override + public boolean isActive() { + return active; + } + +} diff --git a/src/schule/ngb/zm/Leinwand.java b/src/schule/ngb/zm/Leinwand.java deleted file mode 100644 index fe6040d..0000000 --- a/src/schule/ngb/zm/Leinwand.java +++ /dev/null @@ -1,118 +0,0 @@ -package schule.ngb.zm; - -import schule.ngb.zm.formen.Formenebene; - -import javax.swing.*; -import java.awt.*; -import java.util.ArrayList; -import java.util.LinkedList; - -/** - * Eine Leinwand ist die Hauptkomponente einer Zeichenmaschine. Sie besteht aus - * mehreren Ebenen, auf denen auf verschiedene Arten gezeichnet werden kann. Die - * Ebenen lassen sich beliebig übereinander anordenen, ausblenden oder wieder - * löschen. - * - * Jede Ebene besitzt eine Zeichenfläche, auf der ihre Zeichnung liegt. Diese - * zeichenflächen werden pro Frame einmal von "unten" nach "oben" auf diese - * Leinwand gezeichnet. - */ -public class Leinwand extends JComponent { - - - private LinkedList ebenen; - - public Leinwand( int pBreite, int pHoehe ) { - Dimension dim = new Dimension(pBreite, pHoehe); - super.setSize(pBreite, pHoehe); - this.setPreferredSize(dim); - this.setMinimumSize(dim); - - ebenen = new LinkedList<>(); - ebenen.add(new Zeichenebene()); - ebenen.add(new Formenebene()); - setBackground(Konstanten.STD_HINTERGRUND); - } - - @Override - public void setSize( int pBreite, int pHoehe ) { - Dimension dim = new Dimension(pBreite, pHoehe); - super.setSize(pBreite, pHoehe); - this.setPreferredSize(dim); - this.setMinimumSize(dim); - - for( Ebene ebene : ebenen ) { - ebene.setGroesse(getWidth(), getHeight()); - } - } - - public void hinzu( Ebene pEbene ) { - if( pEbene != null ) { - pEbene.setGroesse(getWidth(), getHeight()); - ebenen.add(pEbene); - } - } - - public void hinzu( int pIndex, Ebene pEbene ) { - if( pEbene != null ) { - pEbene.setGroesse(getWidth(), getHeight()); - ebenen.add(pIndex, pEbene); - } - } - - public java.util.List getEbenen() { - return ebenen; - } - - /** - * Holt die {@link Ebene} am Index i (beginnend bei 0). - * - * @param i Index der Ebene (beginnend bei 0). - * @return Die Ebene am Index i oder null. - * @throws IndexOutOfBoundsException Falls der Index nicht existiert. - */ - public Ebene getEbene( int i ) { - if( ebenen.size() > i ) { - return ebenen.get(i); - } else { - throw new IndexOutOfBoundsException("Keine Ebene mit dem Index " + i + " vorhanden (maximum: " + (ebenen.size() - 1) + ")."); - } - } - - /** - * Holt die erste Ebene des angegebenen Typs aus der Liste der Ebenen. - * Existiert keine solche Ebene, wird null zurückgegeben. - * - * @param pClazz Typ der Ebene. - * @param - * @return Erste Ebene vom angegeben Typ. - */ - public L getEbene( Class pClazz ) { - for( Ebene ebene : ebenen ) { - if( ebene.getClass().equals(pClazz) ) { - return pClazz.cast(ebene); - } - } - return null; - } - - public java.util.List getEbenen( Class pClazz ) { - ArrayList result = new ArrayList<>(ebenen.size()); - for( Ebene ebene : ebenen ) { - if( ebene.getClass().equals(pClazz) ) { - result.add(pClazz.cast(ebene)); - } - } - return result; - } - - public void paintComponent( Graphics g ) { - Graphics2D g2d = (Graphics2D) g.create(); - - for( Ebene ebene : ebenen ) { - ebene.zeichnen(g2d); - } - - g2d.dispose(); - } -} diff --git a/src/schule/ngb/zm/Options.java b/src/schule/ngb/zm/Options.java new file mode 100644 index 0000000..7207bfe --- /dev/null +++ b/src/schule/ngb/zm/Options.java @@ -0,0 +1,67 @@ +package schule.ngb.zm; + +import java.awt.geom.Arc2D; + +/** + * Diese Klasse sammelt Enumerationen, die verschiedene Eigenschaften der + * zu zeichnenden Formen darstellen. + */ +public final class Options { + private Options() {} + + public enum StrokeType { + SOLID, DASHED, DOTTED + } + + public enum ArrowHead { + LINES, FILLED + } + + public enum PathType { + OPEN(Arc2D.OPEN), CLOSED(Arc2D.CHORD), PIE(Arc2D.PIE); + public final int awt_type; + PathType( int type ) { + awt_type = type; + } + } + + public enum AppState { + INITIALIZING, + INITIALIZED, + RUNNING, + AUSED, + STOPPED, + TERMINATED + } + + public enum Direction { + CENTER(0), + NORTH(1 << 0), + EAST(1 << 1), + SOUTH(1 << 2), + WEST(1 << 3), + + NORTHEAST(NORTH.mask | EAST.mask), + SOUTHEAST(SOUTH.mask | EAST.mask), + NORTHWEST(NORTH.mask | WEST.mask), + SOUTHWEST(SOUTH.mask | WEST.mask), + + MIDDLE(CENTER.mask), + UP(NORTH.mask), + DOWN(SOUTH.mask), + LEFT(WEST.mask), + RIGHT(EAST.mask); + + public final byte mask; + Direction( int mask ) { + this.mask = (byte) mask; + } + public boolean is( int mask ) { + return (mask & this.mask) == this.mask; + } + public boolean is( Direction dir ) { + return (dir.mask & this.mask) == this.mask; + } + } + +} diff --git a/src/schule/ngb/zm/Shape2DLayer.java b/src/schule/ngb/zm/Shape2DLayer.java new file mode 100644 index 0000000..2beed75 --- /dev/null +++ b/src/schule/ngb/zm/Shape2DLayer.java @@ -0,0 +1,166 @@ +package schule.ngb.zm; + +import java.awt.*; +import java.util.LinkedList; + +public final class Shape2DLayer extends Layer { + + protected Color strokeColor = STD_STROKECOLOR; + + protected Color fillColor = STD_FILLCOLOR; + + protected double strokeWeight = STD_STROKEWEIGHT; + + protected Options.StrokeType strokeType = SOLID; + + private LinkedList shapes; + + private boolean instantDraw = false; + + public Shape2DLayer() { + super(); + shapes = new LinkedList(); + } + + public Shape2DLayer( boolean instantDraw ) { + super(); + shapes = new LinkedList(); + this.instantDraw = instantDraw; + } + + public Shape2DLayer( int width, int height ) { + super(width, height); + shapes = new LinkedList(); + } + + public Shape2DLayer( int width, int height, boolean instantDraw ) { + super(width, height); + shapes = new LinkedList(); + this.instantDraw = instantDraw; + } + + public Color getFillColor() { + return fillColor; + } + + public void setFillColor( int gray ) { + setFillColor(gray, gray, gray, 255); + } + + public void setFillColor( Color pColor ) { + fillColor = pColor; + drawing.setColor(pColor.getColor()); + } + + public void noFill() { + fillColor = null; + } + + public void setFillColor( int gray, int alpha ) { + setFillColor(gray, gray, gray, alpha); + } + + public void setFillColor( int red, int green, int blue ) { + setFillColor(red, green, blue, 255); + } + + public void setFillColor( int red, int green, int blue, int alpha ) { + setFillColor(new Color(red, green, blue, alpha)); + } + + public Color getStrokeColor() { + return strokeColor; + } + + public void setStrokeColor( int gray ) { + setStrokeColor(gray, gray, gray, 255); + } + + public void setStrokeColor( Color pColor ) { + strokeColor = pColor; + drawing.setColor(pColor.getColor()); + } + + public void noStroke() { + strokeColor = null; + } + + public void setStrokeColor( int gray, int alpha ) { + setStrokeColor(gray, gray, gray, alpha); + } + + public void setStrokeColor( int red, int green, int blue ) { + setStrokeColor(red, green, blue, 255); + } + + public void setStrokeColor( int red, int green, int blue, int alpha ) { + setStrokeColor(new Color(red, green, blue, alpha)); + } + + public void setStrokeWeight( double pWeight ) { + strokeWeight = pWeight; + drawing.setStroke(createStroke()); + } + + protected Stroke createStroke() { + 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); + default: + return new BasicStroke( + (float) strokeWeight, + BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND); + } + } + + public Options.StrokeType getStrokeType() { + return strokeType; + } + + public void setStrokeType( Options.StrokeType strokeType ) { + this.strokeType = strokeType; + } + + public java.util.List getShapes() { + return shapes; + } + + public void add( java.awt.Shape s ) { + shapes.add(s); + + if( instantDraw ) { + drawing.setColor(fillColor.getColor()); + drawing.fill(s); + + drawing.setColor(strokeColor.getColor()); + drawing.draw(s); + } + } + + @Override + public void draw( Graphics2D pGraphics ) { + if( !instantDraw ) { + for( Shape shape : shapes ) { + drawing.setColor(fillColor.getColor()); + drawing.fill(shape); + + drawing.setColor(strokeColor.getColor()); + drawing.draw(shape); + } + } + + super.draw(pGraphics); + } + +} diff --git a/src/schule/ngb/zm/Aktualisierbar.java b/src/schule/ngb/zm/Updatable.java similarity index 72% rename from src/schule/ngb/zm/Aktualisierbar.java rename to src/schule/ngb/zm/Updatable.java index 9841ecf..0a60447 100644 --- a/src/schule/ngb/zm/Aktualisierbar.java +++ b/src/schule/ngb/zm/Updatable.java @@ -2,22 +2,22 @@ package schule.ngb.zm; /** * Aktualisierbare Objekte können in regelmäßigen Intervallen (meist einmal - * pro Frame) ihren Zustand aktualisieren. Diese Änderung kann abhängig vom + * pro Frame) ihren Zustand update. Diese Änderung kann abhängig vom * Zeitintervall (in Sekunden) zum letzten Aufruf passieren. */ -public interface Aktualisierbar { +public interface Updatable { /** * Gibt an, ob das Objekt gerade auf Aktualisierungen reagiert. * @return true, wenn das Objekt aktiv ist. */ - public boolean istAktiv(); + public boolean isActive(); /** * Änderung des Zustandes des Objekts abhängig vom Zeitintervall * delta in Sekunden. * @param delta Zeitintervall seit dem letzten Aufruf (in Sekunden). */ - public void aktualisieren( double delta ); + public void update( double delta ); } diff --git a/src/schule/ngb/zm/Vector.java b/src/schule/ngb/zm/Vector.java new file mode 100644 index 0000000..453a84f --- /dev/null +++ b/src/schule/ngb/zm/Vector.java @@ -0,0 +1,253 @@ +package schule.ngb.zm; + +import java.awt.geom.Point2D; + +public class Vector extends Point2D.Double { + + public static final Vector UP = new Vector(0, -1.0); + public static final Vector DOWN = new Vector(0, 1.0); + public static final Vector RIGHT = new Vector(1.0, 0); + public static final Vector LEFT = new Vector(-1.0, -1.0); + + + public Vector() { + x = 0.0; + y = 0.0; + } + + public Vector( double x, double pY ) { + this.x = x; + y = pY; + } + + public Vector( Point2D point ) { + x = point.getX(); + x = point.getY(); + } + + public Vector( Vector vec ) { + x = vec.x; + y = vec.y; + } + + public static Vector random() { + return new Vector(Math.random() * 100, Math.random() * 100); + } + + public static Vector random( double min, double max ) { + return new Vector(Math.random() * (max - min) + min, Math.random() * (max - min) + min); + } + + public static Vector setLength( Vector vector, double length ) { + Vector vec = vector.copy(); + vec.setLen(length); + return vec; + } + + public static Vector normalize( Vector vector ) { + Vector vec = vector.copy(); + vec.normalize(); + return vec; + } + + public static Vector add( Vector vector1, Vector vector2 ) { + Vector vec = vector1.copy(); + vec.add(vector2); + return vec; + } + + public static Vector sub( Vector vector1, Vector vector2 ) { + Vector vec = vector1.copy(); + vec.sub(vector2); + return vec; + } + + public static Vector scale( Vector vector, double scalar ) { + Vector vec = vector.copy(); + vec.scale(scalar); + return vec; + } + + public static Vector div( Vector vector, double scalar ) { + Vector vec = vector.copy(); + vec.div(scalar); + return vec; + } + + public static double dist( Vector vector1, Vector vector2 ) { + return vector1.dist(vector2); + } + + public static double dot( Vector vector1, Vector vector2 ) { + return vector1.dot(vector2); + } + + public static double cross( Vector vector1, Vector vector2 ) { + return vector1.cross(vector2); + } + + public static Vector rotate( Vector vector, double degree ) { + Vector vec = vector.copy(); + vec.rotate(degree); + return vec; + } + + public static Vector morph( Vector vector1, Vector vector2, float t ) { + Vector vec = vector1.copy(); + vec.morph(vector2, t); + return vec; + } + + public Vector copy() { + return new Vector(x, y); + } + + public Vector set( double x, double y ) { + this.x = x; + this.y = y; + return this; + } + + public Vector set( Vector vector ) { + x = vector.x; + y = vector.y; + return this; + } + + public Vector set( Point2D pPunkt ) { + x = pPunkt.getX(); + x = pPunkt.getY(); + return this; + } + + public void setX( double x ) { + this.x = x; + } + + public void setY( double y ) { + this.y = y; + } + + public Point2D getPunkt() { + return new Point2D.Double(x, y); + } + + public double len() { + return Math.sqrt(x * x + y * y); + } + + public double lenSq() { + return x * x + y * y; + } + + public Vector setLen( double length ) { + normalize(); + return scale(length); + } + + public Vector normalize() { + double len = len(); + if( len != 0 && len != 1 ) { + x /= len; + y /= len; + } + return this; + } + + public Vector add( Vector vector ) { + x += vector.x; + y += vector.y; + return this; + } + + public Vector add( double x, double y ) { + this.x += x; + this.y += y; + return this; + } + + public Vector sub( Vector vector ) { + x -= vector.x; + y -= vector.y; + return this; + } + + public Vector sub( double x, double y ) { + this.x -= x; + this.y -= y; + return this; + } + + public Vector scale( double scalar ) { + x *= scalar; + y *= scalar; + return this; + } + + public Vector div( double scalar ) { + x /= scalar; + y /= scalar; + return this; + } + + public double dist( Vector vector ) { + double dx = x - vector.x; + double dy = y - vector.y; + return Math.sqrt(dx * dx + dy * dy); + } + + public double dot( Vector vector ) { + return x * vector.x + y * vector.y; + } + + public double dot( double x, double y ) { + return this.x * x + this.y * y; + } + + // See: http://allenchou.net/2013/07/cross-product-of-2d-vectors/ + public double cross( Vector vector ) { + return x * vector.y - vector.x * y; + } + + public Vector limit( double max ) { + if( lenSq() > max * max ) { + setLen(max); + } + return this; + } + + public Vector limit( double min, double max ) { + if( min > max ) { + throw new IllegalArgumentException("HVector.constrain(): pMin muss kleiner sein als pMax."); + } + if( lenSq() < min * min ) { + setLen(min); + } else if( lenSq() > min * min ) { + setLen(max); + } + return this; + } + + public double angle() { + double angle = Math.atan2(y, x); + return Math.toDegrees(angle); + } + + public Vector rotate( double degree ) { + double temp = x, rad = Math.toRadians(degree); + x = x * Math.cos(rad) - y * Math.sin(rad); + y = temp * Math.sin(rad) + y * Math.cos(rad); + return this; + } + + public void morph( Vector vector, float t ) { + x = x + (vector.x - x) * t; + y = y + (vector.y - y) * t; + } + + @Override + public String toString() { + return "schule.ngb.zm.Vector[x = " + x + ", y = " + y + "]"; + } + +} diff --git a/src/schule/ngb/zm/Vektor.java b/src/schule/ngb/zm/Vektor.java deleted file mode 100644 index edfd1af..0000000 --- a/src/schule/ngb/zm/Vektor.java +++ /dev/null @@ -1,251 +0,0 @@ -package schule.ngb.zm; - -import java.awt.geom.Point2D; - -public class Vektor extends Point2D.Double { - - public Vektor() { - x = 0.0; - y = 0.0; - } - - public Vektor(double pX, double pY) { - x = pX; - y = pY; - } - - public Vektor(Point2D.Double pPunkt) { - x = pPunkt.getX(); - x = pPunkt.getY(); - } - - public Vektor(Vektor pVektor) { - x = pVektor.x; - y = pVektor.y; - } - - public static Vektor zufall() { - return new Vektor(Math.random()*100, Math.random()*100); - } - - public static Vektor zufall( double min, double max ) { - return new Vektor(Math.random()*(max-min)+min, Math.random()*(max-min)+min); - } - - public Vektor kopie() { - return new Vektor(x, y); - } - - public Vektor set(double pX, double pY) { - x = pX; - y = pY; - return this; - } - - public Vektor set(Vektor pVektor) { - x = pVektor.x; - y = pVektor.y; - return this; - } - - public Vektor set(Point2D.Double pPunkt) { - x = pPunkt.getX(); - x = pPunkt.getY(); - return this; - } - - public void setX(double pX) { - x = pX; - } - - public void setY(double pY) { - y = pY; - } - - public Point2D.Double getPunkt() { - return new Point2D.Double(x, y); - } - - public double laenge() { - return Math.sqrt(x * x + y * y); - } - - public double laengeQuad() { - return x * x + y * y; - } - - public Vektor setLaenge(double pLaenge) { - normalisieren(); - return skalieren(pLaenge); - } - - public static Vektor setLaenge(Vektor pVektor, double pLaenge) { - Vektor vec = pVektor.kopie(); - vec.setLaenge(pLaenge); - return vec; - } - - public Vektor normalisieren() { - double len = laenge(); - if (len != 0 && len != 1) { - x /= len; - y /= len; - } - return this; - } - - public static Vektor normalisieren(Vektor pVektor) { - Vektor vec = pVektor.kopie(); - vec.normalisieren(); - return vec; - } - - public Vektor addieren(Vektor pVektor) { - x += pVektor.x; - y += pVektor.y; - return this; - } - - public Vektor addieren(double pX, double pY) { - x += pX; - y += pY; - return this; - } - - public static Vektor addieren(Vektor pVektor1, Vektor pVektor2) { - Vektor vec = pVektor1.kopie(); - vec.addieren(pVektor2); - return vec; - } - - public void subtrahieren(Vektor pVektor) { - x -= pVektor.x; - y -= pVektor.y; - } - - public void subtrahieren(double pX, double pY) { - x -= pX; - y -= pY; - } - - public static Vektor subtrahieren(Vektor pVektor1, Vektor pVektor2) { - Vektor vec = pVektor1.kopie(); - vec.subtrahieren(pVektor2); - return vec; - } - - public Vektor skalieren(double pSkalar) { - x *= pSkalar; - y *= pSkalar; - return this; - } - - public static Vektor skalieren(Vektor pVektor, double pSkalar) { - Vektor vec = pVektor.kopie(); - vec.skalieren(pSkalar); - return vec; - } - - public Vektor dividieren(double pSkalar) { - x /= pSkalar; - y /= pSkalar; - return this; - } - - public static Vektor dividieren(Vektor pVektor, double pSkalar) { - Vektor vec = pVektor.kopie(); - vec.dividieren(pSkalar); - return vec; - } - - public double abstand(Vektor pVektor) { - double dx = x - pVektor.x; - double dy = y - pVektor.y; - return Math.sqrt(dx * dx + dy * dy); - } - - public static double abstand(Vektor pVektor1, Vektor pVektor2) { - return pVektor1.abstand(pVektor2); - } - - public double dot(Vektor pVektor) { - return x * pVektor.x + y * pVektor.y; - } - - public double dot(double pX, double pY) { - return x * pX + y * pY; - } - - public static double dot(Vektor pVektor1, Vektor pVektor2) { - return pVektor1.dot(pVektor2); - } - - // See: http://allenchou.net/2013/07/cross-product-of-2d-vectors/ - public double cross(Vektor pVektor) { - return x * pVektor.y - pVektor.x * y; - } - - public static double cross(Vektor pVektor1, Vektor pVektor2) { - return pVektor1.cross(pVektor2); - } - - public void limitieren(double pMax) { - if (laengeQuad() > pMax * pMax) { - normalisieren(); - skalieren(pMax); - } - } - - public void beschraenken(double pMin, double pMax) { - if (pMin > pMax) { - throw new IllegalArgumentException("HVector.constrain(): pMin muss kleiner sein als pMax."); - } - if (laengeQuad() < pMin * pMin) { - normalisieren(); - skalieren(pMin); - } - if (laengeQuad() > pMax * pMax) { - normalisieren(); - skalieren(pMax); - } - } - - public double richtung() { - double angle = Math.atan2(y, x); - return angle; - } - - public double winkel() { - return richtung(); - } - - public Vektor drehen(double pWinkel) { - double temp = x; - x = x * Math.cos(pWinkel) - y * Math.sin(pWinkel); - y = temp * Math.sin(pWinkel) + y * Math.cos(pWinkel); - return this; - } - - public static Vektor drehen(Vektor pVektor, double pWinkel) { - Vektor vec = pVektor.kopie(); - vec.drehen(pWinkel); - return vec; - } - - public void linterp(Vektor pVektor, float t) { - x = x + (pVektor.x - x) * t; - y = y + (pVektor.y - y) * t; - } - - public static Vektor linterp(Vektor pVektor1, Vektor pVektor2, float t) { - Vektor vec = pVektor1.kopie(); - vec.linterp(pVektor2, t); - return vec; - } - - @Override - public String toString() { - return "schule.ngb.zm.Vektor{x = " + x + ", y = " + y + "}"; - } - -} diff --git a/src/schule/ngb/zm/Zeichenebene.java b/src/schule/ngb/zm/Zeichenebene.java deleted file mode 100644 index b6000c7..0000000 --- a/src/schule/ngb/zm/Zeichenebene.java +++ /dev/null @@ -1,417 +0,0 @@ -package schule.ngb.zm; - -import java.awt.*; -import java.awt.geom.*; -import java.util.Stack; - -public class Zeichenebene extends Ebene { - - private int default_anchor = ZENTRUM; - - protected Color strokeColor; - - protected Color fillColor; - - protected double strokeWeight; - - protected int konturArt = DURCHGEZOGEN; - - public Color getColor() { - return fillColor; - } - - public void noFill() { - fillColor = null; - } - - public void setColor(int gray) { - setColor(gray, gray, gray, 255); - } - - public void setColor(int gray, int alpha) { - setColor(gray, gray, gray, alpha); - } - - public void setColor(int red, int green, int blue) { - setColor(red, green, blue, 255); - } - - public void setColor(int red, int green, int blue, int alpha ) { - if (red < 0 || red >= 256) throw new IllegalArgumentException("red must be between 0 and 255"); - if (green < 0 || green >= 256) throw new IllegalArgumentException("green must be between 0 and 255"); - if (blue < 0 || blue >= 256) throw new IllegalArgumentException("blue must be between 0 and 255"); - if (alpha < 0 || alpha >= 256) throw new IllegalArgumentException("alpha must be between 0 and 255"); - - setColor(new Color(red, green, blue, alpha)); - } - - public void setColor(Color pColor) { - fillColor = pColor; - zeichnung.setColor(pColor); - } - - public Color getStrokeColor() { - return strokeColor; - } - - public void noStroke() { - strokeColor = null; - } - - public void setStrokeColor(int gray) { - setStrokeColor(gray, gray, gray, 255); - } - - public void setStrokeColor(int gray, int alpha) { - setStrokeColor(gray, gray, gray, alpha); - } - - public void setStrokeColor(int red, int green, int blue) { - if (red < 0 || red >= 256) throw new IllegalArgumentException("red must be between 0 and 255"); - if (green < 0 || green >= 256) throw new IllegalArgumentException("green must be between 0 and 255"); - if (blue < 0 || blue >= 256) throw new IllegalArgumentException("blue must be between 0 and 255"); - setStrokeColor(red, green, blue, 255); - } - - public void setStrokeColor(int red, int green, int blue, int alpha ) { - if (red < 0 || red >= 256) throw new IllegalArgumentException("red must be between 0 and 255"); - if (green < 0 || green >= 256) throw new IllegalArgumentException("green must be between 0 and 255"); - if (blue < 0 || blue >= 256) throw new IllegalArgumentException("blue must be between 0 and 255"); - if (alpha < 0 || alpha >= 256) throw new IllegalArgumentException("alpha must be between 0 and 255"); - - setStrokeColor(new Color(red, green, blue, alpha)); - } - - public void setStrokeColor(Color pColor) { - strokeColor = pColor; - zeichnung.setColor(pColor); - } - - public void setStrokeWeight( double pWeight ) { - strokeWeight = pWeight; - zeichnung.setStroke(createStroke()); - } - - protected Stroke createStroke() { - switch(konturArt) { - case GEPUNKTET: - return new BasicStroke( - (float) strokeWeight, - BasicStroke.CAP_ROUND, - BasicStroke.JOIN_ROUND, - 10.0f, new float[]{1.0f, 5.0f}, 0.0f); - case GESTRICHELT: - return new BasicStroke( - (float) strokeWeight, - BasicStroke.CAP_ROUND, - BasicStroke.JOIN_ROUND, - 10.0f, new float[]{5.0f}, 0.0f); - default: - return new BasicStroke( - (float) strokeWeight, - BasicStroke.CAP_ROUND, - BasicStroke.JOIN_ROUND); - } - } - - public int getKonturArt() { - return konturArt; - } - - public void setKonturArt(int konturArt) { - this.konturArt = konturArt; - } - - private Stack transformStack = new Stack<>(); - - public Zeichenebene() { - super(); - transformStack.push(new AffineTransform()); - - strokeColor = Color.BLACK; - fillColor = Color.WHITE; - strokeWeight = 1.0; - } - - public Zeichenebene( int pWidth, int pHeight) { - super(pWidth, pHeight); - } - - public void setAnchor( int pAnchor ) { - default_anchor = pAnchor; - } - - public void leeren() { - clear(200); - } - - public void clear(int gray) { - clear(gray, gray, gray, 255); - } - - public void clear(int gray, int alpha) { - clear(gray, gray, gray, alpha); - } - - public void clear(int red, int green, int blue) { - clear(red, green, blue, 255); - } - - public void clear(int red, int green, int blue, int alpha) { - clear(new Color(red, green, blue, alpha)); - } - - public void clear(Color pColor) { - /*graphics.setBackground(pColor); - graphics.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());*/ - Color currentColor = zeichnung.getColor(); - pushMatrix(); - resetMatrix(); - zeichnung.setColor(pColor); - zeichnung.fillRect(0, 0, puffer.getWidth(), puffer.getHeight()); - zeichnung.setColor(currentColor); - popMatrix(); - } - - public void line(double x1, double y1, double x2, double y2) { - Shape line = new Line2D.Double(x1, y1, x2, y2); - //line = transformToCanvas(line); - - drawShape(line); - } - - public void pixel(double x, double y) { - square(x, y, 1); - } - - public void square(double x, double y, double w) { - rect(x, y, w, w); - } - - public void square(double x, double y, double w, int anchor) { - rect(x, y, w, w, anchor); - } - - public void rect(double x, double y, double w, double h) { - rect(x, y, w, h, default_anchor); - } - - public void rect(double x, double y, double w, double h, int anchor) { - Point2D.Double anchorPoint = getAnchorPoint(x, y, w, h, anchor); - Shape rect = new Rectangle2D.Double( - anchorPoint.getX(), anchorPoint.getY(), w, h - ); - //rect = transformToCanvas(rect); - - fillShape(rect); - drawShape(rect); - } - - public void point(double x, double y) { - circle(x - 1, y - 1, 2); - } - - public void circle(double x, double y, double d) { - ellipse(x, y, d, d, default_anchor); - } - - public void circle(double x, double y, double d, int anchor) { - ellipse(x, y, d, d, anchor); - } - - public void ellipse(double x, double y, double w, double h) { - ellipse(x, y, w, h, default_anchor); - } - - public void ellipse(double x, double y, double w, double h, int anchor) { - Point2D.Double anchorPoint = getAnchorPoint(x, y, w, h, anchor); - Shape ellipse = new Ellipse2D.Double( - anchorPoint.x, anchorPoint.y, w, h - ); - //ellipse = transformToCanvas(ellipse); - - fillShape(ellipse); - drawShape(ellipse); - } - - public void arc( double x, double y, double d, double angle1, double angle2 ) { - while (angle2 < angle1) angle2 += 360.0; - - Point2D.Double anchorPoint = getAnchorPoint(x, y, d, d, ZENTRUM); - Shape arc = new Arc2D.Double( - anchorPoint.x, - anchorPoint.y, - d, d, - angle1, angle2 - angle1, - Arc2D.OPEN - ); - //arc = transformToCanvas(arc); - - drawShape(arc); - } - - public void pie( double x, double y, double d, double angle1, double angle2 ) { - while (angle2 < angle1) angle2 += 360.0; - - Point2D.Double anchorPoint = getAnchorPoint(x, y, d, d, ZENTRUM); - Shape arc = new Arc2D.Double( - anchorPoint.x, - anchorPoint.y, - d, d, - angle1, angle2 - angle1, - Arc2D.PIE - ); - //arc = transformToCanvas(arc); - - fillShape(arc); - drawShape(arc); - } - - private Point2D.Double transformToCanvas(double x, double y) { - return transformToCanvas(new Point2D.Double(x, y)); - } - - private Point2D.Double transformToCanvas( Point2D.Double pPoint ) { - AffineTransform matrix = getMatrix(); - matrix.transform(pPoint, pPoint); - return pPoint; - } - - private Shape transformToCanvas( Shape pShape ) { - AffineTransform matrix = getMatrix(); - return matrix.createTransformedShape(pShape); - } - - private Point2D.Double transformToUser(double x, double y) { - return transformToUser(new Point2D.Double(x, y)); - } - - private Point2D.Double transformToUser( Point2D.Double pPoint ) { - AffineTransform matrix = getMatrix(); - - try { - matrix.inverseTransform(pPoint, pPoint); - } catch (NoninvertibleTransformException e) { - e.printStackTrace(); - } - - return pPoint; - } - - private Shape transformToUser( Shape pShape ) { - AffineTransform matrix = getMatrix(); - try { - matrix = matrix.createInverse(); - pShape = matrix.createTransformedShape(pShape); - } catch (NoninvertibleTransformException e) { - e.printStackTrace(); - } - return pShape; - } - - private AffineTransform getAnchorTransform( Shape pShape, int anchor ) { - AffineTransform at = new AffineTransform(); - Rectangle2D bounds = pShape.getBounds2D(); - switch(anchor) { - case ZENTRUM: - at.translate( - bounds.getWidth() / -2.0, - bounds.getHeight() / -2.0 - ); - break; - - case WESTEN: - at.translate( - 0, bounds.getHeight() / -2.0 - ); - break; - - case SUEDWESTEN: - at.translate( - 0, -1.0 * bounds.getHeight() - ); - break; - } - - return at; - } - - private Point2D.Double getAnchorPoint(double x, double y, double w, double h, int anchor) { - switch(anchor) { - case ZENTRUM: - x -= w / 2.0; - y -= h / 2.0; - break; - - case WESTEN: - y -= h / 2.0; - break; - - case SUEDWESTEN: - y -= h; - break; - } - - return new Point2D.Double(x, y); - } - - private Point2D.Double getAnchorPoint(Shape pShape, int anchor) { - Rectangle2D bounds = pShape.getBounds2D(); - return getAnchorPoint( - bounds.getX(), bounds.getY(), - bounds.getWidth(), bounds.getHeight(), anchor - ); - } - - private void fillShape( Shape pShape ) { - if (fillColor != null && fillColor.getAlpha() > 0.0) { - zeichnung.setColor(fillColor); - zeichnung.fill(pShape); - } - } - - private void drawShape( Shape pShape ) { - if (strokeColor != null && strokeColor.getAlpha() > 0.0 - && strokeWeight > 0.0 ) { - zeichnung.setColor(strokeColor); - zeichnung.draw(pShape); - } - } - - public void translate( double dx, double dy ) { - zeichnung.translate(dx, dy); - } - - public void scale( double factor ) { - zeichnung.scale(factor, factor); - } - - public void rotate( double pAngle ) { - zeichnung.rotate(Math.toRadians(pAngle)); - } - - public void shear( double dx, double dy ) { - zeichnung.shear(dx, dy); - } - - public AffineTransform getMatrix() { - return zeichnung.getTransform(); - } - - public void pushMatrix() { - transformStack.push(zeichnung.getTransform()); - } - - public void popMatrix() { - if( transformStack.isEmpty() ) { - resetMatrix(); - } else { - zeichnung.setTransform(transformStack.pop()); - } - } - - public void resetMatrix() { - zeichnung.setTransform(new AffineTransform()); - } - -} diff --git a/src/schule/ngb/zm/Zeichenleinwand.java b/src/schule/ngb/zm/Zeichenleinwand.java new file mode 100644 index 0000000..b77809f --- /dev/null +++ b/src/schule/ngb/zm/Zeichenleinwand.java @@ -0,0 +1,195 @@ +package schule.ngb.zm; + +import schule.ngb.zm.formen.ShapesLayer; + +import java.awt.*; +import java.awt.image.BufferStrategy; +import java.util.ArrayList; +import java.util.LinkedList; + +/** + * Eine Leinwand ist die Hauptkomponente einer Zeichenmaschine. Sie besteht aus + * mehreren Ebenen, auf denen auf verschiedene Arten gezeichnet werden kann. Die + * Ebenen lassen sich beliebig übereinander aNORTHen, ausblenden oder wieder + * löschen. + * + * Jede Ebene besitzt eine Zeichenfläche, auf der ihre Zeichnung liegt. Diese + * zeichenflächen werden pro Frame einmal von "DOWN" nach "UP" auf diese + * Leinwand gezeichnet. + */ +public class Zeichenleinwand extends Canvas { + + private LinkedList layers; + + public Zeichenleinwand( int width, int height ) { + super.setSize(width, height); + this.setPreferredSize(this.getSize()); + this.setMinimumSize(this.getSize()); + this.setBackground(Constants.STD_BACKGROUND.getColor()); + + // Liste der Ebenen initialisieren und die Standardebenen einfügen + layers = new LinkedList<>(); + layers.add(new ColorLayer(Constants.STD_BACKGROUND)); + layers.add(new DrawingLayer()); + layers.add(new ShapesLayer()); + } + + /** + * Ändert die Größe der Zeichenleinwand auf die angegebene Größe in Pixeln. + * Eine Größenänderung hat auch eine Größenänderung aller Ebenen zur Folge. + * + * @param width Neue Width der Leinwand in Pixeln. + * @param height Neue Höhe der Leinwand in Pixeln. + */ + @Override + public void setSize( int width, int height ) { + super.setSize(width, height); + this.setPreferredSize(this.getSize()); + this.setMinimumSize(this.getSize()); + + for( Layer layer : layers ) { + layer.setSize(width, height); + } + } + + /** + * Fügt der Zeichenleinwand eine Ebene hinzu, die oberhalb aller bisherigen + * Ebenen eingefügt wird. + * @param layer Die neue Ebene. + */ + public void addLayer( Layer layer ) { + if( layer != null ) { + layer.setSize(getWidth(), getHeight()); + layers.add(layer); + } + } + + /** + * Fügt der Zeichenleinwand eine Ebene an einer bestimmten Stelle hinzu. + * @param i Index der Ebene, beginnend mit 0. + * @param layer Die neue Ebene. + */ + public void addLayer( int i, Layer layer ) { + if( layer != null ) { + layer.setSize(getWidth(), getHeight()); + layers.add(i, layer); + } + } + + /** + * Gibt die Liste der bisher hinzugefügten Ebenen zurück. + * @return Liste der Ebenen. + */ + public java.util.List getLayers() { + return layers; + } + + /** + * Holt die Ebene am Index i (beginnend bei 0). + * + * @param i Index der Ebene (beginnend bei 0). + * @return Die Ebene am Index i oder null. + * @throws IndexOutOfBoundsException Falls der Index nicht existiert. + */ + public Layer getLayer( int i ) { + if( layers.size() > i ) { + return layers.get(i); + } else { + throw new IndexOutOfBoundsException("No layer at index " + i + " (max: " + (layers.size() - 1) + ")."); + } + } + + /** + * Sucht die erste Ebene des angegebenen Typs aus der Liste der Ebenen. + * Existiert keine solche Ebene, wird null zurückgegeben. + * + * @param clazz Typ der Ebene. + * @param + * @return Erste Ebene vom angegeben Typ. + */ + public L getLayer( Class clazz ) { + for( Layer layer : layers ) { + if( layer.getClass().equals(clazz) ) { + return clazz.cast(layer); + } + } + return null; + } + + /** + * Sucht alle Ebenen von einem bestimmten Typ aus der Liste der Ebenen und + * gibt diese als Liste zurück. Die Reihenfolge in der Liste entspricht der + * Reihenfolge der Ebenen in der Leinwand (von unten nach oben). + * + * @param pClazz + * @param + * @return + */ + public java.util.List getLayers( Class pClazz ) { + ArrayList result = new ArrayList<>(layers.size()); + for( Layer layer : layers ) { + if( layer.getClass().equals(pClazz) ) { + result.add(pClazz.cast(layer)); + } + } + return result; + } + + public void allocateBuffer() { + this.createBufferStrategy(2); + } + + public void dispose() { + for( Layer layer : layers ) { + layer.dispose(); + } + } + + @Override + public void paint( Graphics g ) { + render(); + } + + public void render() { + if( getBufferStrategy() == null ) { + allocateBuffer(); + } + + if( isDisplayable() ) { + BufferStrategy strategy = this.getBufferStrategy(); + if( strategy != null ) { + do { + do { + Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics(); + g2d.clearRect(0, 0, getWidth(), getHeight()); + + for( Layer layer : layers ) { + layer.draw(g2d); + } + + g2d.dispose(); + } while( strategy.contentsRestored() ); + + // Display the buffer + if (!strategy.contentsLost()) { + strategy.show(); + + Toolkit.getDefaultToolkit().sync(); + } + + // Repeat the rendering if the drawing buffer was lost + } while( strategy.contentsLost() ); + } + + /* + Graphics2D g2d = (Graphics2D) g.create(); + + for( Layer layer : layers ) { + layer.draw(g2d); + } + + g2d.dispose(); + */ + } + } +} diff --git a/src/schule/ngb/zm/Zeichenmaschine.java b/src/schule/ngb/zm/Zeichenmaschine.java new file mode 100644 index 0000000..e202435 --- /dev/null +++ b/src/schule/ngb/zm/Zeichenmaschine.java @@ -0,0 +1,471 @@ +package schule.ngb.zm; + +import schule.ngb.zm.formen.ShapesLayer; + +import javax.swing.*; +import javax.swing.event.MouseInputListener; +import java.awt.*; +import java.awt.event.*; + +/** + * Hauptklasse der Zeichenmaschine. + * + * Projekte der Zeichenmaschine sollten als Unterklasse implementiert werden. + * Die Klasse übernimmt die Initialisierung eines Programmfensters und der + * nötigen Komponenten. + */ +public class Zeichenmaschine extends Constants implements MouseInputListener, KeyListener { + + public static boolean IN_BLUEJ; + + static { + IN_BLUEJ = System.getProperty("java.class.path").contains("bluej"); + } + + /* + * Attributes to be accessed by subclasses. + */ + protected Zeichenleinwand canvas; + + protected DrawingLayer drawing; + + protected ShapesLayer shapes; + + protected int tick = 0; + + protected long runtime = 0L; + + protected double delta = 0.0; + + protected double mouseX = 0.0, mouseY = 0.0, pmouseX = 0.0, pmouseY = 0.0; + + protected int width = STD_WIDTH, height = STD_HEIGHT; + + private Object mouseLock = new Object(); + + private Object keyboardLock = new Object(); + + /* + * Internal attributes for controlling the sketchmachine + * Interne Attribute zur Steuerung der Zeichenamschine. + */ + // + private JFrame frame; + + private boolean running = false, isDrawing = false, isUpdating = false; + + private int framesPerSecond; + + private Thread mainThread; + + private boolean quitAfterTeardown = false, initialized = false; + + public Zeichenmaschine() { + this(APP_NAME + " " + APP_VERSION); + } + + public Zeichenmaschine( String title ) { + this(STD_WIDTH, STD_HEIGHT, title); + } + + public Zeichenmaschine( int width, int height, String title ) { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch( Exception e ) { + System.err.println("Error setting the look and feel."); + } + + GraphicsEnvironment environment = + GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice displayDevice = environment.getDefaultScreenDevice(); + + frame = new JFrame(displayDevice.getDefaultConfiguration()); + frame.setTitle(title); + frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + + this.width = width; + this.height = height; + + canvas = new Zeichenleinwand(width, height); + frame.add(canvas); + + framesPerSecond = STD_FPS; + + drawing = getDrawingLayer(); + shapes = getShapesLayer(); + + settings(); + + canvas.addMouseListener(this); + canvas.addMouseMotionListener(this); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing( WindowEvent e ) { + if( running ) { + running = false; + quitAfterTeardown = true; + } else { + quit(); + } + } + }); + + frame.pack(); + frame.setResizable(false); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + + canvas.allocateBuffer(); + + running = true; + mainThread = new Zeichenthread(); + mainThread.start(); + + //frame.requestFocusInWindow(); + canvas.requestFocus(); + + initialized = true; + } + + + public final void createFrame( String title ) { + + } + + public void show() { + if( !frame.isVisible() ) { + frame.setVisible(true); + } + } + + public void hide() { + if( frame.isVisible() ) { + frame.setVisible(false); + } + } + + public void quit() { + quit(!IN_BLUEJ); + } + + public void quit( boolean exit ) { + frame.setVisible(false); + canvas.dispose(); + frame.dispose(); + + if( exit ) { + System.exit(0); + } + } + + public final void setSize( int width, int height ) { + //frame.setSize(width, height); + + if( canvas != null ) { + canvas.setSize(width, height); + } + this.width = width; + this.height = height; + frame.pack(); + } + + public final int getWidth() { + return width; + } + + public final int getHeight() { + return height; + } + + public final void setTitle( String pTitel ) { + frame.setTitle(pTitel); + } + + public final Zeichenleinwand getCanvas() { + return canvas; + } + + public final void addLayer( Layer layer ) { + canvas.addLayer(layer); + } + + public final DrawingLayer getDrawingLayer() { + DrawingLayer layer = canvas.getLayer(DrawingLayer.class); + if( layer == null ) { + layer = new DrawingLayer(getWidth(), getHeight()); + canvas.addLayer(0, layer); + } + return layer; + } + + public final ShapesLayer getShapesLayer() { + ShapesLayer layer = canvas.getLayer(ShapesLayer.class); + if( layer == null ) { + layer = new ShapesLayer(getWidth(), getHeight()); + canvas.addLayer(layer); + } + return layer; + } + + public final int getFramesPerSecond() { + return framesPerSecond; + } + + public final void setFramesPerSecond( int pFramesPerSecond ) { + framesPerSecond = pFramesPerSecond; + } + + /* + * Methoden, die von Unterklassen überschrieben werden können / sollen. + */ + public void settings() { + + } + + public void setup() { + + } + + public void draw() { + + } + + public void teardown() { + + } + + public void update( double delta ) { + running = false; + } + + public void delay( int ms ) { + if( ms <= 0 ) { + return; + } + + long timer = 0L; + if( isDrawing ) { + // Immediately show the current drawing before waiting + // Measure the render time and subtract from the waiting ms + timer = System.nanoTime(); + canvas.render(); + timer = System.nanoTime() - timer; + } + + try { + int sub = (int) Math.ceil(timer / 1000000.0); + + if( sub >= ms ) { + return; + } + + Thread.sleep(ms - sub, (int) (timer % 1000000L)); + } catch( InterruptedException ex ) { + // Nothing + } + } + + /* + * Mouse handling + */ + + @Override + public void mouseClicked( MouseEvent e ) { + saveMousePosition(e.getPoint()); + mouseClicked(); + } + + public void mouseClicked() { + } + + @Override + public void mousePressed( MouseEvent e ) { + saveMousePosition(e.getPoint()); + mousePressed(); + } + + public void mousePressed() { + } + + @Override + public void mouseReleased( MouseEvent e ) { + saveMousePosition(e.getPoint()); + mouseReleased(); + } + + public void mouseReleased() { + } + + @Override + public void mouseEntered( MouseEvent e ) { + saveMousePosition(e.getPoint()); + } + + @Override + public void mouseExited( MouseEvent e ) { + saveMousePosition(e.getPoint()); + } + + @Override + public void mouseDragged( MouseEvent e ) { + saveMousePosition(e.getPoint()); + mouseDragged(); + } + + public void mouseDragged() { + + } + + @Override + public void mouseMoved( MouseEvent e ) { + saveMousePosition(e.getPoint()); + mouseMoved(); + } + + public void mouseMoved() { + + } + + private void saveMousePosition( Point pLocation ) { + //pmouseX = mouseX; + //pmouseY = mouseY; + /*synchronized(mouseLock) { + mouseX = pLocation.getX()-this.getRootPane().getX(); + mouseY = pLocation.getY()-this.getRootPane().getY(); + }*/ + } + + private void saveMousePosition() { + pmouseX = mouseX; + pmouseY = mouseY; + + java.awt.Point mouseLoc = MouseInfo.getPointerInfo().getLocation(); + java.awt.Point compLoc = canvas.getLocationOnScreen(); + mouseX = mouseLoc.x - compLoc.x; + mouseY = mouseLoc.y - compLoc.y; + } + + @Override + public void keyTyped( KeyEvent e ) { + keyTyped(); + } + + /* + * Keyboard handling + */ + + public void keyTyped() { + + } + + @Override + public void keyPressed( KeyEvent e ) { + keyPressed(); + } + + public void keyPressed() { + + } + + @Override + public void keyReleased( KeyEvent e ) { + keyReleased(); + } + + public void keyReleased() { + + } + + class Zeichenthread extends Thread { + + @Override + public final void run() { + // Wait for full initialization before start + while( !initialized ) { + delay(1); + } + + // start of thread in ms + final long start = System.currentTimeMillis(); + // current time in ns + long beforeTime = System.nanoTime(); + // store for deltas + long overslept = 0L; + // internal counters for tick and runtime + int _tick = 0; + long _runtime = 0; + // public counters for access by subclasses + tick = 0; + runtime = 0; + + // call setup of subclass + setup(); + + while( running ) { + // delta in seconds + delta = (System.nanoTime() - beforeTime) / 1000000000.0; + beforeTime = System.nanoTime(); + + saveMousePosition(); + + handleUpdate(delta); + handleDraw(); + + if( canvas != null ) { + canvas.render(); + //canvas.invalidate(); + //frame.repaint(); + } + + // delta time in ns + long afterTime = System.nanoTime(); + long dt = afterTime - beforeTime; + long sleep = ((1000000000L / framesPerSecond) - dt) - overslept; + + + if( sleep > 0 ) { + try { + Thread.sleep(sleep / 1000000L, (int) (sleep % 1000000L)); + } catch( InterruptedException e ) { + // Interrupt not relevant + } + + overslept = (System.nanoTime() - afterTime) - sleep; + } else { + overslept = 0L; + + } + + _tick += 1; + _runtime = System.currentTimeMillis() - start; + tick = _tick; + runtime = _runtime; + } + + teardown(); + if( quitAfterTeardown ) { + quit(); + } + } + + public void handleUpdate( double delta ) { + if( isUpdating ) { + return; + } + isUpdating = true; + update(delta); + isUpdating = false; + } + + public void handleDraw() { + if( isDrawing ) { + return; + } + isDrawing = true; + draw(); + isDrawing = false; + } + + } + +} diff --git a/src/schule/ngb/zm/formen/AbgerundetesRechteck.java b/src/schule/ngb/zm/formen/AbgerundetesRechteck.java deleted file mode 100644 index 49e9bff..0000000 --- a/src/schule/ngb/zm/formen/AbgerundetesRechteck.java +++ /dev/null @@ -1,59 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.Rectangle2D; -import java.awt.geom.RoundRectangle2D; - -public class AbgerundetesRechteck extends Rechteck { - - protected double rundung = 1.0; - - public AbgerundetesRechteck( double pX, double pY, double pBreite, double pHoehe, double pRundung ) { - super(pX, pY, pBreite, pHoehe); - rundung = pRundung; - } - - public AbgerundetesRechteck( Rechteck pRechteck ) { - super( - pRechteck.x, pRechteck.y, - pRechteck.breite, pRechteck.hoehe); - kopiere(pRechteck); - } - - @Override - public void kopiere( Form pForm ) { - super.kopiere(pForm); - if( pForm instanceof AbgerundetesRechteck ) { - AbgerundetesRechteck rechteck = (AbgerundetesRechteck) pForm; - rundung = rechteck.rundung; - } - } - - @Override - public Shape getShape() { - return new RoundRectangle2D.Double( - 0, 0, breite, hoehe, rundung, rundung - ); - } - - @Override - public boolean equals( Object o ) { - if( this == o ) return true; - if( o == null || getClass() != o.getClass() ) return false; - AbgerundetesRechteck rechteck = (AbgerundetesRechteck) o; - return super.equals(o) && - Double.compare(rechteck.rundung, rundung) == 0; - } - - @Override - public String toString() { - return getClass().getCanonicalName() + "[" + - "x=" + x + - ",y=" + y + - ",breite=" + breite + - ",hoehe=" + hoehe + - ",rundung=" + rundung + - ']'; - } - -} diff --git a/src/schule/ngb/zm/formen/Arc.java b/src/schule/ngb/zm/formen/Arc.java new file mode 100644 index 0000000..3752c1c --- /dev/null +++ b/src/schule/ngb/zm/formen/Arc.java @@ -0,0 +1,111 @@ +package schule.ngb.zm.formen; + +import schule.ngb.zm.Options; + +import java.awt.geom.Arc2D; + +public class Arc extends Shape { + + protected double width; + + protected double height; + + protected double angle; + + protected double startingangle; + + protected Options.PathType type = OPEN; + + public Arc( double x, double y, double radius, double angle ) { + this(x, y, radius, radius, angle, 0.0); + } + + public Arc( double x, double y, double radius, double angle, double startingangle ) { + this(x, y, radius, radius, angle, startingangle); + } + + public Arc( double x, double y, double width, double height, double angle, double startingangle ) { + super(x, y); + this.width = width; + this.height = height; + this.angle = angle; + this.startingangle = startingangle; + setAnchor(CENTER); + + noFill(); + } + + public Arc( Arc arc ) { + this(arc.x, arc.y, arc.width, arc.height, arc.angle, arc.startingangle); + copyFrom(arc); + } + + public double getWidth() { + return width; + } + + public void setWidth( double width ) { + this.width = width; + } + + public double getHeight() { + return height; + } + + public void setHeight( double height ) { + this.height = height; + } + + public double getAngle() { + return angle; + } + + public void setAngle( double angle ) { + this.angle = angle; + } + + public double getStartingangle() { + return startingangle; + } + + public void setStartingangle( double startingangle ) { + this.startingangle = startingangle; + } + + public Options.PathType getType() { + return type; + } + + public void setType( Options.PathType type ) { + this.type = type; + } + + @Override + public Shape copy() { + return new Arc(this); + } + + @Override + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + if( shape instanceof Arc ) { + Arc arc = (Arc) shape; + width = arc.width; + height = arc.height; + angle = arc.angle; + startingangle = arc.startingangle; + type = arc.type; + } + } + + @Override + public void setAnchor( Options.Direction anchor ) { + calculateAnchor(width, height, anchor); + } + + @Override + public java.awt.Shape getShape() { + return new Arc2D.Double(0, 0, width, height, startingangle, angle, type.awt_type); + } + +} diff --git a/src/schule/ngb/zm/formen/Arrow.java b/src/schule/ngb/zm/formen/Arrow.java new file mode 100644 index 0000000..556a4c0 --- /dev/null +++ b/src/schule/ngb/zm/formen/Arrow.java @@ -0,0 +1,178 @@ +package schule.ngb.zm.formen; + +import schule.ngb.zm.Options; +import schule.ngb.zm.Vector; + +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.geom.Path2D; + +public class Arrow extends Line { + + // Spitzenarten + public static final int OPEN = 101; + + public static final int CLOSED = 102; + + private static final double BASE_HEADSIZE = 5.0; + + protected Options.ArrowHead arrowhead = LINES; + + protected double headsize = 1.0; + + public Arrow( double x1, double y1, double x2, double y2 ) { + super(x1, y1, x2, y2); + } + + /** + * Erstellt einen Pfeil, der den übergebenen Vektor darstellt. + * + * @param vector + */ + public Arrow( Vector vector ) { + this(0, 0, vector.x, vector.y); + } + + /** + * Erstellt einen Pfeil, der den Differenzvektor zwischen den übergebenen + * Vektoren darstellt. + * + * @param vector1 + * @param vector2 + */ + public Arrow( Vector vector1, Vector vector2 ) { + this(vector1.x, vector1.y, vector2.x, vector2.y); + } + + /** + * Erstellt einen Pfeil, der den übergebenen Vektor um die angegeben + * Koordinaten verschoben darstellt. + * + * @param x + * @param y + * @param pVektor + */ + public Arrow( double x, double y, Vector pVektor ) { + this(x, y, x + pVektor.x, y + pVektor.y); + } + + /** + * Erstellt einen Pfeil als Kopie einer vorgegebenen Linie. + * + * @param pLine + */ + public Arrow( Line pLine ) { + this(pLine.x, pLine.y, pLine.x2, pLine.y2); + this.copyFrom(pLine); + } + + public double getHeadsize() { + return headsize; + } + + public void setHeadsize( double headsize ) { + this.headsize = headsize; + } + + public Options.ArrowHead getArrowhead() { + return arrowhead; + } + + public void setArrowhead( Options.ArrowHead arrowhead ) { + this.arrowhead = arrowhead; + } + + /** + * Kopiert die Werte des angegebenen Vektors. + * + * @param vector + */ + public void copyFrom( Vector vector ) { + if( vector != null ) { + x2 = x + vector.x; + y2 = y + vector.y; + } + } + + @Override + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + if( shape instanceof Arrow ) { + Arrow pArrow = (Arrow) shape; + headsize = pArrow.headsize; + arrowhead = pArrow.arrowhead; + } + } + + @Override + public Arrow copy() { + return new Arrow(this); + } + + @Override + public java.awt.Shape getShape() { + /*Path2D.Double gruppe = new Path2D.Double(); + gruppe.append(super.getShape(), false); + gruppe.append(getPfeilspitze(), false); + + return gruppe;*/ + return super.getShape(); + } + + protected java.awt.Shape getHeadShape() { + AffineTransform af; + switch( arrowhead ) { + case LINES: + double len = BASE_HEADSIZE * headsize; + Path2D.Double sOPEN = new Path2D.Double(); + sOPEN.moveTo(-len, -len); + sOPEN.lineTo(0, 0); + sOPEN.lineTo(-len, len); + + af = new AffineTransform(); + af.translate(x2-x, y2-y); + af.rotate(Math.atan2(y2 - y, x2 - x)); + return af.createTransformedShape(sOPEN); + case FILLED: + default: + int ix = (int) x2, iy = (int) y2, pg = (int) (BASE_HEADSIZE * headsize); + java.awt.Polygon sCLOSED = new java.awt.Polygon( + new int[]{0, -pg, -pg}, + new int[]{0, -pg, pg}, + 3 + ); + + af = new AffineTransform(); + af.translate(x2-x, y2-y); + af.rotate(Math.atan2(y2 - y, x2 - x)); + return af.createTransformedShape(sCLOSED); + } + } + + @Override + public void draw( Graphics2D graphics, AffineTransform at ) { + if( !visible ) { + return; + } + + super.draw(graphics, at); + + java.awt.Shape head = getHeadShape(); + if( at != null ) { + head = at.createTransformedShape(head); + } + + Color currentColor = graphics.getColor(); + if( strokeColor != null && strokeColor.getAlpha() > 0.0 ) { + graphics.setColor(strokeColor.getColor()); + graphics.setStroke(new BasicStroke((float) strokeWeight)); + if( arrowhead == FILLED ) { + graphics.fill(head); + } else { + graphics.draw(head); + } + } + graphics.setColor(currentColor); + } + +} diff --git a/src/schule/ngb/zm/formen/Bild.java b/src/schule/ngb/zm/formen/Bild.java deleted file mode 100644 index d9365f7..0000000 --- a/src/schule/ngb/zm/formen/Bild.java +++ /dev/null @@ -1,147 +0,0 @@ -package schule.ngb.zm.formen; - -import javax.imageio.ImageIO; -import java.awt.*; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.net.URL; - -public class Bild extends Form { - - private double breite; - - private double hoehe; - - private BufferedImage bild; - - public Bild( String pQuelle ) { - this(0, 0, pQuelle); - } - - public Bild( double pX, double pY, String pQuelle ) { - super(pX, pY); - - if( pQuelle == null || pQuelle.length() == 0 ) - throw new IllegalArgumentException("Bildquelle darf nicht null oder leer sein."); - - try { - // Bilddatei aus dem Arbeitsverzeichnis laden - File file = new File(pQuelle); - if( file.isFile() ) { - bild = ImageIO.read(file); - } else { - // relativ zur .class-Datei - URL url = getClass().getResource(pQuelle); - - // relativ zum ClassLoader - if( url == null ) { - url = getClass().getClassLoader().getResource(pQuelle); - } - - // aWebadresse oder JAR-Datei - if( url == null ) { - url = new URL(pQuelle); - } - - bild = ImageIO.read(url); - } - - if( bild == null ) { - throw new IllegalArgumentException("Bild konnte nicht aus " + pQuelle + " geladen werden!"); - } - - breite = bild.getWidth(null); - hoehe = bild.getHeight(null); - setAnkerpunkt(ZENTRUM); - } catch( IOException ioe ) { - throw new IllegalArgumentException("Bild konnte nicht aus " + pQuelle + " geladen werden!", ioe); - } - } - - public Bild( Bild pBild ) { - super(pBild.getX(), pBild.getY()); - kopiere(pBild); - } - - @Override - public Form kopie() { - return new Bild(this); - } - - @Override - public void kopiere( Form pForm ) { - super.kopiere(pForm); - if( pForm instanceof Bild ) { - Bild pBild = (Bild)pForm; - bild = new BufferedImage(pBild.bild.getWidth(), pBild.bild.getHeight(), pBild.bild.getType()); - Graphics2D g = bild.createGraphics(); - g.drawImage(pBild.bild, 0, 0, null); - g.dispose(); - - breite = bild.getWidth(null); - hoehe = bild.getHeight(null); - setAnkerpunkt(pForm.getAnkerpunkt()); - } - } - - public double getBreite() { - return breite; - } - - public void setBreite( double pBreite ) { - skalieren(pBreite / breite); - } - - public double getHoehe() { - return hoehe; - } - - public void setHoehe( double pHoehe ) { - skalieren(pHoehe / hoehe); - } - - @Override - public void setAnkerpunkt( byte pAnker ) { - ankerBerechnen(breite, hoehe, pAnker); - } - - @Override - public void skalieren( double pFaktor ) { - super.skalieren(pFaktor); - breite *= pFaktor; - hoehe *= pFaktor; - } - - @Override - public Shape getShape() { - return new Rectangle2D.Double(0, 0, breite, hoehe); - } - - /* - @Override - public AffineTransform getVerzerrung() { - AffineTransform verzerrung = new AffineTransform(); - verzerrung.translate(x, y); - verzerrung.scale(skalierung, skalierung); - verzerrung.rotate(Math.toRadians(drehwinkel)); - verzerrung.translate(-anker.x, -anker.y); - return verzerrung; - } - */ - - @Override - public void zeichnen( Graphics2D graphics, AffineTransform pVerzerrung ) { - if( !sichtbar ) { - return; - } - - AffineTransform current = graphics.getTransform(); - graphics.transform(getVerzerrung()); - graphics.drawImage(bild, 0, 0, (int) breite, (int) hoehe, null); - graphics.setTransform(current); - } - -} diff --git a/src/schule/ngb/zm/formen/Bogen.java b/src/schule/ngb/zm/formen/Bogen.java deleted file mode 100644 index c4ef3f9..0000000 --- a/src/schule/ngb/zm/formen/Bogen.java +++ /dev/null @@ -1,110 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.Arc2D; - -public class Bogen extends Form { - - protected double breite; - - protected double hoehe; - - protected double winkel; - - protected double startwinkel; - - protected int typ = OFFEN; - - public Bogen( double pX, double pY, double pRadius, double pWinkel ) { - this(pX, pY, pRadius, pRadius, pWinkel, 0.0); - } - - public Bogen( double pX, double pY, double pRadius, double pWinkel, double pStartwinkel ) { - this(pX, pY, pRadius, pRadius, pWinkel, pStartwinkel); - } - - public Bogen( double pX, double pY, double pBreite, double pHoehe, double pWinkel, double pStartwinkel ) { - super(pX, pY); - breite = pBreite; - hoehe = pHoehe; - winkel = pWinkel; - startwinkel = pStartwinkel; - setAnkerpunkt(ZENTRUM); - - keineFuellung(); - } - - public Bogen( Bogen pBogen ) { - this(pBogen.x, pBogen.y, pBogen.breite, pBogen.hoehe, pBogen.winkel, pBogen.startwinkel); - kopiere(pBogen); - } - - public double getBreite() { - return breite; - } - - public void setBreite( double pBreite ) { - this.breite = pBreite; - } - - public double getHoehe() { - return hoehe; - } - - public void setHoehe( double pHoehe ) { - this.hoehe = pHoehe; - } - - public double getWinkel() { - return winkel; - } - - public void setWinkel( double pWinkel ) { - this.winkel = pWinkel; - } - - public double getStartwinkel() { - return startwinkel; - } - - public void setStartwinkel( double pStartwinkel ) { - this.startwinkel = pStartwinkel; - } - - public int getTyp() { - return typ; - } - - public void setTyp( int pTyp ) { - this.typ = pTyp; - } - - @Override - public Form kopie() { - return new Bogen(this); - } - - @Override - public void kopiere(Form pForm) { - super.kopiere(pForm); - if( pForm instanceof Bogen ) { - Bogen b = (Bogen) pForm; - breite = b.breite; - hoehe = b.hoehe; - winkel = b.winkel; - startwinkel = b.startwinkel; - typ = b.typ; - } - } - - @Override - public void setAnkerpunkt( byte pAnker ) { - ankerBerechnen(breite, hoehe, pAnker); - } - - @Override - public Shape getShape() { - return new Arc2D.Double(0, 0, breite, hoehe, startwinkel, winkel, typ); - } - -} diff --git a/src/schule/ngb/zm/formen/Circle.java b/src/schule/ngb/zm/formen/Circle.java new file mode 100644 index 0000000..8c614b0 --- /dev/null +++ b/src/schule/ngb/zm/formen/Circle.java @@ -0,0 +1,76 @@ +package schule.ngb.zm.formen; + +import schule.ngb.zm.Options; + +import java.awt.geom.Ellipse2D; + +public class Circle extends Shape { + + protected double radius; + + public Circle( double x, double y, double radius ) { + super(x, y); + this.radius = radius; + setAnchor(CENTER); + } + + public Circle( Circle circle ) { + this(circle.x, circle.y, circle.radius); + copyFrom(circle); + } + + public double getRadius() { + return radius; + } + + public void setRadius( double radius ) { + this.radius = radius; + } + + @Override + public void scale( double factor ) { + super.scale(factor); + radius *= factor; + } + + @Override + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + if( shape instanceof Circle ) { + radius = ((Circle) shape).radius; + } + } + + @Override + public Circle copy() { + return new Circle(this); + } + + @Override + public java.awt.Shape getShape() { + return new Ellipse2D.Double(0, 0, radius + radius, radius + radius); + } + + @Override + public void setAnchor( Options.Direction anchor ) { + calculateAnchor(radius + radius, radius + radius, anchor); + } + + @Override + public boolean equals( Object o ) { + if( this == o ) return true; + if( o == null || getClass() != o.getClass() ) return false; + Circle pCircle = (Circle) o; + return super.equals(o) && Double.compare(pCircle.radius, radius) == 0; + } + + @Override + public String toString() { + return getClass().getCanonicalName() + "[" + + "x=" + x + + ",y=" + y + + ",radius=" + radius + + ']'; + } + +} diff --git a/src/schule/ngb/zm/formen/Curve.java b/src/schule/ngb/zm/formen/Curve.java new file mode 100644 index 0000000..1adfb34 --- /dev/null +++ b/src/schule/ngb/zm/formen/Curve.java @@ -0,0 +1,182 @@ +package schule.ngb.zm.formen; + +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Point2D; +import java.awt.geom.QuadCurve2D; +import java.util.Arrays; + +public class Curve extends Shape { + + protected double[] coordinates; + + public Curve( double x, double y, double cx, double cy, double x2, double y2 ) { + super(x, y); + + coordinates = new double[]{ + cx, cy, x2, y2 + }; + + noFill(); + } + + public Curve( double x, double y, double cx1, double cy1, double cx2, double cy2, double x2, double y2 ) { + super(x, y); + + coordinates = new double[]{ + cx1, cy1, cx2, cy2, x2, y2 + }; + + noFill(); + } + + public Curve( Curve curve ) { + super(curve.x, curve.y); + coordinates = Arrays.copyOf(curve.coordinates, curve.coordinates.length); + } + + public Point2D getStart() { + return new Point2D.Double(x, y); + } + + public void setStart( double x, double y ) { + this.x = x; + this.y = y; + } + + public Point2D getEnd() { + return new Point2D.Double( + coordinates[coordinates.length - 2], + coordinates[coordinates.length - 1] + ); + } + + public void setEnd( double x, double y ) { + coordinates[coordinates.length - 2] = x; + coordinates[coordinates.length - 1] = y; + } + + public Point2D getControl1() { + return new Point2D.Double( + coordinates[0], + coordinates[1] + ); + } + + public void setControl1( double x, double y ) { + coordinates[0] = x; + coordinates[1] = y; + } + + public Point2D getControl2() { + return new Point2D.Double( + coordinates[coordinates.length - 4], + coordinates[coordinates.length - 3] + ); + } + + public void setControl2( double x, double y ) { + coordinates[coordinates.length - 4] = x; + coordinates[coordinates.length - 3] = y; + } + + public void setPoints( double x, double y, double cx, double cy, double x2, double y2 ) { + setStart(x, y); + coordinates = coordinates = new double[]{ + cx, cy, x2, y2 + }; + } + + public void setPoints( double x, double y, double cx1, double cy1, double cx2, double cy2, double x2, double y2 ) { + setStart(x, y); + coordinates = new double[]{ + cx1, cy1, cx2, cy2, x2, y2 + }; + } + + public boolean isCubic() { + return coordinates.length == 6; + } + + public boolean isQuad() { + return coordinates.length == 4; + } + + @Override + public Shape copy() { + return new Curve(this); + } + + @Override + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + if( shape instanceof Curve ) { + Curve k = (Curve) shape; + coordinates = Arrays.copyOf(k.coordinates, k.coordinates.length); + } + } + + @Override + public java.awt.Shape getShape() { + if( isCubic() ) { + return new CubicCurve2D.Double( + 0, 0, + coordinates[0] - x, coordinates[1] - y, + coordinates[2] - x, coordinates[3] - y, + coordinates[4] - x, coordinates[5] - y + ); + } else { + return new QuadCurve2D.Double( + 0, 0, + coordinates[0] - x, coordinates[1] - y, + coordinates[2] - x, coordinates[3] - y + ); + } + } + + @Override + public void scale( double factor ) { + super.scale(factor); + coordinates[coordinates.length - 4] *= factor; + coordinates[coordinates.length - 3] *= factor; + coordinates[coordinates.length - 2] *= factor; + coordinates[coordinates.length - 1] *= factor; + } + + @Override + public void move( double dx, double dy ) { + super.move(dx, dy); + for( int i = 0; i < coordinates.length; i += 2 ) { + coordinates[i] = coordinates[i] + dx; + coordinates[i + 1] = coordinates[i + 1] + dy; + } + } + + @Override + public void moveTo( double x, double y ) { + double dx = x - x, dy = y - y; + move(dx, dy); + } + + @Override + public boolean equals( Object o ) { + if( this == o ) return true; + if( o == null || getClass() != o.getClass() ) return false; + Curve pCurve = (Curve) o; + return super.equals(o) && + getStart().equals(pCurve.getStart()) && + getControl1().equals(pCurve.getControl1()) && + getControl2().equals(pCurve.getControl2()) && + getEnd().equals(pCurve.getEnd()); + } + + @Override + public String toString() { + return getClass().getCanonicalName() + "[" + + "x=" + x + + ",y=" + y + + "x2=" + coordinates[coordinates.length - 2] + + ",y2=" + coordinates[coordinates.length - 1] + + ']'; + } + +} diff --git a/src/schule/ngb/zm/formen/CustomShape.java b/src/schule/ngb/zm/formen/CustomShape.java new file mode 100644 index 0000000..229978c --- /dev/null +++ b/src/schule/ngb/zm/formen/CustomShape.java @@ -0,0 +1,53 @@ +package schule.ngb.zm.formen; + +import java.awt.geom.Path2D; + +public class CustomShape extends Shape { + + protected Path2D.Double path; + + public CustomShape( double x, double y ) { + super(x, y); + path = new Path2D.Double(); + } + + public CustomShape( CustomShape custom ) { + super(custom.x, custom.y); + path = custom.path; + } + + public void lineTo( double x, double y ) { + path.lineTo(x - x, y - y); + } + + public void arcTo( double x1, double y1, double x2, double y2 ) { + path.quadTo(x1 - x, y1 - y, x2 - x, y2 - y); + } + + public void curveTo( double x1, double y1, double x2, double y2, double x3, double y3 ) { + path.curveTo(x1 - x, y1 - y, x2 - x, y2 - y, x3 - x, y3 - y); + } + + public void close() { + path.lineTo(0, 0); + } + + @Override + public Shape copy() { + return new CustomShape(this); + } + + @Override + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + if( shape instanceof CustomShape ) { + path = new Path2D.Double(path); + } + } + + @Override + public java.awt.Shape getShape() { + return new Path2D.Double(path); + } + +} diff --git a/src/schule/ngb/zm/formen/Drache.java b/src/schule/ngb/zm/formen/Drache.java deleted file mode 100644 index 69415da..0000000 --- a/src/schule/ngb/zm/formen/Drache.java +++ /dev/null @@ -1,117 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.Path2D; - -public class Drache extends Form { - - private double breite; - - private double hoehe; - - private double verhaeltnis; - - public Drache( double pX, double pY, double pBreite, double pHoehe ) { - this(pX, pY, pBreite, pHoehe, 0.5); - } - - public Drache( double pX, double pY, double pBreite, double pHoehe, double pVerhaeltnis ) { - super(pX, pY); - breite = pBreite; - hoehe = pHoehe; - verhaeltnis = pVerhaeltnis; - setAnkerpunkt(ZENTRUM); - } - - public Drache( Drache pDrache ) { - this(pDrache.x, pDrache.y, pDrache.breite, pDrache.hoehe, pDrache.verhaeltnis); - kopiere(pDrache); - } - - public double getBreite() { - return breite; - } - - public void setBreite( double breite ) { - this.breite = breite; - } - - public double getHoehe() { - return hoehe; - } - - public void setHoehe( double hoehe ) { - this.hoehe = hoehe; - } - - public double getVerhaeltnis() { - return verhaeltnis; - } - - public void setVerhaeltnis( double pVerhaeltnis ) { - this.verhaeltnis = pVerhaeltnis; - } - - @Override - public void kopiere( Form pForm ) { - super.kopiere(pForm); - if( pForm instanceof Drache ) { - Drache d = (Drache) pForm; - breite = d.breite; - hoehe = d.hoehe; - verhaeltnis = d.verhaeltnis; - } - } - - @Override - public Drache kopie() { - return new Drache(this); - } - - @Override - public void skalieren( double pFaktor ) { - super.skalieren(pFaktor); - breite *= pFaktor; - hoehe *= pFaktor; - } - - @Override - public void setAnkerpunkt( byte pAnker ) { - ankerBerechnen(breite, hoehe, pAnker); - } - - @Override - public Shape getShape() { - double bHalb = breite * .5, hVerh = verhaeltnis*hoehe; - Path2D shape = new Path2D.Double(); - shape.moveTo(bHalb, 0); - shape.lineTo(breite, hVerh); - shape.lineTo(bHalb, hoehe); - shape.lineTo(0, hVerh); - shape.closePath(); - return shape; - } - - @Override - public boolean equals( Object o ) { - if( this == o ) return true; - if( o == null || getClass() != o.getClass() ) return false; - Drache d = (Drache) o; - return super.equals(o) && - Double.compare(d.breite, breite) == 0 && - Double.compare(d.hoehe, hoehe) == 0 && - Double.compare(d.verhaeltnis, verhaeltnis) == 0; - } - - @Override - public String toString() { - return getClass().getCanonicalName() + '[' + - "breite=" + breite + - ",hoehe=" + hoehe + - ",verhaeltnis=" + verhaeltnis + - ",x=" + x + - ",y=" + y + - ']'; - } - -} diff --git a/src/schule/ngb/zm/formen/Dreieck.java b/src/schule/ngb/zm/formen/Dreieck.java deleted file mode 100644 index e119003..0000000 --- a/src/schule/ngb/zm/formen/Dreieck.java +++ /dev/null @@ -1,32 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.Point2D; -import java.util.Arrays; - -public class Dreieck extends Vieleck { - - public Dreieck( double pX, double pY, Point2D... pEcken ) { - super(pX, pY, Arrays.copyOf(pEcken, 3)); - if( pEcken.length < 3 ) { - throw new IllegalArgumentException("Ein Dreieck muss genau drei Eckpunkte besitzen."); - } - } - - public Dreieck( Point2D... pEcken ) { - super(Arrays.copyOf(pEcken, 3)); - if( pEcken.length < 3 ) { - throw new IllegalArgumentException("Ein Dreieck muss genau drei Eckpunkte besitzen."); - } - } - - public Dreieck( Dreieck pDreieck ) { - super(pDreieck.x, pDreieck.y); - kopiere(pDreieck); - } - - @Override - public Form kopie() { - return new Dreieck(this); - } -} diff --git a/src/schule/ngb/zm/formen/Ellipse.java b/src/schule/ngb/zm/formen/Ellipse.java index e0dd1e2..ae3a93e 100644 --- a/src/schule/ngb/zm/formen/Ellipse.java +++ b/src/schule/ngb/zm/formen/Ellipse.java @@ -1,73 +1,73 @@ package schule.ngb.zm.formen; -import java.awt.*; +import schule.ngb.zm.Options; + import java.awt.geom.Ellipse2D; -public class Ellipse extends Form { +public class Ellipse extends Shape { - private double breite; + protected double width; - private double hoehe; + protected double height; - public Ellipse( double pX, double pY, double pBreite, double pHoehe ) { - super(pX, pY); - breite = pBreite; - hoehe = pHoehe; - setAnkerpunkt(ZENTRUM); + public Ellipse( double x, double y, double width, double height ) { + super(x, y); + this.width = width; + this.height = height; + setAnchor(CENTER); } - public Ellipse( Ellipse pEllipse ) { - this(pEllipse.x, pEllipse.y, pEllipse.breite, pEllipse.hoehe); - kopiere(pEllipse); + public Ellipse( Ellipse ellipse ) { + this(ellipse.x, ellipse.y, ellipse.width, ellipse.height); + copyFrom(ellipse); } - public double getBreite() { - return breite; + public double getWidth() { + return width; } - public void setBreite( double breite ) { - this.breite = breite; + public void setWidth( double width ) { + this.width = width; } - public double getHoehe() { - return hoehe; + public double getHeight() { + return height; } - public void setHoehe( double hoehe ) { - this.hoehe = hoehe; + public void setHeight( double height ) { + this.height = height; } @Override - public void kopiere( Form pForm ) { - super.kopiere(pForm); - if( pForm instanceof Ellipse ) { - Ellipse e = (Ellipse) pForm; - breite = e.breite; - hoehe = e.hoehe; + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + if( shape instanceof Ellipse ) { + Ellipse e = (Ellipse) shape; + this.width = e.width; + this.height = e.height; } } @Override - public Ellipse kopie() { + public Ellipse copy() { return new Ellipse(this); } @Override - public void skalieren( double pFaktor ) { - super.skalieren(pFaktor); - breite *= pFaktor; - hoehe *= pFaktor; + public void scale( double factor ) { + super.scale(factor); + width *= factor; + height *= factor; } @Override - public void setAnkerpunkt( byte pAnker ) { - ankerBerechnen(breite, hoehe, pAnker); + public void setAnchor( Options.Direction anchor ) { + calculateAnchor(width, height, anchor); } @Override - public Shape getShape() { - Shape shape = new Ellipse2D.Double(0, 0, breite, hoehe); - return shape; + public java.awt.Shape getShape() { + return new Ellipse2D.Double(0, 0, width, height); } @Override @@ -76,15 +76,15 @@ public class Ellipse extends Form { if( o == null || getClass() != o.getClass() ) return false; Ellipse ellipse = (Ellipse) o; return super.equals(o) && - Double.compare(ellipse.breite, breite) == 0 && - Double.compare(ellipse.hoehe, hoehe) == 0; + Double.compare(ellipse.width, width) == 0 && + Double.compare(ellipse.height, height) == 0; } @Override public String toString() { return getClass().getCanonicalName() + '[' + - "breite=" + breite + - ",hoehe=" + hoehe + + "width=" + width + + ",height=" + height + ",x=" + x + ",y=" + y + ']'; diff --git a/src/schule/ngb/zm/formen/FilledShape.java b/src/schule/ngb/zm/formen/FilledShape.java new file mode 100644 index 0000000..36df1a5 --- /dev/null +++ b/src/schule/ngb/zm/formen/FilledShape.java @@ -0,0 +1,41 @@ +package schule.ngb.zm.formen; + +import schule.ngb.zm.Color; + +public abstract class FilledShape extends StrokedShape { + + protected Color fillColor = STD_FILLCOLOR; + + public Color getFillColor() { + return fillColor; + } + + public void setFillColor( Color color ) { + fillColor = color; + } + + public void setFillColor( int gray ) { + setFillColor(gray, gray, gray, 255); + } + + public void noFill() { + fillColor = null; + } + + public void setFillColor( int gray, int alpha ) { + setFillColor(gray, gray, gray, alpha); + } + + public void setFillColor( int red, int green, int blue ) { + setFillColor(red, green, blue, 255); + } + + public void setFillColor( int red, int green, int blue, int alpha ) { + setFillColor(new Color(red, green, blue, alpha)); + } + + public void resetFill() { + setFillColor(STD_FILLCOLOR); + } + +} diff --git a/src/schule/ngb/zm/formen/Form.java b/src/schule/ngb/zm/formen/Form.java deleted file mode 100644 index 31284e8..0000000 --- a/src/schule/ngb/zm/formen/Form.java +++ /dev/null @@ -1,225 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; - -public abstract class Form extends Fuellform { - - - protected double x; - - protected double y; - - protected double drehwinkel = 0.0; - - protected double skalierung = 1.0; - - protected boolean sichtbar = true; - - protected Point2D.Double anker = new Point2D.Double(); - - FormGruppe gruppe = null; - - public Form( double pX, double pY ) { - x = pX; - y = pY; - } - - public Form() { - this(0.0, 0.0); - } - - public double getX() { - return x; - } - - public void setX( double x ) { - this.x = x; - } - - public double getY() { - return y; - } - - public void setY( double y ) { - this.y = y; - } - - public double getDrehwinkel() { - return drehwinkel; - } - - public double getSkalierung() { - return skalierung; - } - - public boolean istSichtbar() { - return sichtbar; - } - - public void verstecken() { - sichtbar = false; - } - - public void zeigen() { - sichtbar = true; - } - - public void umschalten() { - sichtbar = !sichtbar; - } - - public Point2D.Double getAnkerpunkt() { - return new Point2D.Double(anker.x, anker.y); - } - - public void setAnkerpunkt( byte pAnker ) { - Shape shape = getShape(); - if( shape != null ) { - Rectangle2D bounds = shape.getBounds2D(); - ankerBerechnen(bounds.getWidth(), bounds.getHeight(), pAnker); - } else { - anker.x = 0; - anker.y = 0; - } - } - - public void setAnkerpunkt( Point2D.Double pAnker ) { - anker.x = pAnker.x; - anker.y = pAnker.y; - } - - protected void ankerBerechnen( double pBreite, double pHoehe, byte pAnker ) { - double bHalb = pBreite * .5, hHalb = pHoehe * .5; - // pAnker == CENTER - anker.x = bHalb; - anker.y = hHalb; - if( (pAnker & NORDEN) == NORDEN ) { - anker.y -= hHalb; - } - if( (pAnker & SUEDEN) == SUEDEN ) { - anker.y += hHalb; - } - if( (pAnker & WESTEN) == WESTEN ) { - anker.x -= bHalb; - } - if( (pAnker & OSTEN) == OSTEN ) { - anker.x += bHalb; - } - } - - public void kopiere( Form pForm ) { - verschiebeNach(pForm); - setFuellfarbe(pForm.getFuellfarbe()); - setKonturFarbe(pForm.getKonturFarbe()); - setKonturDicke(pForm.getKonturDicke()); - setKonturArt(pForm.getKonturArt()); - sichtbar = pForm.istSichtbar(); - drehwinkel = pForm.drehwinkel; - skalieren(pForm.skalierung); - setAnkerpunkt(pForm.getAnkerpunkt()); - } - - public abstract Form kopie(); - - public Rechteck getBegrenzung() { - return new Rechteck(this); - } - - public void verschieben( double dx, double dy ) { - x += dx; - y += dy; - } - - public void verschiebeNach( double pX, double pY ) { - x = pX; - y = pY; - } - - public void verschiebeNach( Form pForm ) { - verschiebeNach(pForm.x, pForm.y); - } - - public void skalieren( double pFaktor ) { - skalierung = pFaktor; - anker.x *= pFaktor; - anker.y *= pFaktor; - } - - public void skalierenUm( double pFaktor ) { - skalieren(skalierung * pFaktor); - } - - public void drehen( double pWinkel ) { - drehwinkel += pWinkel % 360; - } - - public void drehenNach( double pWinkel ) { - drehwinkel = pWinkel % 360; - } - - /*public void scheren( double dx, double dy ) { - verzerrung.shear(dx, dy); - }*/ - - public AffineTransform getVerzerrung() { - AffineTransform verzerrung = new AffineTransform(); - verzerrung.translate(x, y); - verzerrung.rotate(Math.toRadians(drehwinkel)); - //verzerrung.scale(skalierung, skalierung); - verzerrung.translate(-anker.x, -anker.y); - return verzerrung; - } - - public final void zeichnen( Graphics2D graphics ) { - zeichnen(graphics, getVerzerrung()); - } - - /** - * Zeichnet die Form, aber wendet zuvor noch eine zusätzliche Transformations- - * matrix an. Wird u.A. von der {@link FormGruppe} verwendet. - * @param graphics - * @param pVerzerrung - */ - public void zeichnen( Graphics2D graphics, AffineTransform pVerzerrung ) { - if( !sichtbar ) { - return; - } - - Shape shape = getShape(); - if( shape != null ) { - if( pVerzerrung != null ) { - shape = pVerzerrung.createTransformedShape(shape); - } - - Color currentColor = graphics.getColor(); - if( fuellFarbe != null && fuellFarbe.getAlpha() > 0 ) { - graphics.setColor(fuellFarbe); - graphics.fill(shape); - } - if( konturFarbe != null && konturFarbe.getAlpha() > 0 - && konturDicke > 0.0 ) { - graphics.setColor(konturFarbe); - graphics.setStroke(createStroke()); - graphics.draw(shape); - } - graphics.setColor(currentColor); - } - } - - public abstract Shape getShape(); - - @Override - public boolean equals( Object o ) { - if( this == o ) return true; - if( o == null || getClass() != o.getClass() ) return false; - Form form = (Form) o; - return Double.compare(form.x, x) == 0 && - Double.compare(form.y, y) == 0 && - Double.compare(form.drehwinkel, drehwinkel) == 0 && - Double.compare(form.skalierung, skalierung) == 0; - } - -} diff --git a/src/schule/ngb/zm/formen/FormGruppe.java b/src/schule/ngb/zm/formen/FormGruppe.java deleted file mode 100644 index 437dc45..0000000 --- a/src/schule/ngb/zm/formen/FormGruppe.java +++ /dev/null @@ -1,103 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.AffineTransform; -import java.awt.geom.Path2D; -import java.util.ArrayList; -import java.util.List; - -public class FormGruppe extends Form { - - private List

formen; - - public FormGruppe() { - super(); - formen = new ArrayList<>(10); - } - - public FormGruppe( double pX, double pY ) { - super(pX, pY); - formen = new ArrayList<>(10); - } - - public FormGruppe( double pX, double pY, Form... pFormen ) { - super(pX, pY); - formen = new ArrayList<>(pFormen.length); - for( Form form : pFormen ) { - formen.add(form); - form.gruppe = this; - } - setAnkerpunkt(ZENTRUM); - } - - public Form kopie() { - return null; - } - - public void hinzu( Form... pFormen ) { - for( Form form : pFormen ) { - hinzu(form, false); - } - } - - public void hinzu( Form pForm ) { - hinzu(pForm, false); - } - - public void hinzu( Form pForm, boolean pKoordinatenAnpassen ) { - if( pKoordinatenAnpassen ) { - pForm.x = pForm.x - x; - pForm.y = pForm.y - y; - } - formen.add(pForm); - pForm.gruppe = this; - } - - @Override - public void setAnkerpunkt( byte pAnker ) { - double minx = Double.MAX_VALUE, miny = Double.MAX_VALUE, - maxx = Double.MIN_VALUE, maxy = Double.MIN_VALUE; - for( Form form : formen ) { - Rechteck bounds = form.getBegrenzung(); - if( bounds.x < minx ) - minx = bounds.x; - if( bounds.y < miny ) - miny = bounds.y; - if( bounds.x+bounds.breite > maxx ) - maxx = bounds.x+bounds.breite; - if( bounds.y+bounds.hoehe > maxy ) - maxy = bounds.y+bounds.hoehe; - } - - ankerBerechnen(maxx-minx, maxy-miny, pAnker); - } - - @Override - public Shape getShape() { - Path2D.Double gruppe = new Path2D.Double(); - for( Form form : formen ) { - gruppe.append(form.getShape(), false); - } - return gruppe; - } - - @Override - public void zeichnen( Graphics2D graphics, AffineTransform pVerzerrung ) { - if( !sichtbar ) { - return; - } - - AffineTransform verzerrung = new AffineTransform(); - verzerrung.translate(x, y); - verzerrung.rotate(Math.toRadians(drehwinkel)); - //verzerrung.scale(skalierung, skalierung); - verzerrung.translate(-anker.x, -anker.y); - - for( Form f: formen ) { - AffineTransform af = f.getVerzerrung(); - af.preConcatenate(verzerrung); - f.zeichnen(graphics, af); - } - } - -} diff --git a/src/schule/ngb/zm/formen/Formenebene.java b/src/schule/ngb/zm/formen/Formenebene.java deleted file mode 100644 index 4285e75..0000000 --- a/src/schule/ngb/zm/formen/Formenebene.java +++ /dev/null @@ -1,70 +0,0 @@ -package schule.ngb.zm.formen; - -import schule.ngb.zm.Ebene; - -import java.awt.*; -import java.util.ArrayList; -import java.util.LinkedList; - -public class Formenebene extends Ebene { - - private LinkedList formen; - - public Formenebene() { - super(); - formen = new LinkedList(); - } - - public Formenebene( int pWidth, int pHeight ) { - super(pWidth, pHeight); - formen = new LinkedList(); - } - - public void anzeigen( Form... pFormen ) { - synchronized( formen ) { - for( Form f: pFormen ) { - formen.add(f); - } - } - } - - public void alleVerstecken() { - synchronized( formen ) { - for( Form form : formen ) { - form.verstecken(); - } - } - } - - public void alleZeigen() { - synchronized( formen ) { - for( Form form : formen ) { - form.zeigen(); - } - } - } - - public void leeren() { - Color currentColor = zeichnung.getBackground(); - zeichnung.setBackground(new Color(0, 0, 0, 0)); - zeichnung.clearRect(0, 0, puffer.getWidth(), puffer.getHeight()); - zeichnung.setBackground(currentColor); - } - - public java.util.List getShapes() { - return formen; - } - - @Override - public void zeichnen( Graphics2D pGraphics ) { - synchronized( formen ) { - for( Form form : formen ) { - if( form.istSichtbar() ) { - form.zeichnen(zeichnung); - } - } - } - - super.zeichnen(pGraphics); - } -} diff --git a/src/schule/ngb/zm/formen/Freiform.java b/src/schule/ngb/zm/formen/Freiform.java deleted file mode 100644 index e79f7c0..0000000 --- a/src/schule/ngb/zm/formen/Freiform.java +++ /dev/null @@ -1,40 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.Path2D; - -public class Freiform extends Form { - - private Path2D.Double pfad; - - public Freiform(double pX, double pY) { - super(pX, pY); - pfad = new Path2D.Double(); - } - - public void linieNach( double pX, double pY ) { - pfad.lineTo(pX-x, pY-y); - } - - public void bogenNach( double pX1, double pY1, double pX2, double pY2 ) { - pfad.quadTo(pX1-x, pY1-y, pX2-x, pY2-y); - } - - public void kurveNach( double pX1, double pY1, double pX2, double pY2, double pX3, double pY3 ) { - pfad.curveTo(pX1-x, pY1-y, pX2-x, pY2-y, pX3-x, pY3-y); - } - - public void schliessen() { - pfad.lineTo(0,0); - } - - @Override - public Form kopie() { - return null; - } - - @Override - public Shape getShape() { - return new Path2D.Double(pfad); - } -} diff --git a/src/schule/ngb/zm/formen/Fuellform.java b/src/schule/ngb/zm/formen/Fuellform.java deleted file mode 100644 index 7e40cb2..0000000 --- a/src/schule/ngb/zm/formen/Fuellform.java +++ /dev/null @@ -1,49 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; - -public abstract class Fuellform extends Konturform { - - public static final Color STD_FUELLFARBE = Color.WHITE; - - protected Color fuellFarbe = STD_FUELLFARBE; - - - public Color getFuellfarbe() { - return fuellFarbe; - } - - public void setFuellfarbe( Color pFuellFarbe) { - fuellFarbe = pFuellFarbe; - } - - public void keineFuellung() { - fuellFarbe = null; - } - - public void setFuellfarbe( int gray) { - setFuellfarbe(gray, gray, gray, 255); - } - - public void setFuellfarbe( int gray, int alpha) { - setFuellfarbe(gray, gray, gray, alpha); - } - - public void setFuellfarbe( int red, int green, int blue) { - setFuellfarbe(red, green, blue, 255); - } - - public void setFuellfarbe( int red, int green, int blue, int alpha) { - if (red < 0 || red >= 256) - throw new IllegalArgumentException("red must be between 0 and 255"); - if (green < 0 || green >= 256) - throw new IllegalArgumentException("green must be between 0 and 255"); - if (blue < 0 || blue >= 256) - throw new IllegalArgumentException("blue must be between 0 and 255"); - if (alpha < 0 || alpha >= 256) - throw new IllegalArgumentException("alpha must be between 0 and 255"); - - setFuellfarbe(new Color(red, green, blue, alpha)); - } - -} diff --git a/src/schule/ngb/zm/formen/Kite.java b/src/schule/ngb/zm/formen/Kite.java new file mode 100644 index 0000000..91fac07 --- /dev/null +++ b/src/schule/ngb/zm/formen/Kite.java @@ -0,0 +1,118 @@ +package schule.ngb.zm.formen; + +import schule.ngb.zm.Options; + +import java.awt.geom.Path2D; + +public class Kite extends Shape { + + protected double width; + + protected double height; + + protected double ratio; + + public Kite( double x, double y, double width, double height ) { + this(x, y, width, height, 0.5); + } + + public Kite( double x, double y, double width, double height, double ratio ) { + super(x, y); + this.width = width; + this.height = height; + this.ratio = ratio; + setAnchor(CENTER); + } + + public Kite( Kite pKite ) { + this(pKite.x, pKite.y, pKite.width, pKite.height, pKite.ratio); + copyFrom(pKite); + } + + public double getWidth() { + return width; + } + + public void setWidth( double width ) { + this.width = width; + } + + public double getHeight() { + return height; + } + + public void setHeight( double height ) { + this.height = height; + } + + public double getRatio() { + return ratio; + } + + public void setRatio( double ratio ) { + this.ratio = ratio; + } + + @Override + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + if( shape instanceof Kite ) { + Kite d = (Kite) shape; + width = d.width; + height = d.height; + ratio = d.ratio; + } + } + + @Override + public Kite copy() { + return new Kite(this); + } + + @Override + public void scale( double factor ) { + super.scale(factor); + width *= factor; + height *= factor; + } + + @Override + public void setAnchor( Options.Direction anchor ) { + calculateAnchor(width, height, anchor); + } + + @Override + public java.awt.Shape getShape() { + double hHalf = width * .5, hRatio = ratio * height; + Path2D shape = new Path2D.Double(); + shape.moveTo(hHalf, 0); + shape.lineTo(width, hRatio); + shape.lineTo(hHalf, height); + shape.lineTo(0, hRatio); + shape.closePath(); + return shape; + } + + @Override + public boolean equals( Object o ) { + if( this == o ) return true; + if( o == null || getClass() != o.getClass() ) return false; + Kite d = (Kite) o; + return super.equals(o) && + Double.compare(d.width, width) == 0 && + Double.compare(d.height, height) == 0 && + Double.compare(d.ratio, ratio) == 0; + } + + @Override + public String toString() { + return getClass().getCanonicalName() + '[' + + "width=" + width + + ",height=" + height + + ",verhaeltnis=" + ratio + + ",x=" + x + + ",y=" + y + + ']'; + } + +} diff --git a/src/schule/ngb/zm/formen/Konturform.java b/src/schule/ngb/zm/formen/Konturform.java deleted file mode 100644 index 4ecb960..0000000 --- a/src/schule/ngb/zm/formen/Konturform.java +++ /dev/null @@ -1,114 +0,0 @@ -package schule.ngb.zm.formen; - -import schule.ngb.zm.Konstanten; -import schule.ngb.zm.Zeichenbar; - -import java.awt.*; - -public abstract class Konturform extends Konstanten implements Zeichenbar { - - public static final int DURCHGEZOGEN = 16; - public static final int GESTRICHELT = 17; - public static final int GEPUNKTET = 18; - - public static final Color STD_KONTURFABRE = Color.BLACK; - public static final double STD_KONTURDICKE = 1.0; - - - protected Color konturFarbe = STD_KONTURFABRE; - - protected double konturDicke = STD_KONTURDICKE; - - protected int konturArt = DURCHGEZOGEN; - - - public Color getKonturFarbe() { - return konturFarbe; - } - - public void setKonturFarbe(Color pKonturFarbe) { - this.konturFarbe = pKonturFarbe; - } - - public void keineKontur() { - konturFarbe = null; - } - - public void setKonturFarbe(int gray) { - setKonturFarbe(gray, gray, gray, 255); - } - - public void setKonturFarbe(int gray, int alpha) { - setKonturFarbe(gray, gray, gray, alpha); - } - - public void setKonturFarbe(int red, int green, int blue) { - setKonturFarbe(red, green, blue, 255); - } - - public void setKonturFarbe(int red, int green, int blue, int alpha) { - if (red < 0 || red >= 256) - throw new IllegalArgumentException("red must be between 0 and 255"); - if (green < 0 || green >= 256) - throw new IllegalArgumentException("green must be between 0 and 255"); - if (blue < 0 || blue >= 256) - throw new IllegalArgumentException("blue must be between 0 and 255"); - if (alpha < 0 || alpha >= 256) - throw new IllegalArgumentException("alpha must be between 0 and 255"); - - setKonturFarbe(new Color(red, green, blue, alpha)); - } - - public double getKonturDicke() { - return konturDicke; - } - - public void setKonturDicke(double pKonturDicke) { - this.konturDicke = pKonturDicke; - } - - public int getKonturArt() { - return konturArt; - } - - public void setKonturArt(int konturArt) { - switch (konturArt) { - case GESTRICHELT: - this.konturArt = GESTRICHELT; - break; - case GEPUNKTET: - this.konturArt = GEPUNKTET; - break; - default: - this.konturArt = DURCHGEZOGEN; - break; - } - - } - - public abstract void zeichnen(Graphics2D graphics); - - protected Stroke createStroke() { - switch(konturArt) { - case GEPUNKTET: - return new BasicStroke( - (float) konturDicke, - BasicStroke.CAP_ROUND, - BasicStroke.JOIN_ROUND, - 10.0f, new float[]{1.0f,5.0f}, 0.0f); - case GESTRICHELT: - return new BasicStroke( - (float) konturDicke, - BasicStroke.CAP_ROUND, - BasicStroke.JOIN_ROUND, - 10.0f, new float[]{5.0f}, 0.0f); - case DURCHGEZOGEN: - default: - return new BasicStroke( - (float) konturDicke, - BasicStroke.CAP_ROUND, - BasicStroke.JOIN_ROUND); - } - } - -} diff --git a/src/schule/ngb/zm/formen/Kreis.java b/src/schule/ngb/zm/formen/Kreis.java deleted file mode 100644 index b25e5ab..0000000 --- a/src/schule/ngb/zm/formen/Kreis.java +++ /dev/null @@ -1,67 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.Ellipse2D; - -public class Kreis extends Form { - - public double radius; - - public Kreis( double pX, double pY, double pRadius ) { - super(pX, pY); - radius = pRadius; - setAnkerpunkt(ZENTRUM); - } - - public Kreis( Kreis pKreis ) { - this(pKreis.x, pKreis.y, pKreis.radius); - kopiere(pKreis); - } - - @Override - public void skalieren( double pFaktor ) { - super.skalieren(pFaktor); - radius *= pFaktor; - } - - @Override - public void kopiere( Form pForm ) { - super.kopiere(pForm); - if( pForm instanceof Kreis ) { - radius = ((Kreis) pForm).radius; - } - } - - @Override - public Kreis kopie() { - return new Kreis(this); - } - - @Override - public Shape getShape() { - return new Ellipse2D.Double(0, 0, radius * 2.0, radius * 2.0); - } - - @Override - public void setAnkerpunkt( byte pAnker ) { - ankerBerechnen(radius + radius, radius + radius, pAnker); - } - - @Override - public boolean equals( Object o ) { - if( this == o ) return true; - if( o == null || getClass() != o.getClass() ) return false; - Kreis kreis = (Kreis) o; - return super.equals(o) && Double.compare(kreis.radius, radius) == 0; - } - - @Override - public String toString() { - return getClass().getCanonicalName() + "[" + - "x=" + x + - ",y=" + y + - ",radius=" + radius + - ']'; - } - -} diff --git a/src/schule/ngb/zm/formen/Kurve.java b/src/schule/ngb/zm/formen/Kurve.java deleted file mode 100644 index 76b543c..0000000 --- a/src/schule/ngb/zm/formen/Kurve.java +++ /dev/null @@ -1,183 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.CubicCurve2D; -import java.awt.geom.Point2D; -import java.awt.geom.QuadCurve2D; -import java.util.Arrays; - -public class Kurve extends Form { - - private double[] koordinaten; - - public Kurve( double pX, double pY, double pCx, double pCy, double pX2, double pY2 ) { - super(pX, pY); - - koordinaten = new double[]{ - pCx, pCy, pX2, pY2 - }; - - keineFuellung(); - } - - public Kurve( double pX, double pY, double pCx1, double pCy1, double pCx2, double pCy2, double pX2, double pY2 ) { - super(pX, pY); - - koordinaten = new double[]{ - pCx1, pCy1, pCx2, pCy2, pX2, pY2 - }; - - keineFuellung(); - } - - public Kurve( Kurve pKurve ) { - super(pKurve.x, pKurve.y); - koordinaten = Arrays.copyOf(pKurve.koordinaten, pKurve.koordinaten.length); - } - - public Point2D getStartpunkt() { - return new Point2D.Double(x, y); - } - - public void setStartpunkt( double pX, double pY ) { - x = pX; - y = pY; - } - - public Point2D getEndpunkt() { - return new Point2D.Double( - koordinaten[koordinaten.length - 2], - koordinaten[koordinaten.length - 1] - ); - } - - public void setEndpunkt( double pX, double pY ) { - koordinaten[koordinaten.length - 2] = pX; - koordinaten[koordinaten.length - 1] = pY; - } - - public Point2D getKontrollpunkt1() { - return new Point2D.Double( - koordinaten[0], - koordinaten[1] - ); - } - - public void setKontrollpunkt1( double pX, double pY ) { - koordinaten[0] = pX; - koordinaten[1] = pY; - } - - public Point2D getKontrollpunkt2() { - return new Point2D.Double( - koordinaten[koordinaten.length - 4], - koordinaten[koordinaten.length - 3] - ); - } - - public void setKontrollpunkt2( double pX, double pY ) { - koordinaten[koordinaten.length - 4] = pX; - koordinaten[koordinaten.length - 3] = pY; - } - - public void setPunkte( double pX, double pY, double pCx, double pCy, double pX2, double pY2 ) { - setStartpunkt(pX, pY); - koordinaten = koordinaten = new double[]{ - pCx, pCy, pX2, pY2 - }; - } - - public void setPunkte( double pX, double pY, double pCx1, double pCy1, double pCx2, double pCy2, double pX2, double pY2 ) { - setStartpunkt(pX, pY); - koordinaten = new double[]{ - pCx1, pCy1, pCx2, pCy2, pX2, pY2 - }; - } - - public boolean istKubisch() { - return koordinaten.length == 6; - } - - public boolean istQuadratisch() { - return koordinaten.length == 4; - } - - @Override - public Form kopie() { - return new Kurve(this); - } - - @Override - public void kopiere( Form pForm ) { - super.kopiere(pForm); - if( pForm instanceof Kurve ) { - Kurve k = (Kurve) pForm; - koordinaten = Arrays.copyOf(k.koordinaten, k.koordinaten.length); - } - } - - @Override - public Shape getShape() { - if( istKubisch() ) { - return new CubicCurve2D.Double( - 0, 0, - koordinaten[0] - x, koordinaten[1] - y, - koordinaten[2] - x, koordinaten[3] - y, - koordinaten[4] - x, koordinaten[5] - y - ); - } else { - return new QuadCurve2D.Double( - 0, 0, - koordinaten[0] - x, koordinaten[1] - y, - koordinaten[2] - x, koordinaten[3] - y - ); - } - } - - @Override - public void skalieren( double pFaktor ) { - super.skalieren(pFaktor); - koordinaten[koordinaten.length - 4] *= pFaktor; - koordinaten[koordinaten.length - 3] *= pFaktor; - koordinaten[koordinaten.length - 2] *= pFaktor; - koordinaten[koordinaten.length - 1] *= pFaktor; - } - - @Override - public void verschieben( double dx, double dy ) { - super.verschieben(dx, dy); - for( int i = 0; i < koordinaten.length; i += 2 ) { - koordinaten[i] = koordinaten[i] + dx; - koordinaten[i + 1] = koordinaten[i + 1] + dy; - } - } - - @Override - public void verschiebeNach( double pX, double pY ) { - double dx = pX - x, dy = pY - y; - verschieben(dx, dy); - } - - @Override - public boolean equals( Object o ) { - if( this == o ) return true; - if( o == null || getClass() != o.getClass() ) return false; - Kurve kurve = (Kurve) o; - return super.equals(o) && - getStartpunkt().equals(kurve.getStartpunkt()) && - getKontrollpunkt1().equals(kurve.getKontrollpunkt1()) && - getKontrollpunkt2().equals(kurve.getKontrollpunkt2()) && - getEndpunkt().equals(kurve.getEndpunkt()); - } - - @Override - public String toString() { - return getClass().getCanonicalName() + "[" + - "x=" + x + - ",y=" + y + - "x2=" + koordinaten[koordinaten.length - 2] + - ",y2=" + koordinaten[koordinaten.length - 1] + - ']'; - } - -} diff --git a/src/schule/ngb/zm/formen/Line.java b/src/schule/ngb/zm/formen/Line.java new file mode 100644 index 0000000..2c2a728 --- /dev/null +++ b/src/schule/ngb/zm/formen/Line.java @@ -0,0 +1,108 @@ +package schule.ngb.zm.formen; + +import schule.ngb.zm.Options; + +import java.awt.geom.Line2D; + +public class Line extends Shape { + + protected double x2; + + protected double y2; + + public Line( double x1, double y1, double x2, double y2 ) { + super(x1, y1); + this.x2 = x2; + this.y2 = y2; + } + + public Line( Line line ) { + this(line.x, line.y, line.x2, line.y2); + copyFrom(line); + } + + public double getX2() { + return x2; + } + + public void setX2( double x ) { + this.x2 = x; + } + + public double getY2() { + return y2; + } + + public void setY2( double y ) { + this.y2 = y; + } + + @Override + public void scale( double factor ) { + super.scale(factor); + x2 *= factor; + y2 *= factor; + } + + @Override + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + if( shape instanceof Line ) { + Line pLine = (Line) shape; + x2 = pLine.x2; + y2 = pLine.y2; + } + } + + @Override + public Line copy() { + return new Line(this); + } + + @Override + public void move( double dx, double dy ) { + super.move(dx, dy); + x2 += dx; + y2 += dx; + } + + @Override + public void moveTo( double x, double y ) { + double dx = x2-this.x; + double dy = y2-this.y; + super.moveTo(x, y); + x2 += dx; + y2 += dy; + } + + @Override + public void setAnchor( Options.Direction anchor ) { + calculateAnchor(x2 - x, y2 - y, anchor); + } + + @Override + public java.awt.Shape getShape() { + return new Line2D.Double(0, 0, x2 - x, y2 - y); + } + + @Override + public boolean equals( Object o ) { + if( this == o ) return true; + if( o == null || getClass() != o.getClass() ) return false; + Line pLine = (Line) o; + return super.equals(o) && + Double.compare(pLine.x2, x2) == 0 && + Double.compare(pLine.y2, y2) == 0; + } + + @Override + public String toString() { + return getClass().getCanonicalName() + "[" + + "x1=" + x + + ",y1=" + y + + ",x2=" + x2 + + ",y2=" + y2 + + ']'; + } + +} diff --git a/src/schule/ngb/zm/formen/Linie.java b/src/schule/ngb/zm/formen/Linie.java deleted file mode 100644 index 0082f0c..0000000 --- a/src/schule/ngb/zm/formen/Linie.java +++ /dev/null @@ -1,92 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.Line2D; - -public class Linie extends Form { - - protected double x2; - - protected double y2; - - public Linie( double pX1, double pY1, double pX2, double pY2 ) { - super(pX1, pY1); - x2 = pX2; - y2 = pY2; - } - - public Linie( Linie pLinie ) { - this(pLinie.x, pLinie.y, pLinie.x2, pLinie.y2); - kopiere(pLinie); - } - - public void setX2( double pX ) { - this.x2 = x; - } - - public void setY2( double pY ) { - this.y2 = y; - } - - public double getX2() { - return x2; - } - - public double getY2() { - return y2; - } - - @Override - public void skalieren( double pFaktor ) { - super.skalieren(pFaktor); - x2 *= pFaktor; - y2 *= pFaktor; - } - - @Override - public void kopiere( Form pForm ) { - super.kopiere(pForm); - if( pForm instanceof Linie ) { - Linie linie = (Linie) pForm; - x2 = linie.x2; - y2 = linie.y2; - } - } - - @Override - public Linie kopie() { - return new Linie(this); - } - - @Override - public void setAnkerpunkt( byte pAnker ) { - ankerBerechnen(x2-x, y2-y, pAnker); - } - - @Override - public Shape getShape() { - Line2D.Double linie = new Line2D.Double(0, 0, x2 - x, y2 - y); - return linie; - } - - @Override - public boolean equals( Object o ) { - if( this == o ) return true; - if( o == null || getClass() != o.getClass() ) return false; - Linie linie = (Linie) o; - return super.equals(o) && - Double.compare(linie.x2, x2) == 0 && - Double.compare(linie.y2, y2) == 0; - } - - @Override - public String toString() { - return getClass().getCanonicalName() + "[" + - "x1=" + x + - ",y1=" + y + - ",x2=" + x2 + - ",y2=" + y2 + - ']'; - } - -} diff --git a/src/schule/ngb/zm/formen/Pfeil.java b/src/schule/ngb/zm/formen/Pfeil.java deleted file mode 100644 index 69d5e69..0000000 --- a/src/schule/ngb/zm/formen/Pfeil.java +++ /dev/null @@ -1,137 +0,0 @@ -package schule.ngb.zm.formen; - -import schule.ngb.zm.Vektor; - -import java.awt.*; -import java.awt.geom.AffineTransform; -import java.awt.geom.Path2D; - -public class Pfeil extends Linie { - - public static final int OFFEN = 101; - public static final int GESCHLOSSEN = 102; - private static final double BASIS_PFEILGROESSE = 5.0; - protected int pfeilspitze = OFFEN; - - protected double pfeilgroesse = 1.0; - - public Pfeil( double pX1, double pY1, double pX2, double pY2 ) { - super(pX1, pY1, pX2, pY2); - } - - public Pfeil( Vektor pVektor ) { - this(0, 0, pVektor.x, pVektor.y); - } - - public Pfeil( double pX, double pY, Vektor pVektor ) { - this(pX, pY, pX + pVektor.x, pY + pVektor.y); - } - - public Pfeil( Linie pLinie ) { - this(pLinie.x, pLinie.y, pLinie.x2, pLinie.y2); - kopiere(pLinie); - } - - public double getPfeilgroesse() { - return pfeilgroesse; - } - - public void setPfeilgroesse( double pPfeilgroesse ) { - pfeilgroesse = pPfeilgroesse; - } - - public int getPfeilspitze() { - return pfeilspitze; - } - - public void setPfeilspitze( int pPfeilspitze ) { - this.pfeilspitze = pPfeilspitze; - } - - public void abbilden( Vektor pVektor ) { - x2 = x + pVektor.x; - y2 = y + pVektor.y; - } - - @Override - public void kopiere( Form pForm ) { - super.kopiere(pForm); - if( pForm instanceof Pfeil ) { - Pfeil pfeil = (Pfeil) pForm; - pfeilgroesse = pfeil.pfeilgroesse; - pfeilspitze = pfeil.pfeilspitze; - } - } - - @Override - public Pfeil kopie() { - return new Pfeil(this); - } - - @Override - public Shape getShape() { - /*Path2D.Double gruppe = new Path2D.Double(); - gruppe.append(super.getShape(), false); - gruppe.append(getPfeilspitze(), false); - - return gruppe;*/ - return super.getShape(); - } - - protected Shape getSpitze() { - AffineTransform af; - switch( pfeilspitze ) { - case OFFEN: - double len = BASIS_PFEILGROESSE * pfeilgroesse; - Path2D.Double sOffen = new Path2D.Double(); - sOffen.moveTo(-len, -len); - sOffen.lineTo(0, 0); - sOffen.lineTo(-len, len); - - af = new AffineTransform(); - af.translate(x2, y2); - af.rotate(Math.atan2(y2 - y, x2 - x)); - return af.createTransformedShape(sOffen); - case GESCHLOSSEN: - default: - int ix = (int) x2, iy = (int) y2, pg = (int) (BASIS_PFEILGROESSE * pfeilgroesse); - Polygon sGeschlossen = new Polygon( - new int[]{0, -pg, -pg}, - new int[]{0, -pg, pg}, - 3 - ); - - af = new AffineTransform(); - af.translate(x2, y2); - af.rotate(Math.atan2(y2 - y, x2 - x)); - return af.createTransformedShape(sGeschlossen); - } - } - - @Override - public void zeichnen( Graphics2D graphics, AffineTransform pVerzerrung ) { - if( !sichtbar ) { - return; - } - - super.zeichnen(graphics); - - Shape spitze = getSpitze(); - if( pVerzerrung != null ) { - spitze = pVerzerrung.createTransformedShape(spitze); - } - - Color currentColor = graphics.getColor(); - if( konturFarbe != null && konturFarbe.getAlpha() > 0.0 ) { - graphics.setColor(konturFarbe); - graphics.setStroke(new BasicStroke((float) konturDicke)); - if( pfeilspitze == GESCHLOSSEN ) { - graphics.fill(spitze); - } else { - graphics.draw(spitze); - } - } - graphics.setColor(currentColor); - - } -} diff --git a/src/schule/ngb/zm/formen/Picture.java b/src/schule/ngb/zm/formen/Picture.java new file mode 100644 index 0000000..3b50d42 --- /dev/null +++ b/src/schule/ngb/zm/formen/Picture.java @@ -0,0 +1,128 @@ +package schule.ngb.zm.formen; + + +import schule.ngb.zm.Color; +import schule.ngb.zm.Options; +import schule.ngb.zm.util.ImageLoader; + +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; + +public class Picture extends Shape { + + // https://stackoverflow.com/questions/14225518/tinting-image-in-java-improvement + protected Color tint; + + private BufferedImage image; + + private double width; + + private double height; + + public Picture( String source ) { + this(0, 0, source); + } + + public Picture( double x, double y, String source ) { + super(x, y); + image = ImageLoader.loadImage(source); + + if( image == null ) { + throw new IllegalArgumentException("Could not initialize image from source " + source); + } + + width = image.getWidth(); + height = image.getHeight(); + setAnchor(CENTER); + } + + public Picture( Picture picture ) { + super(picture.getX(), picture.getY()); + copyFrom(picture); + } + + @Override + public Shape copy() { + return new Picture(this); + } + + @Override + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + if( shape instanceof Picture ) { + Picture pic = (Picture) shape; + image = new BufferedImage(pic.image.getWidth(), pic.image.getHeight(), pic.image.getType()); + Graphics2D g = image.createGraphics(); + g.drawImage(pic.image, 0, 0, null); + g.dispose(); + + width = image.getWidth(); + height = image.getHeight(); + setAnchor(shape.getAnchor()); + } + } + + public double getWidth() { + return width; + } + + public void setWidth( double width ) { + scale(width / width); + } + + public double getHeight() { + return height; + } + + public void setHeight( double height ) { + scale(height / height); + } + + public BufferedImage getImage() { + return image; + } + + @Override + public void setAnchor( Options.Direction anchor ) { + calculateAnchor(width, height, anchor); + } + + @Override + public void scale( double factor ) { + super.scale(factor); + width *= factor; + height *= factor; + } + + @Override + public java.awt.Shape getShape() { + return new Rectangle2D.Double(0, 0, width, height); + } + + /* + @Override + public AffineTransform getVerzerrung() { + AffineTransform verzerrung = new AffineTransform(); + verzerrung.translate(x, y); + verzerrung.scale(skalierung, skalierung); + verzerrung.rotate(Math.toRadians(drehwinkel)); + verzerrung.translate(-anker.x, -anker.y); + return verzerrung; + } + */ + + @Override + public void draw( Graphics2D graphics, AffineTransform pVerzerrung ) { + if( !visible ) { + return; + } + + AffineTransform current = graphics.getTransform(); + graphics.transform(getTransform()); + graphics.drawImage(image, 0, 0, (int) width, (int) height, null); + graphics.setTransform(current); + } + +} diff --git a/src/schule/ngb/zm/formen/Point.java b/src/schule/ngb/zm/formen/Point.java new file mode 100644 index 0000000..9e6fa7a --- /dev/null +++ b/src/schule/ngb/zm/formen/Point.java @@ -0,0 +1,58 @@ +package schule.ngb.zm.formen; + +import schule.ngb.zm.Options; + +import java.awt.geom.Point2D; + +public class Point extends Circle { + + private static final double POINT_RADIUS = 2.0; + + /** + * Erstellt ein Punktobjekt mit den angegebenen Koordinaten. + * @param x + * @param y + */ + public Point( double x, double y ) { + super(x, y, POINT_RADIUS); + } + + /** + * Erstellt ein Punktobjekt mit den Koordinaten des übergebenen + * {@link schule.ngb.zm.Vector Vektors}. + * @param point + */ + public Point( schule.ngb.zm.Vector vector ) { + super(vector.x, vector.y, POINT_RADIUS); + } + + /** + * Erstellt ein Punktobjekt mit den Koordinaten des übergebenen + * {@link java.awt.geom.Point2D Punktes}. + * @param point + */ + public Point( Point2D point ) { + super(point.getX(), point.getY(), POINT_RADIUS); + } + + /** + * Erstellt ein Punktobjekt mit den Koordinaten der angegeben Form. + * @param shape + */ + public Point( Shape shape ) { + super(shape.getX(), shape.getY(), POINT_RADIUS); + } + + @Override + public void scale( double factor ) { + // Skalierung ist für Punkte deaktiviert + return; + } + + @Override + public void setAnchor( Options.Direction anchor ) { + // Punkte sind immer im Zentrum verankert + calculateAnchor(radius + radius, radius + radius, CENTER); + } + +} diff --git a/src/schule/ngb/zm/formen/Polygon.java b/src/schule/ngb/zm/formen/Polygon.java new file mode 100644 index 0000000..b14a208 --- /dev/null +++ b/src/schule/ngb/zm/formen/Polygon.java @@ -0,0 +1,82 @@ +package schule.ngb.zm.formen; + +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; + +public class Polygon extends Shape { + + protected Point2D[] points; + + public Polygon( double x, double y, Point2D... points ) { + super(x, y); + + this.points = new Point2D[points.length]; + for( int i = 0; i < points.length; i++ ) { + this.points[i] = new Point2D.Double(points[i].getX() - x, points[i].getY() - y); + } + } + + public Polygon( Point2D... points ) { + this.points = new Point2D[points.length]; + for( int i = 0; i < points.length; i++ ) { + if( i == 0 ) { + x = points[i].getX(); + y = points[i].getY(); + } + this.points[i] = new Point2D.Double(points[i].getX() - x, points[i].getY() - y); + } + } + + public Polygon( double x1, double y1, double x2, double y2, double... coordinates ) { + super(x1, y1); + + int numPoints = coordinates.length / 2 + 2; + + points = new Point2D[numPoints]; + this.points[0] = new Point2D.Double(x1, y1); + this.points[1] = new Point2D.Double(x2 - x1, y2 - y1); + for( int i = 0; i < numPoints - 2; i += 1 ) { + this.points[i + 2] = new Point2D.Double(coordinates[i * 2] - x1, coordinates[i * 2 + 1] - y1); + } + } + + public Polygon( Polygon polygon ) { + super(polygon.x, polygon.y); + copyFrom(polygon); + } + + public Point2D[] getPoints() { + return points; + } + + @Override + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + if( shape instanceof Polygon ) { + Polygon v = (Polygon) shape; + + points = new Point2D[v.points.length]; + for( int i = 0; i < v.points.length; i++ ) { + points[i] = new Point2D.Double(v.points[i].getX(), v.points[i].getY()); + } + } + } + + @Override + public Shape copy() { + return new Polygon(this); + } + + @Override + public java.awt.Shape getShape() { + Path2D shape = new Path2D.Double(); + //shape.moveTo(points[0].getX(), points[0].getY()); + shape.moveTo(0, 0); + for( int i = 1; i < points.length; i++ ) { + shape.lineTo(points[i].getX(), points[i].getY()); + } + shape.closePath(); + return shape; + } + +} diff --git a/src/schule/ngb/zm/formen/Punkt.java b/src/schule/ngb/zm/formen/Punkt.java deleted file mode 100644 index 33ec9d6..0000000 --- a/src/schule/ngb/zm/formen/Punkt.java +++ /dev/null @@ -1,34 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.geom.Point2D; - -public class Punkt extends Kreis { - - private static final double PUNKT_RADIUS = 2.0; - - - public Punkt( double pX, double pY ) { - super(pX, pY, PUNKT_RADIUS); - } - - public Punkt( Point2D pPunkt ) { - super(pPunkt.getX(), pPunkt.getY(), PUNKT_RADIUS); - } - - public Punkt( Form pForm ) { - super(pForm.getX(), pForm.getY(), PUNKT_RADIUS); - } - - @Override - public void skalieren( double pFaktor ) { - // Skalierung ist für Punkte deaktiviert - return; - } - - @Override - public void setAnkerpunkt( byte pAnker ) { - // Punkte sind immer im Zentrum verankert - ankerBerechnen(radius + radius, radius + radius, ZENTRUM); - } - -} diff --git a/src/schule/ngb/zm/formen/Quad.java b/src/schule/ngb/zm/formen/Quad.java new file mode 100644 index 0000000..b62ffb4 --- /dev/null +++ b/src/schule/ngb/zm/formen/Quad.java @@ -0,0 +1,36 @@ +package schule.ngb.zm.formen; + +import java.awt.geom.Point2D; +import java.util.Arrays; + +public class Quad extends Polygon { + + public Quad( double x, double y, Point2D... points ) { + super(x, y, Arrays.copyOf(points, 4)); + if( points.length < 4 ) { + throw new IllegalArgumentException("A quadrilateral requires exactly four points."); + } + } + + public Quad( Point2D... points ) { + super(Arrays.copyOf(points, 4)); + if( points.length < 4 ) { + throw new IllegalArgumentException("A quadrilateral requires exactly four points."); + } + } + + public Quad( double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4 ) { + super(x1, y1, x2, y2, x3, y3, x4, y4); + } + + public Quad( Quad pViereck ) { + super(pViereck.x, pViereck.y); + copyFrom(pViereck); + } + + @Override + public Shape copy() { + return new Quad(this); + } + +} diff --git a/src/schule/ngb/zm/formen/Raute.java b/src/schule/ngb/zm/formen/Raute.java deleted file mode 100644 index 7d95eb6..0000000 --- a/src/schule/ngb/zm/formen/Raute.java +++ /dev/null @@ -1,38 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.Path2D; - -public class Raute extends Drache { - - private double breite; - - private double hoehe; - - public Raute( double pX, double pY, double pBreite, double pHoehe ) { - super(pX, pY, pBreite, pHoehe, 0.5); - setAnkerpunkt(ZENTRUM); - } - - public Raute( Raute pRaute ) { - this(pRaute.x, pRaute.y, pRaute.breite, pRaute.hoehe); - kopiere(pRaute); - } - - @Override - public void setVerhaeltnis( double pVerhaeltnis ) { - super.setVerhaeltnis(0.5); - } - - @Override - public void kopiere( Form pForm ) { - super.kopiere(pForm); - super.setVerhaeltnis(0.5); - } - - @Override - public Drache kopie() { - return new Drache(this); - } - -} diff --git a/src/schule/ngb/zm/formen/Rechteck.java b/src/schule/ngb/zm/formen/Rechteck.java deleted file mode 100644 index 4def6d7..0000000 --- a/src/schule/ngb/zm/formen/Rechteck.java +++ /dev/null @@ -1,107 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.Rectangle2D; - -public class Rechteck extends Form { - - protected double breite; - - protected double hoehe; - - public Rechteck( double pX, double pY, double pBreite, double pHoehe ) { - super(pX, pY); - breite = pBreite; - hoehe = pHoehe; - setAnkerpunkt(NORDWESTEN); - } - - public Rechteck( Rechteck pRechteck ) { - this( - pRechteck.x, pRechteck.y, - pRechteck.breite, pRechteck.hoehe); - kopiere(pRechteck); - } - - public Rechteck( Form pForm ) { - Shape s = pForm.getShape(); - s = pForm.getVerzerrung().createTransformedShape(s); - Rectangle2D bounds = s.getBounds2D(); - x = bounds.getX(); - y = bounds.getY(); - breite = bounds.getWidth(); - hoehe = bounds.getHeight(); - fuellFarbe = null; - konturArt = GESTRICHELT; - } - - public double getBreite() { - return breite; - } - - public void setBreite( double breite ) { - this.breite = breite; - } - - public double getHoehe() { - return hoehe; - } - - public void setHoehe( double hoehe ) { - this.hoehe = hoehe; - } - - - @Override - public Rechteck kopie() { - return new Rechteck(this); - } - - @Override - public void kopiere( Form pForm ) { - super.kopiere(pForm); - if( pForm instanceof Rechteck ) { - Rechteck rechteck = (Rechteck) pForm; - breite = rechteck.breite; - hoehe = rechteck.hoehe; - } - } - - @Override - public void skalieren( double pFaktor ) { - super.skalieren(pFaktor); - breite *= pFaktor; - hoehe *= pFaktor; - } - - @Override - public Shape getShape() { - return new Rectangle2D.Double(0, 0, breite, hoehe); - } - - @Override - public void setAnkerpunkt( byte pAnker ) { - ankerBerechnen(breite, hoehe, pAnker); - } - - @Override - public boolean equals( Object o ) { - if( this == o ) return true; - if( o == null || getClass() != o.getClass() ) return false; - Rechteck rechteck = (Rechteck) o; - return super.equals(o) && - Double.compare(rechteck.breite, breite) == 0 && - Double.compare(rechteck.hoehe, hoehe) == 0; - } - - @Override - public String toString() { - return getClass().getCanonicalName() + "[" + - "x=" + x + - ",y=" + y + - ",breite=" + breite + - ",hoehe=" + hoehe + - ']'; - } - -} diff --git a/src/schule/ngb/zm/formen/Rectangle.java b/src/schule/ngb/zm/formen/Rectangle.java new file mode 100644 index 0000000..80299a0 --- /dev/null +++ b/src/schule/ngb/zm/formen/Rectangle.java @@ -0,0 +1,108 @@ +package schule.ngb.zm.formen; + +import schule.ngb.zm.Options; + +import java.awt.geom.Rectangle2D; + +public class Rectangle extends Shape { + + protected double width; + + protected double height; + + public Rectangle( double x, double y, double width, double height ) { + super(x, y); + this.width = width; + this.height = height; + setAnchor(NORTHWEST); + } + + public Rectangle( Rectangle pRechteck ) { + this( + pRechteck.x, pRechteck.y, + pRechteck.width, pRechteck.height); + copyFrom(pRechteck); + } + + public Rectangle( Shape pShape ) { + java.awt.Shape s = pShape.getShape(); + s = pShape.getTransform().createTransformedShape(s); + Rectangle2D bounds = s.getBounds2D(); + x = bounds.getX(); + y = bounds.getY(); + width = bounds.getWidth(); + height = bounds.getHeight(); + fillColor = null; + strokeType = DASHED; + } + + public double getWidth() { + return width; + } + + public void setWidth( double width ) { + this.width = width; + } + + public double getHeight() { + return height; + } + + public void setHeight( double height ) { + this.height = height; + } + + + @Override + public Rectangle copy() { + return new Rectangle(this); + } + + @Override + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + if( shape instanceof Rectangle ) { + Rectangle rechteck = (Rectangle) shape; + width = rechteck.width; + height = rechteck.height; + } + } + + @Override + public void scale( double factor ) { + super.scale(factor); + width *= factor; + height *= factor; + } + + @Override + public java.awt.Shape getShape() { + return new Rectangle2D.Double(0, 0, width, height); + } + + @Override + public void setAnchor( Options.Direction anchor ) { + calculateAnchor(width, height, anchor); + } + + @Override + public boolean equals( Object o ) { + if( this == o ) return true; + if( o == null || getClass() != o.getClass() ) return false; + Rectangle rechteck = (Rectangle) o; + return super.equals(o) && + Double.compare(rechteck.width, width) == 0 && + Double.compare(rechteck.height, height) == 0; + } + + @Override + public String toString() { + return getClass().getCanonicalName() + "[" + + "x=" + x + + ",y=" + y + + ",width=" + width + + ",height=" + height + + ']'; + } + +} diff --git a/src/schule/ngb/zm/formen/Rhombus.java b/src/schule/ngb/zm/formen/Rhombus.java new file mode 100644 index 0000000..2f86ad7 --- /dev/null +++ b/src/schule/ngb/zm/formen/Rhombus.java @@ -0,0 +1,32 @@ +package schule.ngb.zm.formen; + +public class Rhombus extends Kite { + + public Rhombus( double x, double y, double width, double height ) { + super(x, y, width, height, 0.5); + setAnchor(CENTER); + } + + public Rhombus( Rhombus rhombus ) { + this(rhombus.x, rhombus.y, rhombus.width, rhombus.height); + this.copyFrom(rhombus); + } + + @Override + public void setRatio( double ratio ) { + // Für eine Raute ist das Verhältnis immer 50/5ß + super.setRatio(0.5); + } + + @Override + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + super.setRatio(0.5); + } + + @Override + public Kite copy() { + return new Kite(this); + } + +} diff --git a/src/schule/ngb/zm/formen/RoundedRectangle.java b/src/schule/ngb/zm/formen/RoundedRectangle.java new file mode 100644 index 0000000..98b9bfb --- /dev/null +++ b/src/schule/ngb/zm/formen/RoundedRectangle.java @@ -0,0 +1,57 @@ +package schule.ngb.zm.formen; + +import java.awt.geom.RoundRectangle2D; + +public class RoundedRectangle extends Rectangle { + + protected double borderRadius = 1.0; + + public RoundedRectangle( double x, double y, double width, double height, double borderRadius ) { + super(x, y, width, height); + this.borderRadius = borderRadius; + } + + public RoundedRectangle( Rectangle pRechteck ) { + super( + pRechteck.x, pRechteck.y, + pRechteck.width, pRechteck.height); + copyFrom(pRechteck); + } + + @Override + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + if( shape instanceof RoundedRectangle ) { + RoundedRectangle rechteck = (RoundedRectangle) shape; + borderRadius = rechteck.borderRadius; + } + } + + @Override + public java.awt.Shape getShape() { + return new RoundRectangle2D.Double( + 0, 0, width, height, borderRadius, borderRadius + ); + } + + @Override + public boolean equals( Object o ) { + if( this == o ) return true; + if( o == null || getClass() != o.getClass() ) return false; + RoundedRectangle rechteck = (RoundedRectangle) o; + return super.equals(o) && + Double.compare(rechteck.borderRadius, borderRadius) == 0; + } + + @Override + public String toString() { + return getClass().getCanonicalName() + "[" + + "x=" + x + + ",y=" + y + + ",width=" + width + + ",height=" + height + + ",rundung=" + borderRadius + + ']'; + } + +} diff --git a/src/schule/ngb/zm/formen/Shape.java b/src/schule/ngb/zm/formen/Shape.java new file mode 100644 index 0000000..78846b2 --- /dev/null +++ b/src/schule/ngb/zm/formen/Shape.java @@ -0,0 +1,291 @@ +package schule.ngb.zm.formen; + +import org.jetbrains.annotations.NotNull; +import schule.ngb.zm.Options; + +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +public abstract class Shape extends FilledShape { + + protected double x; + + protected double y; + + protected double rotation = 0.0; + + protected double scale = 1.0; + + protected boolean visible = true; + + protected Point2D.Double anchor = new Point2D.Double(); + + public Shape( double x, double y ) { + this.x = x; + this.y = y; + } + + public Shape() { + this(0.0, 0.0); + } + + public double getX() { + return x; + } + + public void setX( double x ) { + this.x = x; + } + + public double getY() { + return y; + } + + public void setY( double y ) { + this.y = y; + } + + public double getRotation() { + return rotation; + } + + public double getScale() { + return scale; + } + + public boolean isVisible() { + return visible; + } + + public void hide() { + visible = false; + } + + public void show() { + visible = true; + } + + public void toggle() { + visible = !visible; + } + + public Point2D.Double getAnchor() { + return new Point2D.Double(anchor.x, anchor.y); + } + + /** + * Setzt den Ankerpunkt der Form basierend auf der angegebenen + * {@link Options.Direction Richtung}. + * + * Für das Setzen des Ankers muss das {@link #getBounds() begrenzende + * Rechteck} berechnet werden. Unterklassen sollten die Methode + * überschreiben, wenn der Anker auch direkt gesetzt werden kann. + * @param anchor + */ + public void setAnchor( Options.Direction anchor ) { + java.awt.Shape shape = getShape(); + if( shape != null ) { + Rectangle2D bounds = shape.getBounds2D(); + calculateAnchor(bounds.getWidth(), bounds.getHeight(), anchor); + } else { + this.anchor.x = 0; + this.anchor.y = 0; + } + } + + /** + * Setzt den Ankerpunkt explizit auf den angegebenen + * @param anchor + */ + public void setAnchor( Point2D.Double anchor ) { + setAnchor(anchor, false); + } + + public void setAnchor( Point2D.Double anchor, boolean isRelative ) { + if( anchor != null ) { + setAnchor(anchor.x, anchor.y, isRelative); + } else { + setAnchor(0, 0, true); + } + } + + public void setAnchor( double x, double y ) { + setAnchor(x, y, false); + } + + public void setAnchor( double x, double y, boolean isRelative ) { + if( isRelative ) { + this.anchor.x = x; + this.anchor.y = y; + } else { + this.anchor.x = this.x-x; + this.anchor.y = this.y-y; + } + } + + /** + * Hilfsmethode zur Berechnung eines Ankerpunktes relativ zu den angegebenen + * Begrenzungen basierend aus {@link #x}-, {@link #y}-Koordinate und + * width / height (Breite / Höhe). + * @param width + * @param height + * @param anchor + */ + protected void calculateAnchor( double width, double height, @NotNull Options.Direction anchor ) { + double bHalf = width * .5, hHalf = height * .5; + // pAnker == CENTER + this.anchor.x = bHalf; + this.anchor.y = hHalf; + if( NORTH.is(anchor) ) { + this.anchor.y -= hHalf; + } + if( SOUTH.is(anchor) ) { + this.anchor.y += hHalf; + } + if( WEST.is(anchor) ) { + this.anchor.x -= bHalf; + } + if( EAST.is(anchor) ) { + this.anchor.x += bHalf; + } + } + + /** + * Kopiert die Eigenschaften der übergebenen Form in diese. + * + * Unterklassen sollten diese Methode überschreiben, um weitere Eigenschaften + * zu kopieren (zum Beispiel den Radius eines Kreises). Mit dem Aufruf + * super.copyFrom(shape) sollten die Basiseigenschaften + * kopiert werden. + * @param shape + */ + public void copyFrom( Shape shape ) { + moveTo(shape.x, shape.y); + setFillColor(shape.getFillColor()); + setStrokeColor(shape.getStrokeColor()); + setStrokeWeight(shape.getStrokeWeight()); + setStrokeType(shape.getStrokeType()); + visible = shape.isVisible(); + rotation = shape.rotation; + scale(shape.scale); + setAnchor(shape.getAnchor()); + } + + public abstract Shape copy(); + + public abstract java.awt.Shape getShape(); + + public Rectangle getBounds() { + return new Rectangle(this); + } + + public void move( double dx, double dy ) { + x += dx; + y += dy; + } + + public void moveTo( double x, double y ) { + this.x = x; + this.y = y; + } + + public void scale( double factor ) { + scale = factor; + anchor.x *= factor; + anchor.y *= 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 shear( double dx, double dy ) { + verzerrung.shear(dx, dy); + }*/ + + public AffineTransform getTransform() { + AffineTransform transform = new AffineTransform(); + transform.translate(x, y); + transform.rotate(Math.toRadians(rotation)); + //transform.scale(scale, scale); + transform.translate(-anchor.x, -anchor.y); + return transform; + } + + /** + * Zeichnet die Form. + * + * @param graphics + */ + @Override + public final void draw( Graphics2D graphics ) { + draw(graphics, getTransform()); + } + + /** + * Zeichnet die Form, aber wendet zuvor noch eine zusätzliche Transformations- + * matrix an. Wird u.A. von der {@link ShapeGroup} verwendet. + * + * @param graphics + * @param pVerzerrung + */ + public void draw( Graphics2D graphics, AffineTransform pVerzerrung ) { + if( !visible ) { + return; + } + + java.awt.Shape shape = getShape(); + if( shape != null ) { + if( pVerzerrung != null ) { + shape = pVerzerrung.createTransformedShape(shape); + } + + Color currentColor = graphics.getColor(); + if( fillColor != null && fillColor.getAlpha() > 0 ) { + graphics.setColor(fillColor.getColor()); + graphics.fill(shape); + } + if( strokeColor != null && strokeColor.getAlpha() > 0 + && strokeWeight > 0.0 ) { + graphics.setColor(strokeColor.getColor()); + graphics.setStroke(createStroke()); + graphics.draw(shape); + } + graphics.setColor(currentColor); + } + } + + /** + * Vergleicht die Form mit einem anderen Objekt. Handelt es sich bei dem + * Objekt um eine andere Form, werden Position, Drehwinkel und Skalierung + * verglichen. Unterklassen überschreiben die Methode, um weitere + * Eigenschaften zu berücksichtigen. + *

+ * Die Eigenschaften von {@link FilledShape} und {@link StrokedShape} werden + * nicht verglichen. + * + * @param o Ein anderes Objekt. + * @return + */ + @Override + public boolean equals( Object o ) { + if( this == o ) return true; + if( o == null || getClass() != o.getClass() ) return false; + Shape pShape = (Shape) o; + return Double.compare(pShape.x, x) == 0 && + Double.compare(pShape.y, y) == 0 && + Double.compare(pShape.rotation, rotation) == 0 && + Double.compare(pShape.scale, scale) == 0; + } + +} diff --git a/src/schule/ngb/zm/formen/ShapeGroup.java b/src/schule/ngb/zm/formen/ShapeGroup.java new file mode 100644 index 0000000..e66d382 --- /dev/null +++ b/src/schule/ngb/zm/formen/ShapeGroup.java @@ -0,0 +1,138 @@ +package schule.ngb.zm.formen; + +import schule.ngb.zm.Options; + +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.geom.Path2D; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class ShapeGroup extends Shape { + + private List shapes; + + public ShapeGroup() { + super(); + shapes = new ArrayList<>(10); + } + + public ShapeGroup( double x, double y ) { + super(x, y); + shapes = new ArrayList<>(10); + } + + public ShapeGroup( double x, double y, Shape... shapes ) { + super(x, y); + this.shapes = new ArrayList<>(shapes.length); + for( Shape pShape : shapes ) { + this.shapes.add(pShape); + } + setAnchor(CENTER); + } + + public Shape copy() { + // TODO: implement? + return null; + } + + public void add( Shape... shapes ) { + for( Shape shape : shapes ) { + add(shape, false); + } + } + + public void add( Shape pShape, boolean relative ) { + if( relative ) { + pShape.x = pShape.x - x; + pShape.y = pShape.y - y; + } + shapes.add(pShape); + } + + public void removeAll() { + shapes.clear(); + } + + public List getShapes() { + return shapes; + } + public List getShapes( Class typeClass ) { + LinkedList list = new LinkedList<>(); + for( Shape s: shapes ) { + if( typeClass.getClass().isInstance(s) ) { + list.add((ShapeType)s); + } + } + return list; + } + + public void remove( Shape shape ) { + shapes.remove(shape); + } + + public Shape get( int index ) { + if( index < shapes.size() ) { + return shapes.get(index); + } else { + return null; + } + } + + public boolean contains( Shape shape ) { + return shapes.contains(shape); + } + + public int size() { + return shapes.size(); + } + + @Override + public void setAnchor( Options.Direction anchor ) { + double minx = Double.MAX_VALUE, miny = Double.MAX_VALUE, + maxx = Double.MIN_VALUE, maxy = Double.MIN_VALUE; + for( Shape pShape : shapes ) { + Rectangle bounds = pShape.getBounds(); + if( bounds.x < minx ) + minx = bounds.x; + if( bounds.y < miny ) + miny = bounds.y; + if( bounds.x+bounds.width > maxx ) + maxx = bounds.x+bounds.width; + if( bounds.y+bounds.height > maxy ) + maxy = bounds.y+bounds.height; + } + + calculateAnchor(maxx-minx, maxy-miny, anchor); + } + + @Override + public java.awt.Shape getShape() { + Path2D.Double gruppe = new Path2D.Double(); + for( Shape pShape : shapes ) { + gruppe.append(pShape.getShape(), false); + } + return gruppe; + } + + @Override + public void draw( Graphics2D graphics, AffineTransform pVerzerrung ) { + if( !visible ) { + return; + } + + AffineTransform verzerrung = new AffineTransform(); + verzerrung.translate(x, y); + verzerrung.rotate(Math.toRadians(rotation)); + //verzerrung.scale(skalierung, skalierung); + verzerrung.translate(-anchor.x, -anchor.y); + + for( Shape f: shapes ) { + AffineTransform af = f.getTransform(); + af.preConcatenate(verzerrung); + f.draw(graphics, af); + } + } + +} diff --git a/src/schule/ngb/zm/formen/ShapesLayer.java b/src/schule/ngb/zm/formen/ShapesLayer.java new file mode 100644 index 0000000..2ffc1f0 --- /dev/null +++ b/src/schule/ngb/zm/formen/ShapesLayer.java @@ -0,0 +1,69 @@ +package schule.ngb.zm.formen; + +import schule.ngb.zm.Layer; + +import java.awt.*; +import java.util.LinkedList; + +public class ShapesLayer extends Layer { + + protected boolean clearBeforeDraw = true; + + private LinkedList formen; + + public ShapesLayer() { + super(); + formen = new LinkedList(); + } + + public ShapesLayer( int width, int height ) { + super(width, height); + formen = new LinkedList(); + } + + public void add( Shape... pFormen ) { + synchronized( formen ) { + for( Shape f : pFormen ) { + formen.add(f); + } + } + } + + public void showAll() { + synchronized( formen ) { + for( Shape pShape : formen ) { + pShape.hide(); + } + } + } + + public void hideAll() { + synchronized( formen ) { + for( Shape pShape : formen ) { + pShape.show(); + } + } + } + + public java.util.List getShapes() { + return formen; + } + + @Override + public void draw( Graphics2D pGraphics ) { + if( clearBeforeDraw ) { + clear(); + } + + synchronized( formen ) { + for( Shape pShape : formen ) { + if( pShape.isVisible() ) { + pShape.draw(drawing); + } + } + } + + super.draw(pGraphics); + } + +} diff --git a/src/schule/ngb/zm/formen/StrokedShape.java b/src/schule/ngb/zm/formen/StrokedShape.java new file mode 100644 index 0000000..61508a6 --- /dev/null +++ b/src/schule/ngb/zm/formen/StrokedShape.java @@ -0,0 +1,104 @@ +package schule.ngb.zm.formen; + +import schule.ngb.zm.Color; +import schule.ngb.zm.Constants; +import schule.ngb.zm.Drawable; +import schule.ngb.zm.Options; + +import java.awt.*; + + +public abstract class StrokedShape extends Constants implements Drawable { + + protected Color strokeColor = STD_STROKECOLOR; + + protected double strokeWeight = STD_STROKEWEIGHT; + + protected Options.StrokeType strokeType = SOLID; + + public Color getStrokeColor() { + return strokeColor; + } + + public void setStrokeColor( Color color ) { + this.strokeColor = color; + } + + public void setStrokeColor( int gray ) { + setStrokeColor(gray, gray, gray, 255); + } + + public void noStroke() { + strokeColor = null; + } + + public void setStrokeColor( int gray, int alpha ) { + setStrokeColor(gray, gray, gray, alpha); + } + + public void setStrokeColor( int red, int green, int blue ) { + setStrokeColor(red, green, blue, 255); + } + + public void setStrokeColor( int red, int green, int blue, int alpha ) { + setStrokeColor(new Color(red, green, blue, alpha)); + } + + public double getStrokeWeight() { + return strokeWeight; + } + + public void setStrokeWeight( double weight ) { + this.strokeWeight = weight; + } + + public Options.StrokeType getStrokeType() { + return strokeType; + } + + /** + * Setzt den Typ der Kontur. Erlaubte Werte sind {@link #DASHED}, + * {@link #DOTTED} und {@link #SOLID}. + * @param type + */ + public void setStrokeType( Options.StrokeType type ) { + this.strokeType = DASHED; + } + + @Override + public abstract void draw( Graphics2D graphics ); + + /** + * Erstellt ein {@link Stroke} Objekt für den Konturtyp. + * @return + */ + protected Stroke createStroke() { + 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); + } + } + + public void resetStroke() { + setStrokeColor(STD_STROKECOLOR); + setStrokeWeight(STD_STROKEWEIGHT); + setStrokeType(SOLID); + } + +} diff --git a/src/schule/ngb/zm/formen/Text.java b/src/schule/ngb/zm/formen/Text.java index 6370b33..ef8fb7f 100644 --- a/src/schule/ngb/zm/formen/Text.java +++ b/src/schule/ngb/zm/formen/Text.java @@ -1,56 +1,56 @@ package schule.ngb.zm.formen; +import schule.ngb.zm.Options; + import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; -public class Text extends Form { - - private static final int DFT_FONT_SIZE = 14; +public class Text extends Shape { protected String text; - protected Font schriftart; + protected Font font; - protected int breite = 0, hoehe = 0, ascent = 0; + protected int width = 0, height = 0, ascent = 0; - public Text( double pX, double pY, String pText ) { - super(pX, pY); - schriftart = new Font(Font.SANS_SERIF, Font.PLAIN, DFT_FONT_SIZE); - setText(pText); + public Text( double x, double y, String text ) { + super(x, y); + font = new Font(Font.SANS_SERIF, Font.PLAIN, STD_FONTSIZE); + setText(text); } - public Text( Text pText ) { - super(pText.getX(), pText.getY()); - kopiere(pText); + public Text( Text text ) { + super(text.getX(), text.getY()); + copyFrom(text); } - public Form kopie() { + public Shape copy() { return new Text(this); } @Override - public void kopiere( Form pForm ) { - super.kopiere(pForm); - if( pForm instanceof Text ) { - Text pText = (Text)pForm; + public void copyFrom( Shape shape ) { + super.copyFrom(shape); + if( shape instanceof Text ) { + Text pText = (Text) shape; this.text = pText.getText(); - this.schriftart = pText.getSchriftart(); + this.font = pText.getFont(); } } @Override - public void skalieren( double pFaktor ) { - super.skalieren(pFaktor); - setSchriftgroesse(schriftart.getSize2D()*pFaktor); + public void scale( double factor ) { + super.scale(factor); + setFontsize(font.getSize2D() * factor); } - public Font getSchriftart() { - return schriftart; + public Font getFont() { + return font; } - public void setSchriftgroesse( double pGroesse ) { - schriftart = schriftart.deriveFont((float)pGroesse); + public void setFontsize( double size ) { + font = font.deriveFont((float) size); setText(text); } @@ -62,34 +62,34 @@ public class Text extends Form { text = pText; Canvas metricsCanvas = new Canvas(); - FontMetrics metrics = metricsCanvas.getFontMetrics(schriftart); - breite = metrics.stringWidth(text); - hoehe = metrics.getDescent() + metrics.getAscent(); + FontMetrics metrics = metricsCanvas.getFontMetrics(font); + width = metrics.stringWidth(text); + height = metrics.getDescent() + metrics.getAscent(); ascent = metrics.getAscent(); - setAnkerpunkt(ZENTRUM); + setAnchor(CENTER); } - public double getBreite() { - return breite; + public double getWidth() { + return width; } - public double getHoehe() { - return hoehe; + public double getHeight() { + return height; } - public void setAnkerpunkt( byte pAnker ) { - ankerBerechnen(breite, ascent - hoehe, pAnker); + public void setAnchor( Options.Direction anchor ) { + calculateAnchor(width, ascent - height, anchor); } @Override - public Shape getShape() { - return new Rectangle2D.Double(0, 0, breite, hoehe); + public java.awt.Shape getShape() { + return new Rectangle2D.Double(0, 0, width, height); } @Override - public void zeichnen( Graphics2D graphics, AffineTransform pVerzerrung ) { - if( !sichtbar ) { + public void draw( Graphics2D graphics, AffineTransform pVerzerrung ) { + if( !visible ) { return; } @@ -99,11 +99,11 @@ public class Text extends Form { AffineTransform af = graphics.getTransform(); // Neue Werte setzen - graphics.setFont(schriftart); - graphics.setColor(konturFarbe); - graphics.transform(getVerzerrung()); + graphics.setFont(font); + graphics.setColor(strokeColor.getColor()); + graphics.transform(pVerzerrung); - // Text zeichnen + // Draw text FontMetrics fm = graphics.getFontMetrics(); //graphics.drawString(text, (float) (x - fm.stringWidth(text)/2.0), (float) (y + fm.getDescent())); graphics.drawString(text, 0, 0); @@ -121,13 +121,15 @@ public class Text extends Form { Text text = (Text) o; return super.equals(o) && text.equals(text.text) && - schriftart.equals(text.schriftart); + font.equals(text.font); } @Override public String toString() { return getClass().getCanonicalName() + "[" + - "text=" + text + + "text=" + text + ',' + + "font=" + font.getName() + ',' + + "size=" + font.getSize() + ']'; } diff --git a/src/schule/ngb/zm/formen/Triangle.java b/src/schule/ngb/zm/formen/Triangle.java new file mode 100644 index 0000000..6aa8a71 --- /dev/null +++ b/src/schule/ngb/zm/formen/Triangle.java @@ -0,0 +1,36 @@ +package schule.ngb.zm.formen; + +import java.awt.geom.Point2D; +import java.util.Arrays; + +public class Triangle extends Polygon { + + public Triangle( double x, double y, Point2D... points ) { + super(x, y, Arrays.copyOf(points, 3)); + if( points.length < 3 ) { + throw new IllegalArgumentException("A triangle requires exactly three points."); + } + } + + public Triangle( Point2D... points ) { + super(Arrays.copyOf(points, 3)); + if( points.length < 3 ) { + throw new IllegalArgumentException("A triangle requires exactly three points."); + } + } + + public Triangle( double x1, double y1, double x2, double y2, double x3, double y3 ) { + super(x1, y1, x2, y2, x3, y3); + } + + public Triangle( Triangle triangle ) { + super(triangle.x, triangle.y); + copyFrom(triangle); + } + + @Override + public Shape copy() { + return new Triangle(this); + } + +} diff --git a/src/schule/ngb/zm/formen/Vieleck.java b/src/schule/ngb/zm/formen/Vieleck.java deleted file mode 100644 index 7ef6158..0000000 --- a/src/schule/ngb/zm/formen/Vieleck.java +++ /dev/null @@ -1,72 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.Path2D; -import java.awt.geom.Point2D; -import java.util.Arrays; - -public class Vieleck extends Form { - - protected Point2D[] ecken; - - public Vieleck( double pX, double pY, Point2D... pEcken ) { - super(pX, pY); - - ecken = new Point2D[pEcken.length]; - for( int i = 0; i < pEcken.length; i++ ) { - ecken[i] = new Point2D.Double(pEcken[i].getX()-pX, pEcken[i].getY()-pY); - } - } - - public Vieleck( Point2D... pEcken ) { - super(); - - ecken = new Point2D[pEcken.length]; - for( int i = 0; i < pEcken.length; i++ ) { - if( i == 0 ) { - x = pEcken[i].getX(); - y = pEcken[i].getY(); - } - ecken[i] = new Point2D.Double(pEcken[i].getX()-x, pEcken[i].getY()-y); - } - } - - public Vieleck( Vieleck pVieleck ) { - this(pVieleck.x, pVieleck.y); - kopiere(pVieleck); - } - - public Point2D[] getEcken() { - return ecken; - } - - @Override - public void kopiere( Form pForm ) { - super.kopiere(pForm); - if( pForm instanceof Vieleck ) { - Vieleck v = (Vieleck) pForm; - - ecken = new Point2D[v.ecken.length]; - for( int i = 0; i < v.ecken.length; i++ ) { - ecken[i] = new Point2D.Double(v.ecken[i].getX(), v.ecken[i].getY()); - } - } - } - - @Override - public Form kopie() { - return new Vieleck(this); - } - - @Override - public Shape getShape() { - Path2D shape = new Path2D.Double(); - shape.moveTo(ecken[0].getX(), ecken[0].getY()); - for( int i = 1; i < ecken.length; i++ ) { - shape.lineTo(ecken[i].getX(), ecken[i].getY()); - } - shape.closePath(); - return shape; - } - -} diff --git a/src/schule/ngb/zm/formen/Viereck.java b/src/schule/ngb/zm/formen/Viereck.java deleted file mode 100644 index 02a1fd2..0000000 --- a/src/schule/ngb/zm/formen/Viereck.java +++ /dev/null @@ -1,34 +0,0 @@ -package schule.ngb.zm.formen; - -import java.awt.*; -import java.awt.geom.Path2D; -import java.awt.geom.Point2D; -import java.util.Arrays; - -public class Viereck extends Vieleck { - - public Viereck( double pX, double pY, Point2D... pEcken ) { - super(pX, pY, Arrays.copyOf(pEcken, 4)); - if( pEcken.length < 4 ) { - throw new IllegalArgumentException("Ein Viereck muss genau vier Eckpunkte besitzen."); - } - } - - public Viereck( Point2D... pEcken ) { - super(Arrays.copyOf(pEcken, 4)); - if( pEcken.length < 4 ) { - throw new IllegalArgumentException("Ein Viereck muss genau vier Eckpunkte besitzen."); - } - } - - public Viereck( Viereck pViereck ) { - super(pViereck.x, pViereck.y); - kopiere(pViereck); - } - - @Override - public Form kopie() { - return new Viereck(this); - } - -} diff --git a/src/schule/ngb/zm/util/ImageLoader.java b/src/schule/ngb/zm/util/ImageLoader.java new file mode 100644 index 0000000..42e23d7 --- /dev/null +++ b/src/schule/ngb/zm/util/ImageLoader.java @@ -0,0 +1,141 @@ +package schule.ngb.zm.util; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; + +public class ImageLoader { + + public static boolean cacheing = true; + + private static HashMap imageCache = new HashMap<>(); + + public static BufferedImage loadImage( String source ) { + return loadImage(source, cacheing); + } + + /** + * Läadt ein Bild von der angegebenen Quelle source und gibt das + * Bild zurück oder null, wenn das Bild nicht geladen werden + * konnte. Ist ein Bild mit der angegebenen Quelle im Cache, wird das + * gechachete Bild zurückgegeben. Dies kann mit cacheing = false + * verhindert werden. + *

+ * Wurde chacheing global deaktiviert, kann mit cacheing = true + * das Bild trotzdem aus dem Cache geladen werden, wenn es vorhanden ist. + * + * @param source + * @param cacheing + * @return + */ + public static BufferedImage loadImage( String source, boolean cacheing ) { + if( source == null || source.length() == 0 ) + throw new IllegalArgumentException("Image source may not be null or empty."); + + if( cacheing && imageCache.containsKey(source) ) { + BufferedImage cachedImage = imageCache.get(source); + if( cachedImage != null ) { + return cachedImage; + } + } + + try { + BufferedImage img; + + // Load image from working dir + File file = new File(source); + if( file.isFile() ) { + img = ImageIO.read(file); + } else { + // load ressource relative to .class-file + URL url = ImageLoader.class.getResource(source); + + // relative to ClassLoader + if( url == null ) { + url = ImageLoader.class.getClassLoader().getResource(source); + } + + // load form web or jar-file + if( url == null ) { + url = new URL(source); + } + + img = ImageIO.read(url); + } + + if( cacheing && img != null ) { + imageCache.put(source, img); + } + + return img; + } catch( IOException ioe ) { + return null; + } + } + + /** + * Loads an image into the cache with a user specified name that may differ + * from the image source string. + * + * @param name + * @param source + * @return + */ + public static boolean preloadImage( String name, String source ) { + BufferedImage img = loadImage(source, true); + if( cacheing && img != null ) { + imageCache.put(name, img); + return true; + } + return false; + } + + /** + * Checks if an image with the given name is currently cached. + * + * @param name + * @return + */ + public static boolean isCached( String name ) { + if( imageCache.containsKey(name) ) { + return imageCache.get(name) != null; + } + return false; + } + + /** + * Remove the specified key from the cache. + * + * @param name + */ + public static void invalidateCache( String name ) { + if( imageCache.containsKey(name) ) { + imageCache.remove(name); + } + } + + /** + * Prevents caching for the specified source. + * + * @param source + */ + public static void noCache( String source ) { + imageCache.put(source, null); + } + + public static void clearCache() { + imageCache.clear(); + } + + public static void enableCaching() { + cacheing = true; + } + + public static void disableCaching() { + cacheing = false; + } + +} diff --git a/src/schule/ngb/zm/util/List.java b/src/schule/ngb/zm/util/List.java new file mode 100644 index 0000000..6dc8d16 --- /dev/null +++ b/src/schule/ngb/zm/util/List.java @@ -0,0 +1,349 @@ +package schule.ngb.zm.util; + +import javax.swing.text.AbstractDocument; + +/** + *

+ * Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018 + *

+ *

+ * Generische Klasse List + *

+ *

+ * Objekt der generischen Klasse List verwalten beliebig viele linear + * angeordnete Objekte vom Typ ContentType. Auf hoechstens ein Listenobjekt, + * aktuellesObjekt genannt, kann jeweils zugegriffen werden.
+ * Wenn eine Liste leer ist, vollstaendig durchlaufen wurde oder das aktuelle + * Objekt am Ende der Liste geloescht wurde, gibt es kein aktuelles Objekt.
+ * Das erste oder das letzte Objekt einer Liste koennen durch einen Auftrag zum + * aktuellen Objekt gemacht werden. Ausserdem kann das dem aktuellen Objekt + * folgende Listenobjekt zum neuen aktuellen Objekt werden.
+ * Das aktuelle Objekt kann gelesen, veraendert oder geloescht werden. Ausserdem + * kann vor dem aktuellen Objekt ein Listenobjekt eingefuegt werden. + *

+ * + * @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule + * @version Generisch_06 2015-10-25 + */ +public class List { + + /* --------- Anfang der privaten inneren Klasse -------------- */ + + private class ListNode { + + private ContentType contentObject; + private ListNode next; + + /** + * Ein neues Objekt wird erschaffen. Der Verweis ist leer. + * + * @param pContent das Inhaltsobjekt vom Typ ContentType + */ + private ListNode(ContentType pContent) { + contentObject = pContent; + next = null; + } + + /** + * Der Inhalt des Knotens wird zurueckgeliefert. + * + * @return das Inhaltsobjekt des Knotens + */ + public ContentType getContentObject() { + return contentObject; + } + + /** + * Der Inhalt dieses Kontens wird gesetzt. + * + * @param pContent das Inhaltsobjekt vom Typ ContentType + */ + public void setContentObject(ContentType pContent) { + contentObject = pContent; + } + + /** + * Der Nachfolgeknoten wird zurueckgeliefert. + * + * @return das Objekt, auf das der aktuelle Verweis zeigt + */ + public ListNode getNextNode() { + return this.next; + } + + /** + * Der Verweis wird auf das Objekt, das als Parameter uebergeben + * wird, gesetzt. + * + * @param pNext der Nachfolger des Knotens + */ + public void setNextNode(ListNode pNext) { + this.next = pNext; + } + + } + + /* ----------- Ende der privaten inneren Klasse -------------- */ + + // erstes Element der Liste + ListNode first; + + // letztes Element der Liste + ListNode last; + + // aktuelles Element der Liste + ListNode current; + + /** + * Eine leere Liste wird erzeugt. + */ + public List() { + first = null; + last = null; + current = null; + } + + /** + * Die Anfrage liefert den Wert true, wenn die Liste keine Objekte enthaelt, + * sonst liefert sie den Wert false. + * + * @return true, wenn die Liste leer ist, sonst false + */ + public boolean isEmpty() { + // Die Liste ist leer, wenn es kein erstes Element gibt. + return first == null; + } + + /** + * Die Anfrage liefert den Wert true, wenn es ein aktuelles Objekt gibt, + * sonst liefert sie den Wert false. + * + * @return true, falls Zugriff moeglich, sonst false + */ + public boolean hasAccess() { + // Es gibt keinen Zugriff, wenn current auf kein Element verweist. + return current != null; + } + + /** + * Falls die Liste nicht leer ist, es ein aktuelles Objekt gibt und dieses + * nicht das letzte Objekt der Liste ist, wird das dem aktuellen Objekt in + * der Liste folgende Objekt zum aktuellen Objekt, andernfalls gibt es nach + * Ausfuehrung des Auftrags kein aktuelles Objekt, d.h. hasAccess() liefert + * den Wert false. + */ + public void next() { + if (this.hasAccess()) { + current = current.getNextNode(); + } + } + + /** + * Falls die Liste nicht leer ist, wird das erste Objekt der Liste aktuelles + * Objekt. Ist die Liste leer, geschieht nichts. + */ + public void toFirst() { + if (!isEmpty()) { + current = first; + } + } + + /** + * Falls die Liste nicht leer ist, wird das letzte Objekt der Liste + * aktuelles Objekt. Ist die Liste leer, geschieht nichts. + */ + public void toLast() { + if (!isEmpty()) { + current = last; + } + } + + /** + * Falls es ein aktuelles Objekt gibt (hasAccess() == true), wird das + * aktuelle Objekt zurueckgegeben, andernfalls (hasAccess() == false) gibt + * die Anfrage den Wert null zurueck. + * + * @return das aktuelle Objekt (vom Typ ContentType) oder null, wenn es + * kein aktuelles Objekt gibt + */ + public ContentType getContent() { + if (this.hasAccess()) { + return current.getContentObject(); + } else { + return null; + } + } + + /** + * Falls es ein aktuelles Objekt gibt (hasAccess() == true) und pContent + * ungleich null ist, wird das aktuelle Objekt durch pContent ersetzt. Sonst + * geschieht nichts. + * + * @param pContent + * das zu schreibende Objekt vom Typ ContentType + */ + public void setContent(ContentType pContent) { + // Nichts tun, wenn es keinen Inhalt oder kein aktuelles Element gibt. + if (pContent != null && this.hasAccess()) { + current.setContentObject(pContent); + } + } + + /** + * Falls es ein aktuelles Objekt gibt (hasAccess() == true), wird ein neues + * Objekt vor dem aktuellen Objekt in die Liste eingefuegt. Das aktuelle + * Objekt bleibt unveraendert.
+ * Wenn die Liste leer ist, wird pContent in die Liste eingefuegt und es + * gibt weiterhin kein aktuelles Objekt (hasAccess() == false).
+ * Falls es kein aktuelles Objekt gibt (hasAccess() == false) und die Liste + * nicht leer ist oder pContent gleich null ist, geschieht nichts. + * + * @param pContent + * das einzufuegende Objekt vom Typ ContentType + */ + public void insert(ContentType pContent) { + if (pContent != null) { // Nichts tun, wenn es keinen Inhalt gibt. + if (this.hasAccess()) { // Fall: Es gibt ein aktuelles Element. + + // Neuen Knoten erstellen. + ListNode newNode = new ListNode(pContent); + + if (current != first) { // Fall: Nicht an erster Stelle einfuegen. + ListNode previous = this.getPrevious(current); + newNode.setNextNode(previous.getNextNode()); + previous.setNextNode(newNode); + } else { // Fall: An erster Stelle einfuegen. + newNode.setNextNode(first); + first = newNode; + } + + } else { //Fall: Es gibt kein aktuelles Element. + + if (this.isEmpty()) { // Fall: In leere Liste einfuegen. + + // Neuen Knoten erstellen. + ListNode newNode = new ListNode(pContent); + + first = newNode; + last = newNode; + } + + } + } + } + + /** + * Falls pContent gleich null ist, geschieht nichts.
+ * Ansonsten wird ein neues Objekt pContent am Ende der Liste eingefuegt. + * Das aktuelle Objekt bleibt unveraendert.
+ * Wenn die Liste leer ist, wird das Objekt pContent in die Liste eingefuegt + * und es gibt weiterhin kein aktuelles Objekt (hasAccess() == false). + * + * @param pContent + * das anzuhaengende Objekt vom Typ ContentType + */ + public void append(ContentType pContent) { + if (pContent != null) { // Nichts tun, wenn es keine Inhalt gibt. + + if (this.isEmpty()) { // Fall: An leere Liste anfuegen. + this.insert(pContent); + } else { // Fall: An nicht-leere Liste anfuegen. + + // Neuen Knoten erstellen. + ListNode newNode = new ListNode(pContent); + + last.setNextNode(newNode); + last = newNode; // Letzten Knoten aktualisieren. + } + + } + } + + /** + * Falls es sich bei der Liste und pList um dasselbe Objekt handelt, + * pList null oder eine leere Liste ist, geschieht nichts.
+ * Ansonsten wird die Liste pList an die aktuelle Liste angehaengt. + * Anschliessend wird pList eine leere Liste. Das aktuelle Objekt bleibt + * unveraendert. Insbesondere bleibt hasAccess identisch. + * + * @param pList + * die am Ende anzuhaengende Liste vom Typ List + */ + public void concat(List pList) { + if (pList != this && pList != null && !pList.isEmpty()) { // Nichts tun, + // wenn pList und this identisch, pList leer oder nicht existent. + + if (this.isEmpty()) { // Fall: An leere Liste anfuegen. + this.first = pList.first; + this.last = pList.last; + } else { // Fall: An nicht-leere Liste anfuegen. + this.last.setNextNode(pList.first); + this.last = pList.last; + } + + // Liste pList loeschen. + pList.first = null; + pList.last = null; + pList.current = null; + } + } + + /** + * Wenn die Liste leer ist oder es kein aktuelles Objekt gibt (hasAccess() + * == false), geschieht nichts.
+ * Falls es ein aktuelles Objekt gibt (hasAccess() == true), wird das + * aktuelle Objekt geloescht und das Objekt hinter dem geloeschten Objekt + * wird zum aktuellen Objekt.
+ * Wird das Objekt, das am Ende der Liste steht, geloescht, gibt es kein + * aktuelles Objekt mehr. + */ + public void remove() { + // Nichts tun, wenn es kein aktuelle Element gibt oder die Liste leer ist. + if (this.hasAccess() && !this.isEmpty()) { + + if (current == first) { + first = first.getNextNode(); + } else { + ListNode previous = this.getPrevious(current); + if (current == last) { + last = previous; + } + previous.setNextNode(current.getNextNode()); + } + + ListNode temp = current.getNextNode(); + current.setContentObject(null); + current.setNextNode(null); + current = temp; + + //Beim loeschen des letzten Elements last auf null setzen. + if (this.isEmpty()) { + last = null; + } + } + } + + /** + * Liefert den Vorgaengerknoten des Knotens pNode. Ist die Liste leer, pNode + * == null, pNode nicht in der Liste oder pNode der erste Knoten der Liste, + * wird null zurueckgegeben. + * + * @param pNode + * der Knoten, dessen Vorgaenger zurueckgegeben werden soll + * @return der Vorgaenger des Knotens pNode oder null, falls die Liste leer ist, + * pNode == null ist, pNode nicht in der Liste ist oder pNode der erste Knoten + * der Liste ist + */ + private ListNode getPrevious(ListNode pNode) { + if (pNode != null && pNode != first && !this.isEmpty()) { + ListNode temp = first; + while (temp != null && temp.getNextNode() != pNode) { + temp = temp.getNextNode(); + } + return temp; + } else { + return null; + } + } + +}