001package fr.aumgn.bukkitutils.geom;
002
003import java.util.Iterator;
004
005import org.apache.commons.lang.builder.HashCodeBuilder;
006import org.bukkit.Location;
007import org.bukkit.World;
008import org.bukkit.block.Block;
009import org.bukkit.entity.Entity;
010
011import fr.aumgn.bukkitutils.geom.direction.Vector2DDirection;
012import fr.aumgn.bukkitutils.geom.vector.Vector2DIterator;
013
014/**
015 * Immutable Vector2D class.
016 * Inspired from WorldEdit.
017 */
018public class Vector2D implements Iterable<Vector2D> {
019
020    private final double x, z;
021
022    public Vector2D() {
023        this.x = 0;
024        this.z = 0;
025    }
026
027    public Vector2D(int x, int z) {
028        this.x = x;
029        this.z = z;
030    }
031
032    public Vector2D(double x, double z) {
033        this.x = x;
034        this.z = z;
035    }
036
037    public Vector2D(Location loc) {
038        this(loc.getX(), loc.getZ());
039    }
040
041    public Vector2D(Entity entity) {
042        this(entity.getLocation());
043    }
044
045    public Vector2D(Block block) {
046        this(block.getX(), block.getZ());
047    }
048
049    public double getX() {
050        return x;
051    }
052
053    public int getBlockX() {
054        return (int) Math.round(x);
055    }
056
057    public double getZ() {
058        return z;
059    }
060
061    public int getBlockZ() {
062        return (int) Math.round(z);
063    }
064
065    public Vector2D setX(double x) {
066        return new Vector2D(x, z);
067    }
068
069    public Vector2D setZ(double z) {
070        return new Vector2D(x, z);
071    }
072
073    public Vector2D add(double i) {
074        return new Vector2D(this.x + i, this.z + i);
075    }
076
077    public Vector2D add(double ox, double oz) {
078        return new Vector2D(x + ox, z + oz);
079    }
080
081    public Vector2D add(Vector2D other) {
082        return new Vector2D(x + other.x, z + other.z);
083    }
084
085    public Vector2D addX(double ox) {
086        return new Vector2D(x + ox, z);
087    }
088
089    public Vector2D addZ(double oz) {
090        return new Vector2D(x, z + oz);
091    }
092
093    public Vector2D subtract(double i) {
094        return new Vector2D(x - i, z - i);
095    }
096
097    public Vector2D subtract(double ox, double oz) {
098        return new Vector2D(x - ox, z - oz);
099    }
100
101    public Vector2D subtract(Vector2D other) {
102        return new Vector2D(x - other.x, z - other.z);
103    }
104
105    public Vector2D subtractX(double ox) {
106        return new Vector2D(x - ox, z);
107    }
108
109    public Vector2D subtractZ(double oz) {
110        return new Vector2D(x, z - oz);
111    }
112
113    public Vector2D multiply(double i) {
114        return new Vector2D(x * i, z * i);
115    }
116
117    public Vector2D multiply(double ox, double oz) {
118        return new Vector2D(x * ox, z * oz);
119    }
120
121    public Vector2D multiply(Vector2D other) {
122        return new Vector2D(x * other.x, z * other.z);
123    }
124
125    public Vector2D divide(double i) {
126        return new Vector2D(x / i, z / i);
127    }
128
129    public Vector2D divide(double ox, double oz) {
130        return new Vector2D(x / ox, z / oz);
131    }
132
133    public Vector2D divide(Vector2D other) {
134        return new Vector2D(x / other.x, z / other.z);
135    }
136
137    public Vector2D getMiddle(Vector2D other) {
138        return new Vector2D(
139                (x + other.x) / 2,
140                (z + other.z) / 2);
141    }
142
143    public boolean isInside(Vector2D min, Vector2D max) {
144        return x >= min.x && x <= max.x
145                && z >= min.z && z <= max.z;
146    }
147
148    public boolean isZero() {
149        return x == 0.0 && z == 0;
150    }
151
152    public Vector2D positive() {
153        return new Vector2D(Math.abs(x), Math.abs(z));
154    }
155
156    public double lengthSq() {
157        return x * x + z * z;
158    }
159
160    public double length() {
161        return Math.sqrt(lengthSq());
162    }
163
164    public double distanceSq(Vector2D other) {
165        return subtract(other).lengthSq();
166    }
167
168    public double distance(Vector2D other) {
169        return subtract(other).length();
170    }
171
172    public Vector2D rotate90() {
173        return new Vector2D(-z, x);
174    }
175
176    public Vector2D normalize() {
177        return divide(length());
178    }
179
180    public Direction toDirection() {
181        if (isZero()) {
182            return Direction.NONE;
183        }
184
185        return new Vector2DDirection(this);
186    }
187
188    public Direction towards(Vector2D to) {
189        return to.subtract(this).toDirection();
190    }
191
192    public Vector to3D() {
193        return new Vector(x, 0, z);
194    }
195
196    public Vector to3D(double y) {
197        return new Vector(x, y, z);
198    }
199
200    public Vector toHighest(World world) {
201        int y = world.getHighestBlockYAt(
202                getBlockX(), getBlockZ());
203        return to3D(y);
204    }
205
206    @Override
207    public Iterator<Vector2D> iterator() {
208        return new Vector2DIterator(new Vector2D(), this);
209    }
210
211    public Iterable<Vector2D> rectangle(final Vector2D max) {
212        return new Iterable<Vector2D>() {
213            @Override
214            public Iterator<Vector2D> iterator() {
215                return new Vector2DIterator(Vector2D.this, max);
216            }
217        };
218    }
219
220    @Override
221    public String toString() {
222        return "(" + x + ", " + + z + ")";
223    }
224
225    @Override
226    public int hashCode() {
227        return new HashCodeBuilder(29, 13)
228                .append(x)
229                .append(z)
230                .toHashCode();
231    }
232
233    @Override
234    public boolean equals(Object obj) {
235        if (this == obj) {
236            return true;
237        }
238
239        if (!(obj instanceof Vector2D)) {
240            return false;
241        }
242
243        Vector2D o = (Vector2D) obj;
244        if (Double.doubleToLongBits(x) != Double.doubleToLongBits(o.x)
245                || Double.doubleToLongBits(z) != Double.doubleToLongBits(o.z)) {
246            return false;
247        }
248
249        return true;
250    }
251
252    public boolean equalsBlock(Object obj) {
253        if (this == obj) {
254            return true;
255        }
256
257        if (!(obj instanceof Vector2D)) {
258            return false;
259        }
260
261        Vector2D other = (Vector2D) obj;
262        if (getBlockX() != other.getBlockX()
263                || getBlockZ() != other.getBlockZ()) {
264            return false;
265        }
266
267        return true;
268    }
269}