/*
* Engine Alpha ist eine anfängerorientierte 2D-Gaming Engine.
*
* Copyright (c) 2011 - 2014 Michael Andonie and contributors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ea;
/**
* Ein Vektor bezeichnet eine relative Punktangabe, ansonsten unterscheidet er sich nicht weiter von
* einem Punkt.<br /> Vektoren werden meist für die Beschreibung einer Bewegung benutzt.
*
* @author Michael Andonie
*/
public final class Vektor implements Cloneable {
/**
* Konstante für einen "bewegungslosen" Vektor (0, 0)
*/
public static final Vektor NULLVEKTOR = new Vektor(0, 0);
/**
* Konstante für eine einfache Verschiebung nach rechts (1, 0)
*/
public static final Vektor RECHTS = new Vektor(1, 0);
/**
* Konstante für eine einfache Verschiebung nach links (-1, 0)
*/
public static final Vektor LINKS = new Vektor(-1, 0);
/**
* Konstante für eine einfache Verschiebung nach oben (0, -1)
*/
public static final Vektor OBEN = new Vektor(0, -1);
/**
* Konstante für eine einfache Verschiebung nach unten (0, 1)
*/
public static final Vektor UNTEN = new Vektor(0, 1);
/**
* Konstante, die widerspiegelt, dass keine Bewegung vollzogen wird.
*/
public static final int KEINE_BEWEGUNG = -1;
/**
* Die Konstante für die Richtung Westen
*/
public static final int W = 0;
/**
* Die Konstante für die Richtung Osten
*/
public static final int O = 1;
/**
* Die Konstante für die Richtung Norden
*/
public static final int N = 2;
/**
* Die Konstante für die Richtung Süden
*/
public static final int S = 3;
/**
* Die Konstante für die Richtung Nordwesten
*/
public static final int NW = 4;
/**
* Die Konstante für die Richtung Nordosten
*/
public static final int NO = 5;
/**
* Die Konstante für die Richtung Südwesten
*/
public static final int SW = 6;
/**
* Die Konstante für die Richtung Südosten
*/
public static final int SO = 7;
/**
* Der kontinuierliche DeltaX-Wert des Punktes. Die anderen Koordinaten sind ggf. nur gerundet.
*/
public final float x;
/**
* Der kontinuierliche DeltaY-Wert des Punktes. Die anderen Koordinaten sind ggf. nur gerundet.
*/
public final float y;
/**
* Konstruktor.
*
* @param x
* Bewegungsanteil <code>x</code>.
* @param y
* Bewegungsanteil <code>y</code>.
*/
public Vektor (float x, float y) {
this.x = x;
this.y = y;
}
/**
* Konstruktor. <br /><br /> Vektor wird erzeugt als die nötige Bewegung von einem Punkt zu
* einem zweiten.
*
* @param start
* Ausgangspunkt
* @param ziel
* Zielpunkt
*/
public Vektor (Punkt start, Punkt ziel) {
this.x = ziel.x - start.x;
this.y = ziel.y - start.y;
}
/**
* Gibt eine <b>Normierung</b> des Vektors aus. Dies ist ein Vektor, der <li>in die selbe
* Richtung wie der ursprüngliche Vektor zeigt.</li> <li>eine Länge von (möglichst) exakt 1
* hat.</li>
*
* @return Normierter Vektor zu diesem Vektor
*/
public Vektor normiert () {
return this.teilen(this.laenge());
}
/**
* Teilt die effektive Länge des Vektors durch eine Zahl und kürzt dadurch seine Effektivität.
*
* @param divisor
* Hierdurch wird die Länge des Vektors auf der Zeichenebene geteilt.
*
* @return Vektor-Objekt, das eine Bewegung in dieselbe Richtung beschreibt, allerdings in der
* Länge gekürzt um den angegebenen Divisor.
*
* @throws java.lang.ArithmeticException
* Falls <code>divisor</code> <code>0</code> ist.
* @see #multiplizieren(float)
*/
public Vektor teilen (float divisor) {
if (divisor == 0) {
throw new ArithmeticException("Der Divisor für das Teilen war 0!");
}
return new Vektor(x / divisor, y / divisor);
}
/**
* Gibt die Länge des Vektors aus.
*
* @return Länge des Vektors.
*/
public float laenge () {
return (float) Math.sqrt(x * x + y * y);
}
/**
* Berechnet die Gegenrichtung des Vektors.
*
* @return Neues Vektor-Objekt, das genau die Gegenbewegung zu dem eigenen beschreibt.
*/
public Vektor gegenrichtung () {
return new Vektor(-this.x, -this.y);
}
/**
* Berechnet die effektive Bewegung, die dieser Vektor und ein weiterer zusammen ausüben.
*
* @param v
* zweiter Vektor
*
* @return Neues Vektor-Objekt, das die Summe der beiden ursprünglichen Bewegungen darstellt.
*/
public Vektor summe (Vektor v) {
return new Vektor(this.x + v.x, this.y + v.y);
}
/**
* Berechnet die Differenz zwischen diesem und einem weiteren Vektor.
*
* @param v
* zweiter Vektor
*
* @return Die Differenz der beiden Vektoren (<code>"this - v"</code>)
*/
public Vektor differenz (Vektor v) {
return new Vektor(this.x - v.x, this.y - v.y);
}
/**
* Multipliziert die effektiven Längen beider Anteile des Vektors (<code>x</code> und
* <code>y</code>) mit einem festen Faktor. <br /> Dadurch entsteht ein neuer Vektor mit anderen
* Werten, welcher zurückgegeben wird.
*
* @param faktor
* Der Faktor, mit dem die <code>x</code>- und <code>y</code>-Werte des Vektors multipliziert
* werden
*
* @return Der Vektor mit den multiplizierten Werten
*
* @see #teilen(float)
*/
public Vektor multiplizieren (float faktor) {
return new Vektor(x * faktor, y * faktor);
}
/**
* Berechnet das <b>Skalarprodukt</b> von diesem Vektor mit einem weiteren. Das Skalarprodukt
* für zweidimensionale Vektoren ist: <code>(a, b) o (c, d) = a * b + c * d</code>
*
* @param v
* zweiter Vektor
*
* @return Skalarprodukt dieses Vektoren mit dem Vektor <code>v</code>.
*/
public float skalarprodukt (Vektor v) {
return this.x * v.x + this.y * v.y;
}
/**
* Berechnet, ob dieser Vektor keine Wirkung hat. Dies ist der Fall, wenn beide Komponenten
* (<code>x</code> und <code>y</code>) 0 sind.
*
* @return <code>true</code>, wenn dieser keine Auswirkungen macht, sonst <code>false</code>.
*/
public boolean unwirksam () {
return this.x == 0 && this.y == 0;
}
/**
* Gibt zurück, ob dieser Vektor <i>echt ganzzahlig</i> ist, also ob seine <b>tatsächlichen
* Delta-Werte</b> beide Ganzzahlen sind.
*
* @return <code>true</code>, wenn <b>beide</b> Delta-Werte dieses Punktes ganzzahlig sind,
* sonst <code>false</code>.
*/
public boolean istEchtGanzzahlig () {
return x == (int) x && y == (int) y;
}
/**
* Gibt die <code>x</code>-Verschiebung dieses Vektors wider.
*
* @return <code>x</code>-Verschiebung dieses Vektors. Positive Werte verschieben nach rechts,
* negative Werte verschieben nach links.
*
* @see #realY()
*/
public float realX () {
return x;
}
/**
* Gibt die <code>y</code>-Verschiebung dieses Vektors wider.
*
* @return <code>y</code>-Verschiebung dieses Vektors. Positive Werte verschieben nach unten,
* negative Werte verschieben nach oben.
*
* @see #realX()
*/
public float realY () {
return y;
}
/**
* Gibt einen einfachen Vektor zurück, dessen Richtungskomponenten nur <code>-1</code>,
* <code>0</code> oder <code>1</code> annehmen. <br /><br /> <li>-1 bei Werten < 0</li> <li>0
* bei Werten = 0</li> <li>1 bei Werten > 0</li>
*
* @return einfacher Vektor, der die Richtung des ursprünglichen mit einfachen Werten
* beschreibt.
*/
public Vektor einfacher () {
return vonKonstante(richtung());
}
/**
* Berechnet einen einfachen Vektor (maximale Auslenkung bei jeder Achse 1 (positiv wie
* negativ)), der der entsprechenden Konstante dieser Klasse entspricht möglich sind: <br />
* <code>N</code>, <code>S</code>, <code>O</code>, <code>W</code>, <code>NO</code>,
* <code>NW</code>, <code>SO</code>, <code>SW</code>
*
* @param konstante
* Konstante, die die Bewegungsrichtung beschreibt
*
* @return Vektor, der mit einer einfachen Auslenkung (d.h. für <code>x</code> und
* <code>y</code> je ein Wertebereich von {-1, 0, 1}) die entsprechende Bewegung macht.<br />
*
* @throws java.lang.IllegalArgumentException
* Falls die Konstante einen nicht verwendbaren Wert hat
*/
public static Vektor vonKonstante (int konstante) {
switch (konstante) {
case N:
return OBEN;
case S:
return UNTEN;
case O:
return RECHTS;
case W:
return LINKS;
case NO:
return new Vektor(+1, -1);
case NW:
return new Vektor(-1, -1);
case SO:
return new Vektor(+1, +1);
case SW:
return new Vektor(-1, +1);
default:
throw new IllegalArgumentException("Die eingegebene Konstante hatte keinen der möglichen Werte!");
}
}
/**
* Berechnet die Richtung des Vektors, in die er wirkt.<br /> Der Rückgabewert basiert auf den
* Konstanten der eigenen Klasse und sind entweder die Basiswerte (<code>N / S / O / W</code>)
* oder die Kombiwerte (<code>NO / NW / SO / SW</code>). Alle diese sind Konstanten dieser
* Klasse.
*
* @return Der Wert der Konstanten, die diese Bewegung wiederspiegelt.
*/
public int richtung () {
if (x == 0 && y == 0) {
return KEINE_BEWEGUNG;
}
if (x == 0) {
return y > 0 ? S : N;
}
if (y == 0) {
return x > 0 ? O : W;
}
if (x < 0 && y < 0) {
return NW;
}
if (x > 0 && y < 0) {
return NO;
}
if (x > 0 && y > 0) {
return SO;
}
return SW;
}
/**
* Prüft, ob ein beliebiges Objekt gleich diesem Vektor ist. <br /><br /> Zwei Vektoren gelten
* als gleich, wenn <code>x</code> und <code>y</code> der beiden Vektoren übereinstimmen.
*
* @param o
* Das auf Gleichheit mit diesem zu überprüfende Objekt.
*
* @return <code>true</code>, wenn beide Vektoren gleich sind, sonst <code>false</code>.
*/
@Override
public boolean equals (Object o) {
if (o instanceof Vektor) {
Vektor v = (Vektor) o;
return x == v.x && y == v.y;
}
return false;
}
/**
* Erstellt ein <b>neues <code>Vektor</code>-Objekt mit demselben Zustan</b>.
*
* @return Neue Instanz von <code>Vektor</code> mit den selben Koordinaten (x, y)
*/
@Override
public Vektor clone () {
return new Vektor(x, y);
}
/**
* Gibt die String-Repräsentation dieses Objektes aus.
*
* @return String-Repräsentation dieses Vektors
*/
@Override
public String toString () {
return "ea.Vektor [x = " + x + "; y = " + y + "]";
}
/**
* Gibt die <code>x</code>-Verschiebung dieses Vektors mit Ganzzahlen wider.
*
* @return Die <code>x</code>-Verschiebung dieses Vektors. Positive Werte verschieben nach
* rechts, negative Werte verschieben nach links.
*
* @see #dY()
*/
public int dX () {
return (int) x;
}
/**
* Gibt die <code>y</code>-Verschiebung dieses Vektors mit Ganzzahlen wider.
*
* @return Die <code>y</code>-Verschiebung dieses Vektors. Positive Werte verschieben nach
* unten, negative Werte verschieben nach oben.
*
* @see #dX()
*/
public int dY () {
return (int) y;
}
/**
* Gibt diesen Ortsvektor vom Ursprung der Zeichenebene als Punkt aus.<br> Dieser hat die exakt
* selben <code>x</code>- / <code>y</code>-Komponenten. Das bedeutet:<br> <code> Vektor v = new
* Vektor (10, 20);<br />Punkt p = v.alsPunkt(); -> p == new Punkt(10, 20); </code>
*
* @return Ortsvektor dieses Punktes
*
* @see Punkt#alsVektor()
*/
@API
@SuppressWarnings ( "unused" )
public Punkt alsPunkt () {
return new Punkt(x, y);
}
/**
* Gibt die Manhattan-Länge des Vektors zurück. Diese ist für v=(a,b) definiert
* als a+b .
* @return Die Summe von delta X und delta Y des Vektors.
*/
public float manhattanLength() {
float v = x+y;
return v < 0 ? -v : v;
}
}