package org.jactr.modules.pm.visual.memory.impl;
/*
* default logging
*/
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.Future;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javolution.util.FastList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.chunk.IChunk;
import org.jactr.core.slot.BasicSlot;
import org.jactr.core.slot.ISlot;
import org.jactr.modules.pm.common.memory.impl.IIndexManager;
import org.jactr.modules.pm.visual.IVisualModule;
public class VisualLocationManager implements IIndexManager
{
/**
* Logger definition
*/
static private final transient Log LOGGER = LogFactory
.getLog(VisualLocationManager.class);
private SortedMap<Integer, IChunk> _sparseVisualLocations;
private int _horizontalResolution;
private int _verticalResolution;
private double _horizontalSpan;
private double _verticalSpan;
private Lock _lock = new ReentrantLock();
private final IVisualModule _module;
public VisualLocationManager(IVisualModule module)
{
_module = module;
_sparseVisualLocations = new TreeMap<Integer, IChunk>();
}
public void setDimensions(double horizontalSpan, int horizonalResolution,
double verticalSpan, int verticalResolution)
{
_lock.lock();
try
{
_horizontalResolution = horizonalResolution;
_verticalResolution = verticalResolution;
_horizontalSpan = horizontalSpan;
_verticalSpan = verticalSpan;
/*
* recompute the indices
*/
FastList<IChunk> locations = FastList.newInstance();
locations.addAll(_sparseVisualLocations.values());
_sparseVisualLocations.clear();
/*
* grab existing visual-locations, possibly having been written to file
* already
*/
for (IChunk location : _module.getVisualLocationChunkType()
.getSymbolicChunkType().getChunks())
{
double x = ((Number) location.getSymbolicChunk().getSlot(
IVisualModule.SCREEN_X_SLOT).getValue()).doubleValue();
double y = ((Number) location.getSymbolicChunk().getSlot(
IVisualModule.SCREEN_Y_SLOT).getValue()).doubleValue();
location.setMutable(true);
_sparseVisualLocations.put(getVisualLocationChunkIndex(x, y), location);
}
for (IChunk location : locations)
{
double x = ((Number) location.getSymbolicChunk().getSlot(
IVisualModule.SCREEN_X_SLOT).getValue()).doubleValue();
double y = ((Number) location.getSymbolicChunk().getSlot(
IVisualModule.SCREEN_Y_SLOT).getValue()).doubleValue();
location.setMutable(true);
_sparseVisualLocations.put(getVisualLocationChunkIndex(x, y), location);
}
}
finally
{
_lock.unlock();
}
}
public IChunk getIndexChunk(IChunk encodedChunk)
{
try
{
ISlot screenPos = encodedChunk.getSymbolicChunk().getSlot(
IVisualModule.SCREEN_POSITION_SLOT);
return (IChunk) screenPos.getValue();
}
catch (Exception e)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("No valid screen-pos for " + encodedChunk);
return null;
}
}
public IChunk getVisualLocationChunkAt(double x, double y)
{
int index = getVisualLocationChunkIndex(x, y);
if (index < 0) return null;
return getVisualLocationChunk(index, x, y);
}
protected IChunk getVisualLocationChunk(int index, double x, double y)
{
_lock.lock();
IChunk locationChunk = null;
try
{
locationChunk = _sparseVisualLocations.get(index);
if (locationChunk == null)
{
/*
* need to create it
*/
locationChunk = createVisualLocationChunk(x, y);
_sparseVisualLocations.put(index, locationChunk);
}
}
catch (Exception e)
{
LOGGER.error("Could not create visual location ", e);
}
finally
{
_lock.unlock();
}
return locationChunk;
}
private double getClosestXLocation(double x)
{
double half = _horizontalSpan / 2.0;
int xIndex = (int) Math.ceil((x + half) / _horizontalSpan
* _horizontalResolution);
return xIndex * _horizontalSpan / _horizontalResolution
- half;
}
private double getClosestYLocation(double y)
{
double half = _verticalSpan / 2.0;
int yIndex = (int) Math.ceil((y + half) / _verticalSpan
* _verticalResolution);
return yIndex * _verticalSpan / _verticalResolution
- half;
}
private IChunk createVisualLocationChunk(double x, double y) throws Exception
{
x = getClosestXLocation(x);
y = getClosestYLocation(y);
java.text.NumberFormat nf = java.text.NumberFormat.getNumberInstance();
nf.setMinimumFractionDigits(2);
nf.setMaximumFractionDigits(2);
String name = "Loc:" + nf.format(x) + "x" + nf.format(y);
Future<IChunk> created = _module.getModel().getDeclarativeModule()
.createChunk(_module.getVisualLocationChunkType(), name);
Double xLoc = x;
Double yLoc = y;
IChunk locationChunk = created.get();
locationChunk.getSymbolicChunk().addSlot(
new BasicSlot(IVisualModule.SCREEN_X_SLOT, xLoc));
locationChunk.getSymbolicChunk().addSlot(
new BasicSlot(IVisualModule.SCREEN_Y_SLOT, yLoc));
/*
* so that the chunk is marked as being permitted to have its slots changed
* after encoding
*/
locationChunk.setMutable(true);
locationChunk = _module.getModel().getDeclarativeModule().addChunk(
locationChunk).get();
return locationChunk;
}
protected int getVisualLocationChunkIndex(double x, double y)
{
double halfWidth = _horizontalSpan / 2.0;
double halfHeight = _verticalSpan / 2.0;
if (x > halfWidth || x < -halfWidth)
{
LOGGER
.warn("requested visual location beyond the available horizontal range. requested:"
+ x + " width:" + _horizontalSpan);
return -1;
}
if (y > halfHeight || y < -halfHeight)
{
LOGGER
.warn("requested visual location beyond the available vertical range. requested:"
+ y + " width:" + _verticalSpan);
return -1;
}
// take care of -0 issue
if (x == 0) x = 0;
if (y == 0) y = 0;
double xRes = _horizontalSpan / _horizontalResolution;
double yRes = _verticalSpan / _verticalResolution;
int xIndex = (int) Math.ceil((x + halfWidth) / xRes);
int yIndex = (int) Math.ceil((y + halfHeight) / yRes);
return yIndex * _verticalResolution + xIndex;
}
}