Commit 060595ad authored by Nico Mack's avatar Nico Mack

Refactoring of API

parent 64c24630
...@@ -42,8 +42,9 @@ public class ClusterManager<P extends Positionable> { ...@@ -42,8 +42,9 @@ public class ClusterManager<P extends Positionable> {
private double radius; private double radius;
private KdComparator<P> comparator; private KdComparator<P> comparator;
private List<P> constituents; private List<P> nodes;
private List<P> clustered; private List<P> clusteredNodes;
private boolean clustersFormed;
private KdTree<P> kdTree; private KdTree<P> kdTree;
...@@ -66,8 +67,9 @@ public class ClusterManager<P extends Positionable> { ...@@ -66,8 +67,9 @@ public class ClusterManager<P extends Positionable> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public ClusterManager(double radius) { public ClusterManager(double radius) {
this.radius = radius; this.radius = radius;
this.constituents = new ArrayList<>(); this.clustersFormed = false;
this.clustered = new ArrayList<>(); this.nodes = new ArrayList<>();
this.clusteredNodes = new ArrayList<>();
this.comparator = (KdComparator<P>) new PositionableComparator(); this.comparator = (KdComparator<P>) new PositionableComparator();
((PositionableComparator) this.comparator).setReferenceState(ScreenCoordinates.class); ((PositionableComparator) this.comparator).setReferenceState(ScreenCoordinates.class);
} }
...@@ -84,44 +86,50 @@ public class ClusterManager<P extends Positionable> { ...@@ -84,44 +86,50 @@ public class ClusterManager<P extends Positionable> {
// Iterate over all candidates. // Iterate over all candidates.
for (P candidate : candidates) { for (P candidate : candidates) {
// Check whether candidate is indeed clusterable and has not already been // Check whether candidate is indeed clusterable.
// coalesced into a cluster.
if ((candidate instanceof Clusterable) && !coalesced.contains(candidate)) { if ((candidate instanceof Clusterable) && ((Clusterable) candidate).isClusterable()) {
// Try to find items within a specific radius. // Next check whether node has not already been coalesced into a cluster.
List<P> coalescing = kdTree.findNearest(candidate, radius); if (!coalesced.contains(candidate)) {
// If there were items within that zone, than the candidate will be // Try to find items within a specific radius.
// a cluster, representing the matching items.
boolean isClustered = (!coalescing.isEmpty()); List<P> coalescing = kdTree.findNearest(candidate, radius);
// However, since we don't rebuild the kdTree for performance reasons // If there were items within that zone, than the candidate will be
// on each iteration, the tree will always hold to the totality of // a cluster, representing the matching items.
// available items. We thus have to remove previously coalesced items
// in order to find out whether we're in the presence of a new cluster
// or not.
coalescing.removeAll(coalesced); boolean isClustered = (!coalescing.isEmpty());
clusterDetected |= (!coalescing.isEmpty());
// Add remaining coalescing items to list of already coalesced items // However, since we don't rebuild the kdTree for performance reasons
// and make sure none of them remains in the retained list. // on each iteration, the tree will always hold to the totality of
// available items. We thus have to remove previously coalesced items
// in order to find out whether we're in the presence of a new cluster
// or not.
if (isClustered) { coalescing.removeAll(coalesced);
coalesced.addAll(coalescing); clusterDetected |= (!coalescing.isEmpty());
retained.removeAll(coalescing);
} // Add remaining coalescing items to list of already coalesced items
// and make sure none of them remains in the retained list.
// Last but not least, set the candidates' clustered flag and add if (isClustered) {
// it to the list of retained items. coalesced.addAll(coalescing);
retained.removeAll(coalescing);
}
((Clusterable) candidate).setClustered(isClustered); // Last but not least, set the candidates' clustered flag and add
if (!retained.contains(candidate)) { // it to the list of retained items.
retained.add(candidate);
((Clusterable) candidate).setClustered(isClustered);
if (!retained.contains(candidate)) {
retained.add(candidate);
}
} }
} else {
retained.add(candidate);
} }
} }
return clusterDetected; return clusterDetected;
...@@ -133,34 +141,51 @@ public class ClusterManager<P extends Positionable> { ...@@ -133,34 +141,51 @@ public class ClusterManager<P extends Positionable> {
// *************************************************************************** // ***************************************************************************
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** /**
* * Clears internal state of this instance. Previously added constituents are no longer known to this
* instance.
*/ */
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
public void clear() { public void clear() {
this.kdTree = null; this.kdTree = null;
this.constituents.clear(); this.nodes.clear();
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** /**
* @param constituent * sets the list of nodes this cluster manager is aware off. Previously added or set nodes will be
* discarded.
*
* @param newNodes
*/ */
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
public void addConstituent(P constituent) { public void setNodes(List<P> newNodes) {
if (!this.constituents.contains(constituent)) { this.nodes.clear();
this.constituents.add(constituent); this.nodes.addAll(newNodes);
}
// ---------------------------------------------------------------------------
/**
* Adds the specified node to the list of nodes this instance is aware of.
*
* @param node
*/
// ---------------------------------------------------------------------------
public void addNode(P node) {
if (!this.nodes.contains(node)) {
this.nodes.add(node);
} }
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
public void setup() { public void setup() {
if (!this.constituents.isEmpty()) { if (!this.nodes.isEmpty()) {
kdTree = new KdTree<>(this.constituents, 2, this.comparator); kdTree = new KdTree<>(this.nodes, 2, this.comparator);
} else { } else {
LOGGER.info("No clusterable items available! Clustering is disabled!"); LOGGER.info("No clusterable items available! Clustering is disabled!"); //$NON-NLS-1$
} }
} }
...@@ -175,36 +200,41 @@ public class ClusterManager<P extends Positionable> { ...@@ -175,36 +200,41 @@ public class ClusterManager<P extends Positionable> {
if (kdTree == null) if (kdTree == null)
return; return;
this.clustersFormed = false;
List<P> retained = new ArrayList<>(); List<P> retained = new ArrayList<>();
List<P> coalesced = new ArrayList<>(); List<P> coalesced = new ArrayList<>();
List<P> candidates = new ArrayList<>(this.constituents); List<P> candidates = new ArrayList<>(this.nodes);
boolean clusterDetected; boolean clusterDetected;
int iterations = 0; int iterations = 0;
do { do {
clusterDetected = this.cluster(candidates, retained, coalesced); clusterDetected = this.cluster(candidates, retained, coalesced);
clustersFormed |= clusterDetected;
candidates.clear(); candidates.clear();
candidates.addAll(retained); candidates.addAll(retained);
iterations++; iterations++;
} while (clusterDetected); } while (clusterDetected);
synchronized (clustered) { synchronized (clusteredNodes) {
clustered.clear(); clusteredNodes.clear();
clustered.addAll(retained); clusteredNodes.addAll(retained);
LOGGER.info("Clustering completed in {} iteration(s)! {} Marker(s) remaining!", iterations, clustered.size()); //$NON-NLS-1$ if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Clustering completed in {} iteration(s)! {} Marker(s) remaining!", iterations, clusteredNodes.size()); //$NON-NLS-1$
}
} }
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
public boolean hasClusters() { public boolean haveClustersFormed() {
return !this.clustered.isEmpty(); return this.clustersFormed;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
public synchronized List<P> getClusters() { public synchronized List<P> getClusteredNodes() {
return new ArrayList<>(this.clustered); return new ArrayList<>(this.clusteredNodes);
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment