electrodynamics/archive/java/resonantinduction/mechanical/fluid/pump/LiquidPathFinder.java
2014-01-11 17:44:07 +08:00

344 lines
7.9 KiB
Java

package resonantinduction.mechanical.fluid.pump;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.common.ForgeDirection;
import universalelectricity.api.vector.Vector2;
import universalelectricity.api.vector.Vector3;
import calclavia.lib.utility.FluidHelper;
/**
* A simpler path Finder used to find drainable or fillable tiles
*
* @author DarkGuardsman
*/
public class LiquidPathFinder
{
/** Curent world this pathfinder will operate in */
private World world;
/** List of all nodes traveled by the path finder */
public Set<Vector3> nodeList = new HashSet<Vector3>();
/** List of all nodes that match the search parms */
public Set<Vector3> results = new HashSet<Vector3>();
/** Are we looking for liquid fillable blocks */
private boolean fill = false;
/** priority search direction either up or down only */
private ForgeDirection priority;
/** Limit on the searched nodes per run */
private int resultLimit = 200;
private int resultsFound = 0;
private int resultRun = resultLimit;
private int runs = 0;
/** Start location of the pathfinder used for range calculations */
private Vector3 Start;
/** Range to limit the search to */
private double range;
/** List of forgeDirection to use that are shuffled to prevent strait lines */
List<ForgeDirection> shuffledDirections = new ArrayList<ForgeDirection>();
public LiquidPathFinder(final World world, final int resultLimit, final double range)
{
this.range = range;
this.world = world;
if (fill)
{
priority = ForgeDirection.DOWN;
}
else
{
priority = ForgeDirection.UP;
}
this.resultLimit = resultLimit;
this.reset();
shuffledDirections.add(ForgeDirection.EAST);
shuffledDirections.add(ForgeDirection.WEST);
shuffledDirections.add(ForgeDirection.NORTH);
shuffledDirections.add(ForgeDirection.SOUTH);
}
public void addNode(Vector3 vec)
{
if (!this.nodeList.contains(vec))
{
this.nodeList.add(vec);
}
}
public void addResult(Vector3 vec)
{
if (!this.results.contains(vec))
{
this.resultsFound++;
this.results.add(vec);
}
}
/**
* Searches for nodes attached to the given node
*
* @return True on success finding, false on failure.
*/
public boolean findNodes(Vector3 node)
{
if (node == null)
{
return false;
}
try
{
this.addNode(node);
if (this.isValidResult(node))
{
this.addResult(node);
}
if (this.isDone(node.clone()))
{
return false;
}
if (find(this.priority, node.clone()))
{
return true;
}
Collections.shuffle(shuffledDirections);
Collections.shuffle(shuffledDirections);
for (ForgeDirection direction : shuffledDirections)
{
if (find(direction, node.clone()))
{
return true;
}
}
if (find(this.priority.getOpposite(), node.clone()))
{
return true;
}
}
catch (Exception e)
{
e.printStackTrace();
}
return false;
}
/**
* Find all node attached to the origin mode in the given direction
*
* Note: Calls findNode if the next code is valid
*/
public boolean find(ForgeDirection direction, Vector3 origin)
{
this.runs++;
Vector3 vec = origin.clone().modifyPositionFromSide(direction);
double distance = vec.toVector2().distance(this.Start.toVector2());
if (distance <= this.range && this.isValidNode(vec))
{
if (this.fill && FluidHelper.drainBlock(world, vec, false) != null || FluidHelper.isFillableFluid(world, vec))
{
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS)
{
Vector3 veb = vec.clone().modifyPositionFromSide(dir);
if (FluidHelper.isFillableBlock(world, veb))
{
this.addNode(veb);
if (this.isValidResult(veb))
{
this.addResult(veb);
}
}
}
}
if (this.findNodes(vec))
{
return true;
}
}
return false;
}
/** Checks to see if this node is valid to path find threw */
public boolean isValidNode(Vector3 pos)
{
if (pos == null)
{
return false;
}
/* Check if the chunk is loaded to prevent action outside of the loaded area */
Chunk chunk = this.world.getChunkFromBlockCoords(pos.intX(), pos.intZ());
if (chunk == null || !chunk.isChunkLoaded)
{
return false;
}
return FluidHelper.drainBlock(world, pos, false) != null || FluidHelper.isFillableFluid(world, pos);
}
public boolean isValidResult(Vector3 node)
{
if (this.fill)
{
return FluidHelper.isFillableBlock(world, node) || FluidHelper.isFillableFluid(world, node);
}
else
{
return FluidHelper.drainBlock(world, node, false) != null;
}
}
/** Checks to see if we are done pathfinding */
public boolean isDone(Vector3 vec)
{
if (this.runs > 1000)
{
return true;
}
if (this.resultsFound >= this.resultRun)
if (this.results.size() >= this.resultLimit || this.nodeList.size() >= 10000)
{
return true;
}
return false;
}
/** Called to execute the pathfinding operation. */
public LiquidPathFinder start(final Vector3 startNode, int runCount, final boolean fill)
{
this.Start = startNode;
this.fill = fill;
this.runs = 0;
this.resultsFound = 0;
this.resultRun = runCount;
this.find(ForgeDirection.UNKNOWN, startNode);
this.refresh();
this.sortBlockList(Start, results, !fill, fill);
return this;
}
public LiquidPathFinder reset()
{
this.nodeList.clear();
this.results.clear();
return this;
}
public LiquidPathFinder refresh()
{
Iterator<Vector3> it = this.nodeList.iterator();
while (it.hasNext())
{
Vector3 vec = it.next();
if (!this.isValidNode(vec))
{
it.remove();
}
if (this.isValidResult(vec))
{
this.addResult(vec);
}
}
it = this.results.iterator();
while (it.hasNext())
{
Vector3 vec = it.next();
if (!this.isValidResult(vec))
{
it.remove();
}
if (this.isValidNode(vec))
{
this.addNode(vec);
}
}
return this;
}
/**
* Used to sort a list of vector3 locations using the vector3's distance from one point and
* elevation in the y axis
*
* @param start - start location to measure distance from
* @param results2 - list of vectors to sort
* @param closest - sort closest distance to the top
* @param highest - sort highest y value to the top.
*
* Note: highest takes priority over closest
*/
public void sortBlockList(final Vector3 start, final Set<Vector3> set, final boolean closest, final boolean highest)
{
try
{
List<Vector3> list = new ArrayList<Vector3>();
list.addAll(set);
Collections.sort(list, new Comparator<Vector3>()
{
@Override
public int compare(Vector3 vecA, Vector3 vecB)
{
// Though unlikely always return zero for equal vectors
if (vecA.equals(vecB))
{
return 0;
}
// Check y value fist as this is the primary search area
if (Integer.compare(vecA.intY(), vecB.intY()) != 0)
{
if (highest)
{
return vecA.intY() > vecB.intY() ? -1 : 1;
}
else
{
return vecA.intY() > vecB.intY() ? 1 : -1;
}
}
// Check distance after that
double distanceA = Vector2.distance(vecA.toVector2(), start.toVector2());
double distanceB = Vector2.distance(vecB.toVector2(), start.toVector2());
if (Double.compare(distanceA, distanceB) != 0)
{
if (closest)
{
return distanceA > distanceB ? 1 : -1;
}
else
{
return distanceA > distanceB ? -1 : 1;
}
}
return Double.compare(distanceA, distanceB);
}
});
set.clear();
set.addAll(list);
}
catch (Exception e)
{
System.out.println("FluidMech>>>BlockDrain>>FillArea>>Error>>CollectionSorter");
e.printStackTrace();
}
}
public LiquidPathFinder setWorld(World world2)
{
if (world2 != this.world)
{
this.reset();
this.world = world2;
}
return this;
}
}