package org.jactr.modules.pm.visual.scene; /* * default logging */ import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.commonreality.identifier.IIdentifier; import org.commonreality.modalities.visual.DefaultVisualPropertyHandler; import org.commonreality.modalities.visual.IVisualPropertyHandler; import org.commonreality.object.IAfferentObject; import org.commonreality.object.delta.IObjectDelta; import org.commonreality.object.manager.event.IAfferentListener; import org.commonreality.object.manager.event.IObjectEvent; import org.jactr.modules.pm.common.memory.map.FeatureMapEvent; import org.jactr.modules.pm.common.memory.map.IFeatureMapListener; import org.jactr.modules.pm.visual.IVisualModule; /** * Scene change listener that implements both {@link IVisualFeatureMapListener} to * track changes to the {@link IVisualModule}'s {@link IVisualMap}'s feature maps, and * {@link IAfferentListener} which allows the listener to circumvent the visual module * and go straight to the source of the percepts. <br> * <br> * The listener works by collecting the {@link IIdentifier}s of all the percepts * that change between calls to {@link #reset()}, call to {@link #check()} will * recompute the change ratio ( {@link #getChangeRatio()} ). * <br> * * @author harrison */ public class SceneChangeListener implements IFeatureMapListener, IAfferentListener { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(SceneChangeListener.class); final private Set<IIdentifier> _allIdentifiers; final private Set<IIdentifier> _changedIdentifiers; final private Set<IIdentifier> _removedIdentifiers; private volatile double _changeRatio = 0; final private ReentrantLock _lock = new ReentrantLock(); final private IVisualPropertyHandler _propertyHandler = new DefaultVisualPropertyHandler(); public SceneChangeListener() { _allIdentifiers = new HashSet<IIdentifier>(); _changedIdentifiers = new HashSet<IIdentifier>(); _removedIdentifiers = new HashSet<IIdentifier>(); } /** * will be called when any new percept is added, we check to see if it * is a visual percept and log it * @param addEvent * @see org.commonreality.object.manager.event.IObjectListener#objectsAdded(org.commonreality.object.manager.event.IObjectEvent) */ public void objectsAdded(IObjectEvent<IAfferentObject, ?> addEvent) { for (IAfferentObject object : addEvent.getObjects()) if (_propertyHandler.hasModality(object)) changed(object.getIdentifier()); } /** * called when a percept is removed. if it is visual, we log it * @param removeEvent * @see org.commonreality.object.manager.event.IObjectListener#objectsRemoved(org.commonreality.object.manager.event.IObjectEvent) */ public void objectsRemoved(IObjectEvent<IAfferentObject, ?> removeEvent) { for (IAfferentObject object : removeEvent.getObjects()) if (_propertyHandler.hasModality(object)) removed(object.getIdentifier()); } /** * called when a percept changes, we determine if it is relevant and log it * @param updateEvent * @see org.commonreality.object.manager.event.IObjectListener#objectsUpdated(org.commonreality.object.manager.event.IObjectEvent) */ public void objectsUpdated(IObjectEvent<IAfferentObject, ?> updateEvent) { try { _lock.lock(); for (IObjectDelta delta : updateEvent.getDeltas()) { IIdentifier id = delta.getIdentifier(); if (_allIdentifiers.contains(id) || _changedIdentifiers.contains(id)) changed(id); } } finally { _lock.unlock(); } } /** * log the id of the changed percept. while this is only called on the CR * thread, we need to use the lock since {@link #reset()} can be called from * anywhere * * @param id */ private void changed(IIdentifier id) { try { _lock.lock(); _changedIdentifiers.add(id); } finally { _lock.unlock(); } } private void changed(Collection<IIdentifier> ids) { try { _lock.lock(); _changedIdentifiers.addAll(ids); } finally { _lock.unlock(); } } private void removed(Collection<IIdentifier> ids) { try { _lock.lock(); _removedIdentifiers.addAll(ids); } finally { _lock.unlock(); } } /** * log the id of the removed percept * * @param id */ private void removed(IIdentifier id) { try { _lock.lock(); _removedIdentifiers.add(id); } finally { _lock.unlock(); } } /** * reset establishes a new baseline. This may be called by any thread, so it * is locked */ protected void reset() { try { _lock.lock(); _allIdentifiers.addAll(_changedIdentifiers); _allIdentifiers.removeAll(_removedIdentifiers); _removedIdentifiers.clear(); _changedIdentifiers.clear(); _changeRatio = 0; } finally { _lock.unlock(); } } /** * calculate and return the change ratio since the last {@link #reset()} * * @return */ protected double check() { /* * since we're just doing size checks, there's no need to synchronize */ _changeRatio = Math.min( (double) (_changedIdentifiers.size() + _removedIdentifiers.size()) / (double) _allIdentifiers.size(), 1); return _changeRatio; } public double getChangeRatio() { return _changeRatio; } public void featureAdded(FeatureMapEvent event) { changed(event.getIdentifiers()); } public void featureRemoved(FeatureMapEvent event) { removed(event.getIdentifiers()); } public void featureUpdated(FeatureMapEvent event) { changed(event.getIdentifiers()); } }