/*
Copyright 2006 by Sean Luke and George Mason University
Licensed under the Academic Free License version 3.0
See the file "LICENSE" for more information
*/
package sim.field.grid;
import sim.util.*;
import java.util.*;
/**
A concrete implementation of the Grid2D methods; used by several subclasses.
Note that you should avoid calling these methods from an object of type Grid2D; instead
try to call them from something more concrete (AbstractGrid2D or SparseGrid2D).
Otherwise they will not get inlined. For example,
<pre><tt>
Grid2D foo = ... ;
foo.tx(4); // will not get inlined
AbstractGrid2D bar = ...;
bar.tx(4); // WILL get inlined
ObjectGrid2D baz = ...; // (assuming we're an ObjectGrid2D)
baz.tx(4); // WILL get inlined
</tt></pre>
*/
public abstract class AbstractGrid2D implements Grid2D
{
private static final long serialVersionUID = 1;
// this should never change except via setTo
protected int width;
// this should never change except via setTo
protected int height;
public final int getWidth() { return width; }
public final int getHeight() { return height; }
public Map buildMap(Map other) { return new HashMap(other); }
public Map buildMap(int size)
{
if (size <= ANY_SIZE) return new HashMap();
else return new HashMap(size);
}
// slight revision for more efficiency
public final int tx(int x)
{
final int width = this.width;
if (x >= 0 && x < width) return x; // do clearest case first
x = x % width;
if (x < 0) x = x + width;
return x;
}
// slight revision for more efficiency
public final int ty(int y)
{
final int height = this.height;
if (y >= 0 && y < height) return y; // do clearest case first
y = y % height;
if (y < 0) y = y + height;
return y;
}
public final int stx(final int x)
{ if (x >= 0) { if (x < width) return x; return x - width; } return x + width; }
public final int sty(final int y)
{ if (y >= 0) { if (y < height) return y ; return y - height; } return y + height; }
public final int ulx(final int x, final int y) { return x - 1; }
public final int uly(final int x, final int y) { if ((x & 1) == 0) return y - 1; return y; }
public final int urx(final int x, final int y) { return x + 1; }
public final int ury(final int x, final int y) { if ((x & 1) == 0) return y - 1; return y; }
public final int dlx(final int x, final int y) { return x - 1; }
public final int dly(final int x, final int y) { if ((x & 1) == 0) return y ; return y + 1; }
public final int drx(final int x, final int y) { return x + 1; }
public final int dry(final int x, final int y) { if ((x & 1) == 0) return y ; return y + 1; }
public final int upx(final int x, final int y) { return x; }
public final int upy(final int x, final int y) { return y - 1; }
public final int downx(final int x, final int y) { return x; }
public final int downy(final int x, final int y) { return y + 1; }
public boolean trb(final int x, final int y) { return ((x + y) & 1) == 1; }
public boolean trt(final int x, final int y) { return ((x + y) & 1) == 0; }
// this internal version of tx is arranged to be 34 bytes. It first tries stx, then tx.
int tx(int x, int width, int widthtimestwo, int xpluswidth, int xminuswidth)
{
if (x >= -width && x < widthtimestwo)
{
if (x < 0) return xpluswidth;
if (x < width) return x;
return xminuswidth;
}
return tx2(x, width);
}
// used internally by the internal version of tx above. Do not call directly.
int tx2(int x, int width)
{
x = x % width;
if (x < 0) x = x + width;
return x;
}
// this internal version of ty is arranged to be 34 bytes. It first tries sty, then ty.
int ty(int y, int height, int heighttimestwo, int yplusheight, int yminusheight)
{
if (y >= -height && y < heighttimestwo)
{
if (y < 0) return yplusheight;
if (y < height) return y;
return yminusheight;
}
return ty2(y, height);
}
// used internally by the internal version of ty above. Do not call directly.
int ty2(int y, int height)
{
y = y % height;
if (y < 0) y = y + height;
return y;
}
protected void removeOrigin(int x, int y, IntBag xPos, IntBag yPos)
{
int size = xPos.size();
for(int i = 0; i <size; i++)
{
if (xPos.get(i) == x && yPos.get(i) == y)
{
xPos.remove(i);
yPos.remove(i);
return;
}
}
}
// only removes the first occurence
protected void removeOriginToroidal(int x, int y, IntBag xPos, IntBag yPos)
{
int size = xPos.size();
x = tx(x, width, width*2, x+width, x-width);
y = ty(y, height, height*2, y+height, y-height);
for(int i = 0; i <size; i++)
{
if (tx(xPos.get(i), width, width*2, x+width, x-width) == x && ty(yPos.get(i), height, height*2, y+height, y-height) == y)
{
xPos.remove(i);
yPos.remove(i);
return;
}
}
}
/** @deprecated */
public void getNeighborsMaxDistance( final int x, final int y, final int dist, final boolean toroidal, IntBag xPos, IntBag yPos )
{
getMooreLocations(x, y, dist, toroidal ? TOROIDAL : BOUNDED, true, xPos, yPos);
}
public void getMooreLocations( final int x, final int y, final int dist, int mode, boolean includeOrigin, IntBag xPos, IntBag yPos )
{
boolean toroidal = (mode == TOROIDAL);
boolean bounded = (mode == BOUNDED);
if (mode != BOUNDED && mode != UNBOUNDED && mode != TOROIDAL)
{
throw new RuntimeException("Mode must be either Grid2D.BOUNDED, Grid2D.UNBOUNDED, or Grid2D.TOROIDAL");
}
// won't work for negative distances
if( dist < 0 )
{
throw new RuntimeException( "Distance must be positive" );
}
if( xPos == null || yPos == null )
{
throw new RuntimeException( "xPos and yPos should not be null" );
}
if( ( x < 0 || x >= width || y < 0 || y >= height ) && !bounded)
throw new RuntimeException( "Invalid initial position" );
xPos.clear();
yPos.clear();
// local variables are faster
final int height = this.height;
final int width = this.width;
// for toroidal environments the code will be different because of wrapping arround
if( toroidal )
{
// compute xmin and xmax for the neighborhood
int xmin = x - dist;
int xmax = x + dist;
// next: is xmax - xmin humongous? If so, no need to continue wrapping around
if (xmax - xmin >= width) // too wide, just use whole neighborhood
{ xmin = 0; xmax = width - 1; }
// compute ymin and ymax for the neighborhood
int ymin = y - dist;
int ymax = y + dist;
// next: is ymax - ymin humongous? If so, no need to continue wrapping around
if (ymax - ymin >= height) // too wide, just use whole neighborhood
{ ymin = 0; ymax = width - 1; }
for( int x0 = xmin ; x0 <= xmax ; x0++ )
{
final int x_0 = tx(x0, width, width*2, x0+width, x0-width);
for( int y0 = ymin ; y0 <= ymax ; y0++ )
{
final int y_0 = ty(y0, height, height*2, y0+height, y0-height);
xPos.add( x_0 );
yPos.add( y_0 );
}
}
if (!includeOrigin) removeOriginToroidal(x,y,xPos,yPos);
}
else // not toroidal
{
// compute xmin and xmax for the neighborhood such that they are within boundaries
final int xmin = ((x-dist>=0) || !bounded ?x-dist:0);
final int xmax =((x+dist<=width-1) || !bounded ?x+dist:width-1);
// compute ymin and ymax for the neighborhood such that they are within boundaries
final int ymin = ((y-dist>=0) || !bounded ?y-dist:0);
final int ymax = ((y+dist<=height-1) || !bounded ?y+dist:height-1);
for( int x0 = xmin; x0 <= xmax ; x0++ )
{
for( int y0 = ymin ; y0 <= ymax ; y0++ )
{
xPos.add( x0 );
yPos.add( y0 );
}
}
if (!includeOrigin) removeOrigin(x,y,xPos,yPos);
}
}
/** @deprecated */
public void getNeighborsHamiltonianDistance( final int x, final int y, final int dist, final boolean toroidal, IntBag xPos, IntBag yPos )
{
getVonNeumannLocations(x, y, dist, toroidal ? TOROIDAL : BOUNDED, true, xPos, yPos);
}
public void getVonNeumannLocations( final int x, final int y, final int dist, int mode, boolean includeOrigin, IntBag xPos, IntBag yPos )
{
boolean toroidal = (mode == TOROIDAL);
boolean bounded = (mode == BOUNDED);
if (mode != BOUNDED && mode != UNBOUNDED && mode != TOROIDAL)
{
throw new RuntimeException("Mode must be either Grid2D.BOUNDED, Grid2D.UNBOUNDED, or Grid2D.TOROIDAL");
}
// won't work for negative distances
if( dist < 0 )
{
throw new RuntimeException( "Distance must be positive" );
}
if( xPos == null || yPos == null )
{
throw new RuntimeException( "xPos and yPos should not be null" );
}
if( ( x < 0 || x >= width || y < 0 || y >= height ) && !bounded)
throw new RuntimeException( "Invalid initial position" );
xPos.clear();
yPos.clear();
// local variables are faster
final int height = this.height;
final int width = this.width;
// for toroidal environments the code will be different because of wrapping arround
if( toroidal )
{
// compute xmin and xmax for the neighborhood
final int xmax = x+dist;
final int xmin = x-dist;
for( int x0 = xmin; x0 <= xmax ; x0++ )
{
final int x_0 = tx(x0, width, width*2, x0+width, x0-width);
// compute ymin and ymax for the neighborhood; they depend on the current x0 value
final int ymax = y+(dist-((x0-x>=0)?x0-x:x-x0));
final int ymin = y-(dist-((x0-x>=0)?x0-x:x-x0));
for( int y0 = ymin; y0 <= ymax; y0++ )
{
final int y_0 = ty(y0, height, height*2, y0+height, y0-height);
xPos.add( x_0 );
yPos.add( y_0 );
}
}
if (dist * 2 >= width || dist * 2 >= height) // too big, will have to remove duplicates
{
int sz = xPos.size();
Map map = buildMap(sz);
for(int i = 0 ; i < sz; i++)
{
Double2D elem = new Double2D(xPos.get(i), yPos.get(i));
if (map.containsKey(elem)) // already there
{
xPos.remove(i);
yPos.remove(i);
i--;
sz--;
}
else
{
map.put(elem, elem);
}
}
}
if (!includeOrigin) removeOriginToroidal(x,y,xPos,yPos);
}
else // not toroidal
{
// compute xmin and xmax for the neighborhood such that they are within boundaries
final int xmax = ((x+dist<=width-1) || !bounded ?x+dist:width-1);
final int xmin = ((x-dist>=0) || !bounded ?x-dist:0);
for( int x0 = xmin ; x0 <= xmax ; x0++ )
{
// compute ymin and ymax for the neighborhood such that they are within boundaries
// they depend on the curreny x0 value
final int ymax = ((y+(dist-((x0-x>=0)?x0-x:x-x0))<=height-1) || !bounded ?y+(dist-((x0-x>=0)?x0-x:x-x0)):height-1);
final int ymin = ((y-(dist-((x0-x>=0)?x0-x:x-x0))>=0) || !bounded ?y-(dist-((x0-x>=0)?x0-x:x-x0)):0);
for( int y0 = ymin; y0 <= ymax; y0++ )
{
xPos.add( x0 );
yPos.add( y0 );
}
}
if (!includeOrigin) removeOrigin(x,y,xPos,yPos);
}
}
/** @deprecated */
public void getNeighborsHexagonalDistance( final int x, final int y, final int dist, final boolean toroidal, IntBag xPos, IntBag yPos )
{
getHexagonalLocations(x, y, dist, toroidal ? TOROIDAL : BOUNDED, true, xPos, yPos);
}
public void getHexagonalLocations( final int x, final int y, final int dist, int mode, boolean includeOrigin, IntBag xPos, IntBag yPos )
{
boolean toroidal = (mode == TOROIDAL);
boolean bounded = (mode == BOUNDED);
if (mode != BOUNDED && mode != UNBOUNDED && mode != TOROIDAL)
{
throw new RuntimeException("Mode must be either Grid2D.BOUNDED, Grid2D.UNBOUNDED, or Grid2D.TOROIDAL");
}
// won't work for negative distances
if( dist < 0 )
{
throw new RuntimeException( "Distance must be positive" );
}
if( xPos == null || yPos == null )
{
throw new RuntimeException( "xPos and yPos should not be null" );
}
if( ( x < 0 || x >= width || y < 0 || y >= height ) && !bounded)
throw new RuntimeException( "Invalid initial position" );
xPos.clear();
yPos.clear();
// local variables are faster
final int height = this.height;
final int width = this.width;
if( toroidal && height%2==1 )
throw new RuntimeException( "toroidal hexagonal environment should have even heights" );
if( toroidal )
{
// compute ymin and ymax for the neighborhood
int ymin = y - dist;
int ymax = y + dist;
for( int y0 = ymin ; y0 <= ymax ; y0 = downy(x,y0) )
{
xPos.add( tx(x, width, width*2, x+width, x-width) );
yPos.add( ty(y0, height, height*2, y0+height, y0-height) );
}
int x0 = x;
for( int i = 1 ; i <= dist ; i++ )
{
final int temp_ymin = ymin;
ymin = dly( x0, ymin );
ymax = uly( x0, ymax );
x0 = dlx( x0, temp_ymin );
for( int y0 = ymin ; y0 <= ymax ; y0 = downy(x0,y0) )
{
xPos.add( tx(x0, width, width*2, x0+width, x0-width) );
yPos.add( ty(y0, height, height*2, y0+height, y0-height) );
}
}
x0 = x;
ymin = y-dist;
ymax = y+dist;
for( int i = 1 ; i <= dist ; i++ )
{
final int temp_ymin = ymin;
ymin = dry( x0, ymin );
ymax = ury( x0, ymax );
x0 = drx( x0, temp_ymin );
for( int y0 = ymin ; y0 <= ymax ; y0 = downy(x0,y0) )
{
xPos.add( tx(x0, width, width*2, x0+width, x0-width) );
yPos.add( ty(y0, height, height*2, y0+height, y0-height) );
}
}
if (dist * 2 >= width || dist * 2 >= height) // too big, will have to remove duplicates
{
int sz = xPos.size();
Map map = buildMap(sz);
for(int i = 0 ; i < sz; i++)
{
Double2D elem = new Double2D(xPos.get(i), yPos.get(i));
if (map.containsKey(elem)) // already there
{
xPos.remove(i);
yPos.remove(i);
i--;
sz--;
}
else
{
map.put(elem, elem);
}
}
}
if (!includeOrigin) removeOriginToroidal(x,y,xPos,yPos);
}
else // not toroidal
{
int ymin = y - dist;
int ymax = y + dist;
// compute ymin and ymax for the neighborhood
int ylBound = ((ymin >= 0 || !bounded) ? ymin : 0);
int yuBound = ((ymax < height || !bounded) ? ymax : height-1);
// add vertical center line of hexagon
for( int y0 = ylBound ; y0 <= yuBound ; y0 = downy(x,y0) )
{
xPos.add( x );
yPos.add( y0 );
}
// add right half of hexagon
int x0 = x;
ymin = y - dist;
ymax = y + dist;
for( int i = 1 ; i <= dist ; i++ )
{
final int temp_ymin = ymin;
ymin = dly( x0, ymin );
ymax = uly( x0, ymax );
x0 = dlx( x0, temp_ymin );
ylBound = ((ymin >= 0 || !bounded) ? ymin : 0);
yuBound = ((ymax < height || !bounded) ? ymax : height-1);
// yuBound = (( ymax<height || !bounded) ? ymax : height-1);
if( x0 >= 0 )
for( int y0 = ylBound ; y0 <= yuBound ; y0 = downy(x0,y0) )
{
if( y0 >= 0 || !bounded )
{
xPos.add( x0 );
yPos.add( y0 );
}
}
}
x0 = x;
ymin = y - dist;
ymax = y + dist;
for( int i = 1 ; i <= dist ; i++ )
{
final int temp_ymin = ymin;
ymin = dry( x0, ymin );
ymax = ury( x0, ymax );
x0 = drx( x0, temp_ymin );
ylBound = ((ymin >= 0 || !bounded) ? ymin : 0);
yuBound = ((ymax < height || !bounded) ? ymax : height-1);
// yuBound = ((ymax<height) || !bounded ?ymax:height);
if( x0 < width )
for( int y0 = ymin ; y0 <= yuBound; y0 = downy(x0,y0) )
{
if( y0 >= 0 || !bounded )
{
xPos.add( x0 );
yPos.add( y0 );
}
}
}
if (!includeOrigin) removeOrigin(x,y,xPos,yPos);
}
}
static double ds(double d1x, double d1y, double d2x, double d2y)
{
return ((d1x - d2x) * (d1x - d2x) + (d1y - d2y) * (d1y - d2y));
}
static boolean within(double d1x, double d1y, double d2x, double d2y, double distanceSquared, boolean closed)
{
double d = ds(d1x, d1y, d2x, d2y);
return (d < distanceSquared || (d == distanceSquared && closed));
}
static boolean removeForAny(int x, int y, int xp, int yp, double dist, boolean closed)
{
// above or below -- check for edges
if (x == xp)
{
if (y < yp)
{
double d = (yp - 0.5) - y;
return !(d < dist || (d == dist && closed));
}
else if (y > yp)
{
double d = -((yp - 0.5) - y);
return !(d < dist || (d == dist && closed));
}
else // y == yp // special case
{
// don't remove unless open and dist is zero, a rare case
return (dist == 0 && !closed);
}
}
// left or right -- check for edges
else if (y == yp)
{
if (x < xp)
{
double d = (xp - 0.5) - x;
return !(d < dist || (d == dist && closed));
}
else // x > xp // (x == xp checked for earlier)
{
double d = -((xp - 0.5) - x);
return !(d < dist || (d == dist && closed));
}
}
// off center -- check for nearest corner
else if (x < xp)
{
if (y < yp)
return !within(x,y,xp-0.5,yp-0.5,dist*dist,closed);
else
return !within(x,y,xp-0.5,yp+0.5,dist*dist,closed);
}
else if (x > xp)
{
if (y < yp)
return !within(x,y,xp+0.5,yp-0.5,dist*dist,closed);
else
return !within(x,y,xp+0.5,yp+0.5,dist*dist,closed);
}
else return false;
}
public void getRadialLocations( final int x, final int y, final double dist, int mode, boolean includeOrigin, IntBag xPos, IntBag yPos )
{
getRadialLocations(x, y, dist, mode, includeOrigin, Grid2D.ANY, true, xPos, yPos);
}
public void getRadialLocations( final int x, final int y, final double dist, int mode, boolean includeOrigin, int measurementRule, boolean closed, IntBag xPos, IntBag yPos )
{
boolean toroidal = (mode == TOROIDAL);
// won't work for negative distances
if( dist < 0 )
{
throw new RuntimeException( "Distance must be positive" );
}
if (measurementRule != Grid2D.ANY && measurementRule != Grid2D.ALL && measurementRule != Grid2D.CENTER)
{
throw new RuntimeException(" Measurement rule must be one of ANY, ALL, or CENTER" );
}
// grab the rectangle
if (toroidal)
getMooreLocations(x,y, (int) Math.ceil(dist + 0.5), UNBOUNDED, includeOrigin, xPos, yPos);
else
getMooreLocations(x,y, (int) Math.ceil(dist + 0.5), mode, includeOrigin, xPos, yPos);
int len = xPos.size();
double distsq = dist * dist;
int width = this.width;
int height = this.height;
int widthtimestwo = width * 2;
int heighttimestwo = height * 2;
for(int i = 0; i < len; i++)
{
int xp = xPos.get(i);
int yp = yPos.get(i);
boolean remove = false;
if (measurementRule == Grid2D.ANY)
{
remove = AbstractGrid2D.removeForAny(x, y, xp, yp, dist, closed);
}
else if (measurementRule == Grid2D.ALL)
{
// must check if ALL corners lie within the boundary. However
// we can reduce it to just specific corners depending on the relative
// positions of the coordinates
if (x < xp)
{
if (y < yp)
remove = !within(x,y,xp+0.5,yp+0.5,distsq,closed);
else // y >= yp. y==yp is included because only one corner must be checked -- grid points are centered in their squares
remove = !within(x,y,xp+0.5,yp-0.5,distsq,closed);
}
else // x >= xp. x==xp is included because only one corner must be checked -- grid points are centered in their squares
{
if (y < yp)
remove = !within(x,y,xp-0.5,yp+0.5,distsq,closed);
else // y >= yp. y==yp is included because only one corner must be checked -- grid points are centered in their squares
remove = !within(x,y,xp-0.5,yp-0.5,distsq,closed);
}
}
else // (measurementRule == Grid2D.CENTER)
{
remove = !within(x,y,xp,yp,distsq,closed);
}
if (remove)
{ xPos.remove(i); yPos.remove(i); i--; len--; }
else if (toroidal) // need to convert to toroidal position
{
int _x = xPos.get(i);
int _y = yPos.get(i);
xPos.set(i, tx(_x, width, widthtimestwo, _x + width, _x - width));
yPos.set(i, tx(_y, height, heighttimestwo, _y + width, _y - width));
}
}
}
protected void checkBounds(Grid2D other)
{
if (getHeight() != other.getHeight() || getWidth() != other.getWidth())
throw new IllegalArgumentException("Grids must be the same dimensions.");
}
/*
double dxForRadius(double dy, double radius) // may return NaN
{
return Math.sqrt(radius*radius - dy*dy);
}
// still in real-valued space
double dxForAngle(double dy, double xa, double ya)
{
if (ya == 0 && dy == 0) // horizontal line, push dx way out to the far edges of space, even if we're not in line with it
{ return xa > 0 ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY ; }
else if ((dy >= 0 && ya >= 0) || (dy <= 0 && ya <= 0)) // same side
{
// dx : dy :: xa : ya
return (xa * dy) / ya;
}
else return Double.NaN;
}
// a is next to b (<) and c and d are not between them
boolean nextTo(double a, double b, double c, double d)
{
return (a <= b &&
!(c >= a && c <= b) &&
!(d >= a && d <= b));
}
int pushLeft(double x, double y, double slope, double radiusSq)
{
System.err.println("<---- " + x + " " + y + " " + slope);
double xa = x;
double ya = y;
int xi = (int)xa;
int yi = (int)ya;
while(
((slope >= 0 && xi * slope >= yi) ||
(slope < 0 && xi * slope < (yi + 1))) &&
xi * xi + (xi * slope) * (xi * slope) < radiusSq)
{
xi--;
}
return xi;
}
int pushRight(double x, double y, double slope, double radiusSq) // radius limits our distance
{
System.err.println("----> " + x + " " + y + " " + slope);
double xa = x;
double ya = y;
int xi = (int)xa;
int yi = (int)ya;
while(
((slope <= 0 && (xi + 1) * slope >= yi) ||
(slope > 0 && (xi + 1) * slope < (yi + 1))) &&
(xi + 1) * (xi + 1) * ((xi + 1) * slope) * ((xi + 1) * slope) < radiusSq)
{
xi++;
}
return xi;
}
boolean scan(int x, int y, int dy, double radius, double xa, double ya, double xb, double yb,
boolean crossesZero, boolean crossesPi, boolean toroidal, IntBag xPos, IntBag yPos)
{
// too high?
if (dy > radius || dy < -radius) return false;
double r = dxForRadius(dy, radius);
double l = - r;
double s = dxForAngle(dy, xa, ya);
double e = dxForAngle(dy, xb, yb);
int min = 0;
int max = 0;
System.err.println("dy=" + dy + " radius=" + radius + " zero=" + crossesZero + " Pi=" + crossesPi);
System.err.println("xa " + xa + " ya " + ya + " xb " + xb + " yb " + yb);
System.err.println("r " + r + " l " + l + " s " + s + " e " + e);
//if (dy == 0) // special case dy == 0
// {
// if (crossesPi) min = (int) Math.round(l);
// if (crossesZero) max = (int) Math.round(r);
// System.err.println("MIN " + min + " MAX " + max);
// if (toroidal)
// for(int i = min; i <= max; i++) { xPos.add(stx(x + i)); yPos.add(sty(y + dy)); }
// else
// for(int i = min; i <= max; i++) { xPos.add(x + i); yPos.add(y + dy); }
// return true;
// }
// else
if ((s==Double.POSITIVE_INFINITY && e == Double.POSITIVE_INFINITY) || (s == Double.NEGATIVE_INFINITY && e==Double.NEGATIVE_INFINITY)) // straight line in the same direction, handle specially
{ return false; }
else if (dy >= 0)
{
if (l!=l) // NaN, signifies out of bounds
return false;
// six cases: (L = negativeDXForRadius, R = positiveDXForRadius, S = start (Xa, Ya), E = end(Xb, Yb)
if (s == e) // line
{
System.err.println("S==E");
if (s * s + dy * dy > radius * radius) return false;
min = pushLeft(s, dy, ya / xa, radius * radius);
max = pushRight(s, dy, ya / xa, radius * radius);
}
// L S E R
else if (l <= s && s <= e && e <= r)
{
System.err.println("LSER");
// draw first line
min = (int)Math.floor(l); max = (int)Math.ceil(s);
if (toroidal)
for(int i = min; i <= max; i++) { xPos.add(stx(x + i)); yPos.add(sty(y + dy)); }
else
for(int i = min; i <= max; i++) { xPos.add(x + i); yPos.add(y + dy); }
// set up second line
min = (int)Math.floor(e); max = (int)Math.ceil(r);
}
// L R
else if ((e <= l && l <= r && r <= s) ||
(l <= r && r <= s && s <= e))
// nextTo(l, r, e, s) && e <= s) // end must be less than start for this to be valid
{
System.err.println("LR");
min = (int)Math.floor(l); max = (int)Math.ceil(r);
}
// E S
else if //(nextTo(e, s, l, r))
(
(l <= e && e <= s && s <= r)
//||
//(l <= e && e <= s && r <= e) ||
//(l >= e && e <= s && r >= e)
)
{
System.err.println("ES");
min = (int)Math.floor(e); max = (int)Math.ceil(s);
}
// E R
else if (nextTo(e, r, l, s))
{
System.err.println("ER");
min = (int)Math.floor(e); max = (int)Math.ceil(r);
}
// L S
else if (nextTo(l, s, e, r))
{
System.err.println("LS");
min = (int)Math.floor(l); max = (int)Math.ceil(s);
}
else return false;
System.err.println("MIN " + min + " MAX " + max);
// draw line
if (toroidal)
for(int i = min; i <= max; i++) { xPos.add(stx(x + i)); yPos.add(sty(y + dy)); }
else
for(int i = min; i <= max; i++) { xPos.add(x + i); yPos.add(y + dy); }
return true;
}
else // (dy < 0)
{
if (l!=l) // out of bounds
return false;
//double s = dxForAngle(dy, xa, ya);
//double e = dxForAngle(dy, xb, yb);
// five cases: (L = negativeDXForRadius, R = positiveDXForRadius, S = start (Xa, Ya), E = end(Xb, Yb)
// L E S R
if (l <= e && e <= s && s <= r)
{
// draw first line
min = (int)Math.round(l); max = (int)Math.round(e);
if (toroidal)
for(int i = min; i <= max; i++) { xPos.add(stx(x + i)); yPos.add(sty(y + dy)); }
else
for(int i = min; i <= max; i++) { xPos.add(x + i); yPos.add(y + dy); }
// set up second line
min = (int)Math.round(s); max = (int)Math.round(r);
}
// L R
else if (nextTo(l, r, e, s) && s <= e) // end must be greater than start for this to be valid
{
min = (int)Math.round(l); max = (int)Math.round(r);
}
// S E
else if (nextTo(s, e, l, r))
{
min = (int)Math.round(s); max = (int)Math.round(e);
}
// L E
else if (nextTo(l, e, r, s))
{
min = (int)Math.round(l); max = (int)Math.round(e);
}
// S R
else if (nextTo(s, r, l, e))
{
min = (int)Math.round(s); max = (int)Math.round(r);
}
else return false;
// draw line
if (toroidal)
for(int i = min; i <= max; i++) { xPos.add(stx(x + i)); yPos.add(sty(y)); }
else
for(int i = min; i <= max; i++) { xPos.add(x + i); yPos.add(y); }
return true;
}
}
//
// public void getNeighborsWithinArc(int x, int y, double radius, double startAngle, double endAngle, IntBag xPos, IntBag yPos)
// { getNeighborsWithinArc(x,y,radius,startAngle,endAngle,false,xPos,yPos); }
//
// public void getNeighborsWithinArc(int x, int y, double radius, double startAngle, double endAngle, boolean toroidal, IntBag xPos, IntBag yPos)
// {
// if (radius < 0)
// throw new RuntimeException("Radius must be positive");
// xPos.clear();
// yPos.clear();
//
// // move angles into [0...2 PI)
// if (startAngle < 0) startAngle += Math.PI * 2;
// if (startAngle < 0) startAngle = ((startAngle % Math.PI * 2) + Math.PI * 2);
// if (startAngle >= Math.PI * 2) startAngle = startAngle % Math.PI * 2;
//
// if (endAngle < 0) endAngle += Math.PI * 2;
// if (endAngle < 0) endAngle = ((endAngle % Math.PI * 2) + Math.PI * 2);
// if (endAngle >= Math.PI * 2) endAngle = endAngle % Math.PI * 2;
//
// // compute slopes -- avoid atan2
// double xa = Math.cos(startAngle);
// double ya = Math.sin(startAngle);
// double xb = Math.cos(endAngle);
// double yb = Math.sin(endAngle);
//
// // compute crossings
// boolean crossesZero = false;
// boolean crossesPi = false;
// if (startAngle > endAngle || startAngle == 0 || endAngle == 0) // crosses zero for sure
// crossesZero = true;
// else if (startAngle <= Math.PI && endAngle >= Math.PI)
// crossesPi = true;
//
// // scan up
// int dy = 0;
// while(scan(x, y, dy, radius, xa, ya, xb, yb, crossesZero, crossesPi, toroidal, xPos, yPos))
// dy++;
// // scan down (not including zero)
// dy = -1;
// while(scan(x, y, dy, radius, xa, ya, xb, yb, crossesZero, crossesPi, toroidal, xPos, yPos))
// dy--;
// }
*/
}