/* * Aphelion * Copyright (c) 2013 Joris van der Wel * * This file is part of Aphelion * * Aphelion is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, version 3 of the License. * * Aphelion 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 Affero General Public License * along with Aphelion. If not, see <http://www.gnu.org/licenses/>. * * In addition, the following supplemental terms apply, based on section 7 of * the GNU Affero General Public License (version 3): * a) Preservation of all legal notices and author attributions * b) Prohibition of misrepresentation of the origin of this material, and * modified versions are required to be marked in reasonable ways as * different from the original version (for example by appending a copyright notice). * * Linking this library statically or dynamically with other modules is making a * combined work based on this library. Thus, the terms and conditions of the * GNU Affero General Public License cover the whole combination. * * As a special exception, the copyright holders of this library give you * permission to link this library with independent modules to produce an * executable, regardless of the license terms of these independent modules, * and to copy and distribute the resulting executable under terms of your * choice, provided that you also meet, for each linked independent module, * the terms and conditions of the license of that module. An independent * module is a module which is not derived from or based on this library. */ package aphelion.shared.physics.valueobjects; import aphelion.shared.physics.EnvironmentConf; import aphelion.shared.swissarmyknife.SwissArmyKnife; import javax.annotation.Nonnull; /** * * @author JoWie * */ public final class PhysicsPoint { public static final PhysicsPoint ZERO = new PhysicsPoint(0, 0); // do not modify me public int x = 0; public int y = 0; public boolean set = false; public PhysicsPoint() { this.x = 0; this.y = 0; set = false; } public PhysicsPoint(int x2, int y2) { this.x = x2; this.y = y2; set = true; } public PhysicsPoint(@Nonnull PhysicsPoint other) { set(other); } public void enforceOverflowLimit() { x = SwissArmyKnife.clip(x, -EnvironmentConf.MAX_POSITION, EnvironmentConf.MAX_POSITION); y = SwissArmyKnife.clip(y, -EnvironmentConf.MAX_POSITION, EnvironmentConf.MAX_POSITION); } public void unset() { set = false; x = 0; y = 0; } @Override public boolean equals(Object obj) { if (obj != null && obj instanceof PhysicsPoint) { return this.equals((PhysicsPoint) obj); } return false; } public boolean equals(PhysicsPoint other) { if (!this.set && !other.set) { return true; } return this.x == other.x && this.y == other.y; } public boolean equals(int x, int y) { return this.x == x && this.y == y; } @Override public int hashCode() { int hash = 5; if (set) { hash = 17 * hash + this.x; hash = 17 * hash + this.y; } return hash; } @Override public String toString() { if (set) { return "(" + x + "," + y + ")"; } else { return "(unset)"; } } public void set(@Nonnull PhysicsPoint other) { this.x = other.x; this.y = other.y; this.set = other.set; } public void set(int x, int y) { this.x = x; this.y = y; this.set = true; } public int getX() { return this.x; } public int getY() { return this.y; } public void add(@Nonnull PhysicsPoint other) { addX(other.x); addY(other.y); } public void add(int n) { addX(n); addY(n); } public void addX(int x) { this.x = this.x + x; set = true; } public void addY(int y) { this.y = this.y + y; set = true; } public void sub(@Nonnull PhysicsPoint other) { subX(other.x); subY(other.y); } public void sub(int x, int y) { subX(x); subY(y); } public void sub(int n) { subX(n); subY(n); } public void subX(int x) { this.x = this.x - x; set = true; } public void subY(int y) { this.y = this.y - y; set = true; } public long lengthSquared() { // int32 * int32 always fits in int64 return (long) this.x * (long) this.x + (long) this.y * (long) this.y; } public long distanceSquared(@Nonnull PhysicsPoint other) { return distanceSquared(other.x, other.y); } public long distanceSquared(int otherX, int otherY) { // int32 * int32 always fits in int64 otherX -= this.x; otherY -= this.y; return (long) otherX * (long) otherX + (long) otherY * (long) otherY; } public long length() { return SwissArmyKnife.hypot(this.x, this.y); } public long length(long lengthSq) { return SwissArmyKnife.hypot(this.x, this.y, lengthSq); } public long distance(@Nonnull PhysicsPoint other) { return SwissArmyKnife.hypot(other.x - this.x, other.y - this.y); } public long distance(@Nonnull PhysicsPoint other, long distSq) { return SwissArmyKnife.hypot(other.x - this.x, other.y - this.y, distSq); } /** * Integer division. 5 / 2 = 2 -5 / 2 = -2 */ public void divide(int number) { this.x /= number; this.y /= number; set = true; } public void divideFloor(int number) { this.x = SwissArmyKnife.divideFloor(this.x, number); this.y = SwissArmyKnife.divideFloor(this.y, number); set = true; } public void divideCeil(int number) { this.x = SwissArmyKnife.divideCeil(this.x, number); this.y = SwissArmyKnife.divideCeil(this.y, number); set = true; } /** Divides by rounding up. In such a way that: * x = x / number * x = (x < 0) ? floor(x) : ceil(x) * */ public void divideUp(int number) { this.x = SwissArmyKnife.divideUp(this.x, number); this.y = SwissArmyKnife.divideUp(this.y, number); set = true; } public void multiply(int number) { this.x = this.x * number; this.y = this.y * number; set = true; } public void negate() { negateX(); negateY(); set = true; } public void negateX() { this.x = -this.x; } public void negateY() { this.y = -this.y; } public void abs() { this.x = SwissArmyKnife.abs(this.x); this.y = SwissArmyKnife.abs(this.y); set = true; } public boolean isZero() { return !this.set || (this.x == 0 && this.y == 0); } public long dotProduct(@Nonnull PhysicsPoint other) { return (long)x * other.x + (long)y * other.y; } public long crossProduct(@Nonnull PhysicsPoint other) { return (long)x * other.y - (long)y * other.x; } public void limitLength(int limit) { set = true; if (limit == 0) { this.x = 0; this.y = 0; return; } long lim = limit; long lengthSquared = this.lengthSquared(); long limitSquared = lim * lim; if (lengthSquared <= limitSquared) { // bail without doing a slow square root return; } // Note. StrictMath.sqrt is usually faster, however determinism // is more important (not all languages support strictfp). long length = SwissArmyKnife.hypot(x, y, lengthSquared); if (length == 0) { this.x = 0; this.y = 0; return; } else { this.x = (int) ((this.x * lim) / length); this.y = (int) ((this.y * lim) / length); } // verify lengthSquared = this.lengthSquared(); if (lengthSquared > limitSquared) { // move both x and y towards 0 // so that the new length is always under the limit this.x += this.x > 0 ? -1 : 1; this.y += this.y > 0 ? -1 : 1; } } public void setLength(int desiredLength) { set = true; if (desiredLength == 0) { this.x = 0; this.y = 0; return; } long len = desiredLength; long lengthSquared = this.lengthSquared(); // Note. StrictMath.sqrt is usually faster, however determinism // is more important (not all languages support strictfp). long length = SwissArmyKnife.hypot(x, y, lengthSquared); if (length == 0) { this.x = 0; this.y = 0; } else { this.x = (int) ((this.x * len) / length); this.y = (int) ((this.y * len) / length); } } public void applyRatio(int fraction, int consequent) { this.x = (int) (this.x * (long) fraction / consequent); this.y = (int) (this.y * (long) fraction / consequent); } public void clip(PhysicsPoint min, PhysicsPoint max) { if (min != null) { if (this.x < min.x) { this.x = min.x; } if (this.y < min.y) { this.y = min.y; } } if (max != null) { if (this.x > max.x) { this.x = max.x; } if (this.y > max.y) { this.y = max.y; } } } /** * Throw if not equal * @param x * @param y * @throws AssertionError */ public void assertEquals(int x, int y) { if (!this.set) { throw new AssertionError("expected point:<" + x + "," + y + "> but was:<unset>"); } if (x != this.x || y != this.y) { throw new AssertionError("expected point:<" + x + "," + y + "> but was:<" + this.x + "," + this.y + ">"); } } }