306 lines
6.8 KiB
Java
306 lines
6.8 KiB
Java
package schule.ngb.zm.shapes;
|
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
import schule.ngb.zm.Options;
|
|
|
|
import java.awt.Color;
|
|
import java.awt.Graphics2D;
|
|
import java.awt.geom.AffineTransform;
|
|
import java.awt.geom.Point2D;
|
|
|
|
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 Options.Direction anchor = Options.Direction.CENTER;
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Gibt die Breite dieser Form zurück.
|
|
* <p>
|
|
* Die Breite einer Form ist immer die Breite ihrer Begrenzung, <b>bevor</b>
|
|
* Drehungen und andere Transformationen auf sei angewandt wurden.
|
|
* <p>
|
|
* Die Begrenzungen der tatsächlich gezeichneten Form kann mit {@link #getBounds()}
|
|
* abgerufen werden.
|
|
*
|
|
* @return
|
|
*/
|
|
public abstract double getWidth();
|
|
|
|
/**
|
|
* Gibt die Höhe dieser Form zurück.
|
|
* <p>
|
|
* Die Höhe einer Form ist immer die Höhe ihrer Begrenzung, <b>bevor</b>
|
|
* Drehungen und andere Transformationen auf sei angewandt wurden.
|
|
* <p>
|
|
* Die Begrenzungen der tatsächlich gezeichneten Form kann mit {@link #getBounds()}
|
|
* abgerufen werden.
|
|
*
|
|
* @return
|
|
*/
|
|
public abstract double getHeight();
|
|
|
|
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 Options.Direction getAnchor() {
|
|
return anchor;
|
|
}
|
|
|
|
/**
|
|
* Setzt den Ankerpunkt der Form basierend auf der angegebenen
|
|
* {@link Options.Direction Richtung}.
|
|
* <p>
|
|
* 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 ) {
|
|
if( anchor != null ) {
|
|
this.anchor = anchor;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bestimmt den Ankerpunkt der Form relativ zur oberen linken Ecke und
|
|
* abhängig vom gesetzten {@link #setAnchor(Options.Direction) Anker}.
|
|
*/
|
|
public Point2D.Double getAnchorPoint( Options.Direction anchor ) {
|
|
Point2D.Double anchorpoint = new Point2D.Double(0, 0);
|
|
|
|
double bHalf = getWidth() * .5, hHalf = getHeight() * .5;
|
|
// anchor == CENTER
|
|
anchorpoint.x = bHalf;
|
|
anchorpoint.y = hHalf;
|
|
|
|
if( NORTH.is(anchor) ) {
|
|
anchorpoint.y -= hHalf;
|
|
}
|
|
if( SOUTH.is(anchor) ) {
|
|
anchorpoint.y += hHalf;
|
|
}
|
|
if( WEST.is(anchor) ) {
|
|
anchorpoint.x -= bHalf;
|
|
}
|
|
if( EAST.is(anchor) ) {
|
|
anchorpoint.x += bHalf;
|
|
}
|
|
|
|
return anchorpoint;
|
|
}
|
|
|
|
/**
|
|
* Kopiert die Eigenschaften der übergebenen Form in diese.
|
|
* <p>
|
|
* Unterklassen sollten diese Methode überschreiben, um weitere Eigenschaften
|
|
* zu kopieren (zum Beispiel den Radius eines Kreises). Mit dem Aufruf
|
|
* <code>super.copyFrom(shape)</code> sollten die Basiseigenschaften
|
|
* kopiert werden.
|
|
*
|
|
* @param shape
|
|
*/
|
|
public void copyFrom( Shape shape ) {
|
|
if( shape != null ) {
|
|
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 Bounds getBounds() {
|
|
return new Bounds(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;
|
|
}
|
|
|
|
public void scaleBy( double factor ) {
|
|
scale(scale * factor);
|
|
}
|
|
|
|
public void rotate( double angle ) {
|
|
this.rotation += angle % 360;
|
|
}
|
|
|
|
public void rotateTo( double angle ) {
|
|
this.rotation = angle % 360;
|
|
}
|
|
|
|
public void rotate( Point2D center, double angle ) {
|
|
rotate(center.getX(), center.getY(), angle);
|
|
}
|
|
|
|
public void rotate( double x, double y, double angle ) {
|
|
this.rotation += angle % 360;
|
|
|
|
// Rotate x/y position
|
|
double x1 = this.x-x, y1 = this.y-y;
|
|
|
|
double rad = Math.toRadians(angle);
|
|
double x2 = x1 * Math.cos(rad) - y1 * Math.sin(rad);
|
|
double y2 = x1 * Math.sin(rad) + y1 * Math.cos(rad);
|
|
|
|
this.x = x2 + x;
|
|
this.y = y2 + y;
|
|
}
|
|
|
|
/*public void shear( double dx, double dy ) {
|
|
verzerrung.shear(dx, dy);
|
|
}*/
|
|
|
|
public AffineTransform getTransform() {
|
|
Point2D.Double anchorPoint = getAnchorPoint(this.anchor);
|
|
|
|
AffineTransform transform = new AffineTransform();
|
|
transform.translate(x, y);
|
|
transform.rotate(Math.toRadians(rotation));
|
|
//transform.scale(scale, scale);
|
|
transform.translate(-anchorPoint.x, -anchorPoint.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 transform
|
|
*/
|
|
public void draw( Graphics2D graphics, AffineTransform transform ) {
|
|
if( !visible ) {
|
|
return;
|
|
}
|
|
|
|
java.awt.Shape shape = getShape();
|
|
if( shape != null ) {
|
|
if( transform != null ) {
|
|
shape = transform.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.
|
|
* <p>
|
|
* 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;
|
|
}
|
|
|
|
}
|