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