/**
* This file is part of aion-emu <aion-emu.com>.
*
* aion-emu 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
* (at your option) any later version.
*
* aion-emu 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 aion-emu. If not, see <http://www.gnu.org/licenses/>.
*/
package com.aionemu.gameserver.utils;
import java.awt.Point;
import com.aionemu.gameserver.model.gameobjects.VisibleObject;
import com.aionemu.gameserver.model.geometry.Point3D;
/**
* Class with basic math.<br>
* Thanks to: <li>
* <ul>
* http://geom-java.sourceforge.net/
* </ul>
* <ul>
* http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/DistancePoint.java
* </ul>
* </li> <br>
* <br>
* Few words about speed:
*
* <pre>
* Math.hypot(dx, dy); // Extremely slow
* Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); // 20 times faster than hypot
* Math.sqrt(dx * dx + dy * dy); // 10 times faster then previous line
* </pre>
*
* We don't need squared distances for calculations, {@linkplain Math#sqrt(double)} is very fast.<br>
* In fact the difference is very small, so it can be ignored.<br>
* Feel free to run the following test (or to find a mistake in it ^^).<br>
*
* <pre>
* import java.util.Random;
*
* public class MathSpeedTest
* {
*
* private static long time;
*
* private static long n = 100000000L;
*
* public static void main(String[] args)
* {
*
* Random r = new Random();
*
* long x;
* long y;
*
* double res = 0;
* setTime();
* for(int i = 0; i < n; i++)
* {
* x = r.nextInt();
* y = r.nextInt();
* res = Math.sqrt(x * x + y * y);
* }
* printTime();
* System.out.println(res);
*
* setTime();
* for(int i = 0; i < n; i++)
* {
* x = r.nextInt();
* y = r.nextInt();
* res = x * x + y * y;
* }
* printTime();
* System.out.println(Math.sqrt(res));
* }
*
* public static void setTime()
* {
* time = System.currentTimeMillis();
* }
*
* public static void printTime()
* {
* System.out.println(System.currentTimeMillis() - time);
* }
* }
* </pre>
*
* @author Disturbing
* @author SoulKeeper
*/
public class MathUtil
{
/**
* Returns distance between two 2D points
*
* @param point1
* first point
* @param point2
* second point
* @return distance between points
*/
public static double getDistance(Point point1, Point point2)
{
return getDistance(point1.x, point1.y, point2.x, point2.y);
}
/**
* Returns distance between two sets of coords
*
* @param x1
* first x coord
* @param y1
* first y coord
* @param x2
* second x coord
* @param y2
* second y coord
* @return distance between sets of coords
*/
public static double getDistance(int x1, int y1, int x2, int y2)
{
// using long to avoid possible overflows when multiplying
long dx = x2 - x1;
long dy = y2 - y1;
// return Math.hypot(x2 - x1, y2 - y1); // Extremely slow
// return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); // 20 times faster than hypot
return Math.sqrt(dx * dx + dy * dy); // 10 times faster then previous line
}
/**
* Returns distance between two 3D points
*
* @param point1
* first point
* @param point2
* second point
* @return distance between points
*/
public static double getDistance(Point3D point1, Point3D point2)
{
return getDistance(point1.getX(), point1.getY(), point1.getZ(), point2.getX(), point2.getY(), point2.getZ());
}
/**
* Returns distance between 3D set of coords
*
* @param x1
* first x coord
* @param y1
* first y coord
* @param z1
* first z coord
* @param x2
* second x coord
* @param y2
* second y coord
* @param z2
* second z coord
* @return distance between coords
*/
public static double getDistance(float x1, float y1, float z1, float x2, float y2, float z2)
{
float dx = x1 - x2;
float dy = y1 - y2;
float dz = z1 - z2;
// We should avoid Math.pow or Math.hypot due to perfomance reasons
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
/**
*
* @param object
* @param x
* @param y
* @param z
* @return
*/
public static double getDistance(VisibleObject object , float x, float y , float z)
{
return getDistance(object.getX(), object.getY(), object.getZ(), x, y, z);
}
/**
* Returns closest point on segment to point
*
* @param ss
* segment start point
* @param se
* segment end point
* @param p
* point to found closest point on segment
* @return closest point on segment to p
*/
public static Point getClosestPointOnSegment(Point ss, Point se, Point p)
{
return getClosestPointOnSegment(ss.x, ss.y, se.x, se.y, p.x, p.y);
}
/**
* Returns closest point on segment to point
*
* @param sx1
* segment x coord 1
* @param sy1
* segment y coord 1
* @param sx2
* segment x coord 2
* @param sy2
* segment y coord 2
* @param px
* point x coord
* @param py
* point y coord
* @return closets point on segment to point
*/
public static Point getClosestPointOnSegment(int sx1, int sy1, int sx2, int sy2, int px, int py)
{
double xDelta = sx2 - sx1;
double yDelta = sy2 - sy1;
if((xDelta == 0) && (yDelta == 0))
{
throw new IllegalArgumentException("Segment start equals segment end");
}
double u = ((px - sx1) * xDelta + (py - sy1) * yDelta) / (xDelta * xDelta + yDelta * yDelta);
final Point closestPoint;
if(u < 0)
{
closestPoint = new Point(sx1, sy1);
}
else if(u > 1)
{
closestPoint = new Point(sx2, sy2);
}
else
{
closestPoint = new Point((int) Math.round(sx1 + u * xDelta), (int) Math.round(sy1 + u * yDelta));
}
return closestPoint;
}
/**
* Returns distance to segment
*
* @param ss
* segment start point
* @param se
* segment end point
* @param p
* point to found closest point on segment
* @return distance to segment
*/
public static double getDistanceToSegment(Point ss, Point se, Point p)
{
return getDistanceToSegment(ss.x, ss.y, se.x, se.y, p.x, p.y);
}
/**
* Returns distance to segment
*
* @param sx1
* segment x coord 1
* @param sy1
* segment y coord 1
* @param sx2
* segment x coord 2
* @param sy2
* segment y coord 2
* @param px
* point x coord
* @param py
* point y coord
* @return distance to segment
*/
public static double getDistanceToSegment(int sx1, int sy1, int sx2, int sy2, int px, int py)
{
Point closestPoint = getClosestPointOnSegment(sx1, sy1, sx2, sy2, px, py);
return getDistance(closestPoint.x, closestPoint.y, px, py);
}
/**
* Checks whether two given instances of AionObject are within given range.
*
* @param object1
* @param object2
* @param range
* @return true if objects are in range, false otherwise
*/
public static boolean isInRange(VisibleObject object1, VisibleObject object2, float range)
{
if(object1.getWorldId() != object2.getWorldId())
return false;
float dx = (object2.getX() - object1.getX());
float dy = (object2.getY() - object1.getY());
return dx * dx + dy * dy < range * range;
}
/**
*
* @param obj1X
* @param obj1Y
* @param obj2X
* @param obj2Y
* @return float
*/
public final static float calculateAngleFrom(float obj1X, float obj1Y, float obj2X, float obj2Y)
{
float angleTarget = (float) Math.toDegrees(Math.atan2(obj2Y - obj1Y, obj2X - obj1X));
if (angleTarget < 0)
angleTarget = 360 + angleTarget;
return angleTarget;
}
/**
*
* @param obj1
* @param obj2
* @return float
*/
public static float calculateAngleFrom(VisibleObject obj1, VisibleObject obj2)
{
return calculateAngleFrom(obj1.getX(), obj1.getY(), obj2.getX(), obj2.getY());
}
/**
*
* @param clientHeading
* @return float
*/
public final static float convertHeadingToDegree(byte clientHeading)
{
float degree = clientHeading * 3;
return degree;
}
}