52
« on: December 03, 2011, 10:33:31 PM »
Here's the abstract class that they both implement. It does a little post-processing on the concrete-class results to fix visual artifacts.
Edit: Fixed a bug in the second post-process filter (the one that makes corners appear).
/*
* Copyright (c) 2011, L. Adamson / Dizzy Dragon Games. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* We'd love to hear from you if you're using this software for something or
* have any questions or comments. You can contact us at via email at
* support@dizzydragon.net.
*/
package retrocrawl.lib.fov;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
abstract public class FieldOfView2d implements Serializable
{
private static final long serialVersionUID = 1L;
final public static class FieldOfView2dCoordinate implements Serializable
{
private static final long serialVersionUID = 1L;
private int x;
private int y;
/**
* Instantiates a new FieldOfView2dCoordinate with the specified x and y values.
*
* @param x
* the x coordinate
* @param y
* the y coordinate
*/
public FieldOfView2dCoordinate( int x, int y )
{
this.x = x;
this.y = y;
}
/**
* @return the x value of this coordinate.
*/
public int x()
{
return x;
}
/**
* @return the y value of this coordinate.
*/
public int y()
{
return y;
}
@Override
public int hashCode()
{
return x ^ Integer.reverse( y );
}
@Override
public boolean equals( Object o )
{
if( o instanceof FieldOfView2dCoordinate )
return ( (FieldOfView2dCoordinate)o ).x == x && ( (FieldOfView2dCoordinate)o ).y == y;
return false;
}
@Override
public String toString()
{
return x + "," + y;
}
}
private boolean requiresPostProcessing = false;
final private HashMap<FieldOfView2dCoordinate,Boolean> opaqueCells = new HashMap<FieldOfView2dCoordinate,Boolean>();
final private HashMap<FieldOfView2dCoordinate,Boolean> cellContainsData = new HashMap<FieldOfView2dCoordinate,Boolean>();
protected FieldOfView2d( boolean requiresPostProcessing )
{
this.requiresPostProcessing = requiresPostProcessing;
}
abstract protected Set<FieldOfView2dCoordinate> getFieldOfViewFromImpl( FieldOfView2dCoordinate origin, int radius );
abstract protected boolean cellIsOpaqueImpl( FieldOfView2dCoordinate coordinate );
abstract protected boolean cellContainsDataImpl( FieldOfView2dCoordinate coordinate );
final protected boolean cellIsOpaque( int x, int y )
{
return cellIsOpaque( new FieldOfView2dCoordinate(x,y) );
}
final protected boolean cellIsOpaque( FieldOfView2dCoordinate coordinate )
{
Boolean isOpaque = opaqueCells.get( coordinate );
if( isOpaque == null )
{
isOpaque = cellIsOpaqueImpl( coordinate );
opaqueCells.put( coordinate, isOpaque );
}
return isOpaque;
}
final protected boolean cellContainsData( int x, int y )
{
return cellContainsData( new FieldOfView2dCoordinate(x,y) );
}
final protected boolean cellContainsData( FieldOfView2dCoordinate coordinate )
{
Boolean containsData = cellContainsData.get( coordinate );
if( containsData == null )
{
containsData = cellContainsDataImpl( coordinate );
cellContainsData.put( coordinate, containsData );
}
return containsData;
}
final public Set<FieldOfView2dCoordinate> getFieldOfViewFrom( FieldOfView2dCoordinate origin, int radius )
{
opaqueCells.clear();
cellContainsData.clear();
Set<FieldOfView2dCoordinate> results = getFieldOfViewFromImpl( origin, radius );
if( requiresPostProcessing )
{
// This filter ensures that opaque cells cardinally adjacent to
// non-opaque cells are drawn.
HashSet<FieldOfView2dCoordinate> resultsBuf = new HashSet<FieldOfView2dCoordinate>( results );
for( FieldOfView2dCoordinate c : resultsBuf )
{
if( !cellIsOpaque(c) )
{
for( int d = -1; d<=1; d += 2 )
{
FieldOfView2dCoordinate c2;
c2 = new FieldOfView2dCoordinate(c.x()+d,c.y());
if( cellIsOpaque(c2) && cellContainsData(c2) )
results.add( c2 );
c2 = new FieldOfView2dCoordinate(c.x(),c.y()+d);
if( cellIsOpaque(c2) && cellContainsData(c2) )
results.add( c2 );
}
}
}
// This filter ensures that ordinal corners next to two adjacent
// cardinal opaque cells which are next to a non-opaque cell in
// a convex fashion are drawn. (That is, it gets rid of the
// missing corner syndrome.)
resultsBuf = new HashSet<FieldOfView2dCoordinate>( results );
for( FieldOfView2dCoordinate c : resultsBuf )
{
if( !cellIsOpaque(c) )
{
for( int dy=-1; dy<=1; dy+=2 )
{
for( int dx=-1; dx<=1; dx+=2 )
{
FieldOfView2dCoordinate c2 = new FieldOfView2dCoordinate(c.x()+dx,c.y()+dy);
if( cellContainsData(c2) && cellIsOpaque(c2) )
{
FieldOfView2dCoordinate c3a = new FieldOfView2dCoordinate(c2.x()-dx,c2.y());
FieldOfView2dCoordinate c3b = new FieldOfView2dCoordinate(c2.x(),c2.y()-dy);
if( cellIsOpaque(c3a) && resultsBuf.contains( c3a ) && cellContainsData(c3a) && cellIsOpaque(c3b) && resultsBuf.contains( c3b ) && cellContainsData(c3b) )
results.add( c2 );
}
}
}
}
}
}
opaqueCells.clear(); // Allow entries to be GCed.
cellContainsData.clear(); // Allow entries to be GCed.
return results;
}
final public Set<FieldOfView2dCoordinate> getFieldOfViewFrom( int x, int y, int radius )
{
return getFieldOfViewFrom( new FieldOfView2dCoordinate(x,y), radius );
}
}