import java.awt.geom.*;
import java.util.*;
public class PreciseUtils{
//high speed test to determine if the full method should be run this tick
public static boolean intersects(Point2D.Double botLocation, Wave wave){
double[] distSq = new double[]{
wave.fireLocation.distanceSq(botLocation.x - 18, botLocation.y + 18),
wave.fireLocation.distanceSq(botLocation.x + 18, botLocation.y + 18),
wave.fireLocation.distanceSq(botLocation.x + 18, botLocation.y - 18),
wave.fireLocation.distanceSq(botLocation.x - 18, botLocation.y - 18)};
//faster? I'm not sure
// Arrays.sort(distSq);
// return (sqr(wave.distanceTraveled + bulletVelocity) > distSq[0]
// && sqr(wave.distanceTraveled) < distSq[3]);
return (sqr(wave.distanceTraveled) >
Math.min(Math.min(distSq[0], distSq[1]), Math.min(distSq[2],distSq[3]))
&& sqr(wave.distanceTraveled - wave.bulletVelocity) < Math.max(Math.max(distSq[0], distSq[1]), Math.max(distSq[2],distSq[3])));
}
public static double[] getIntersectionRange(Point2D.Double botLocation, Wave wave){
double[] yBounds = new double[]{botLocation.y - 18, botLocation.y + 18};
double[] xBounds = new double[]{botLocation.x - 18, botLocation.x + 18};
double[] radii = new double[]{wave.distanceTraveled, wave.distanceTraveled - wave.bulletVelocity};
ArrayList<Point2D.Double> intersects = new ArrayList<Point2D.Double>();
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++){
Point2D.Double[] testPoints = vertIntersect(wave.fireLocation.x, wave.fireLocation.y, radii[i], xBounds[j]);
for(int k = 0; k < testPoints.length; k++)
if(inBounds(testPoints[k].y, yBounds))
intersects.add(testPoints[k]);
}
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++){
Point2D.Double[] testPoints = horizIntersect(wave.fireLocation.x, wave.fireLocation.y, radii[i], yBounds[j]);
for(int k = 0; k < testPoints.length; k++)
if(inBounds(testPoints[k].x, xBounds))
intersects.add(testPoints[k]);
}
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++){
Point2D.Double testCorner = new Point2D.Double(xBounds[i], yBounds[j]);
double distSq = testCorner.distanceSq(wave.fireLocation);
if(distSq < sqr(radii[0]) && distSq > sqr(radii[1]))
intersects.add(testCorner);
}
double antiClockAngle = Double.POSITIVE_INFINITY;
double clockAngle = Double.NEGATIVE_INFINITY;
double absBearing = angle(wave.fireLocation, botLocation);
for(Point2D.Double p: intersects){
double angDiff = fastRelativeAngle(angle(wave.fireLocation,p) - absBearing);
if(angDiff > clockAngle)
clockAngle = angDiff;
if(angDiff < antiClockAngle)
antiClockAngle = angDiff;
}
return new double[]{fastAbsoluteAngle(antiClockAngle + absBearing), fastAbsoluteAngle(clockAngle + absBearing)};
}
static boolean inBounds(double q,double[] xBounds){
return q >= bounds[0] && q <= bounds[1] ;
}
//assumes between -PI*2 and PI*2
public static double fastRelativeAngle(double angle) {
return angle<-Math.PI?angle+Math.PI*2:angle>Math.PI?angle-Math.PI*2:angle;
}
//assumes between -PI*2 and PI*4
public static double fastAbsoluteAngle(double angle){
return angle > Math.PI*2 ? angle - Math.PI*2 : angle < 0? angle + Math.PI*2 : angle;
}
static Point2D.Double[] vertIntersect(double centerX, double centerY, double r, double intersectX){
double deltaX = centerX - intersectX;
double sqrtVal = r*r - deltaX*deltaX;
if(sqrtVal < 0)
return new Point2D.Double[]{};
// commented out because having this decision would
// take more execution time than the rare occurence
// that a perfect 0 discriminant would save, and
// the code downstream can deal with it anyways
// if(sqrtVal == 0)
// return new Point2D.Double[]{
// new Point2D.Double(intersectX, centerY)};
sqrtVal = Math.sqrt(sqrtVal);
return new Point2D.Double[]{
new Point2D.Double(intersectX, centerY + sqrtVal),
new Point2D.Double(intersectX, centerY - sqrtVal)};
}
static Point2D.Double[] horizIntersect(double centerX, double centerY, double r, double intersectY){
double deltaY = centerY - intersectY;
double sqrtVal = r*r - deltaY*deltaY;
if(sqrtVal < 0)
return new Point2D.Double[]{};
if(sqrtVal == 0)
return new Point2D.Double[]{
new Point2D.Double(centerX, intersectY)};
sqrtVal = Math.sqrt(sqrtVal);
return new Point2D.Double[]{
new Point2D.Double(centerX + sqrtVal, intersectY),
new Point2D.Double(centerX - sqrtVal, intersectY),};
}
public static double sqr(double d){
return d*d;}
public static double angle(Point2D.Double source, Point2D.Double target) {
return Math.atan2(target.x - source.x, target.y - source.y);
}
public class Wave{
double distanceTraveled;
double bulletVelocity;
Point2D.Double fireLocation;
}
}
Hmm, nice. This looks like a pretty solid implementation to me, and at a glance looks like it would probably notably quicker than mine. I'm quiet curious to see what affect this will have on Druss's targeting ability :-) -- Rednaxela
I've actually made a couple small changes, which would mostly affect the execution speed. I'm curious how you layered all the ranges on top of each other to find a firing angle. Is the methodology anything similar to this?
GFRange[] ranges = new GFRange[cluster.size()];
double[] indices = new double[ranges.length*2];
for(int i = 0; i < ranges.length; i++){
ranges[i] = iterator.next();
indices[i*2] = ranges[i].min;
indices[i*2 + 1] = ranges[i].max;
}
Arrays.sort(indices);
int[] indicesScores = new int[indices.length - 1];
for(int i = 0; i < ranges.length; i++){
int minIndex = Arrays.binarySearch(indices,ranges[i].min);
int maxIndex = Arrays.binarySearch(indices,ranges[i].max);
while(minIndex<maxIndex)
indicesScores[minIndex++]++;
}
int maxIndex = indicesScores.length/2;
for(int i = 0; i < indicesScores.length; i++){
if(indicesScores[i] > indicesScores[maxIndex])
maxIndex = i;
}
double fireGF = (indices[maxIndex] + indices[maxIndex + 1])/2;
return fireGF*MEA*lateralDirection;
-- Skilgannon