Commit 1837b3b1 authored by Nico Mack's avatar Nico Mack

Fixes to KdTree and ClusterManager implementation.

Changes to POM and Eclipse project setup
parent 24907935
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry kind="src" path="config"/> <classpathentry kind="src" path="config"/>
<classpathentry excluding="main/java/|test/java/" kind="src" output="target/classes" path="src"/> <classpathentry kind="src" output="target/classes" path="src">
<classpathentry kind="src" output="target/test-classes" path="test"/> <attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="test">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
<accessrules>
<accessrule kind="accessible" pattern="sun/java2d/**"/>
</accessrules>
</classpathentry> </classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
......
...@@ -11,12 +11,12 @@ ...@@ -11,12 +11,12 @@
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand> <buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name> <name>org.fusesource.ide.project.RiderProjectBuilder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand> <buildCommand>
<name>org.fusesource.ide.project.RiderProjectBuilder</name> <name>org.eclipse.m2e.core.maven2Builder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
......
eclipse.preferences.version=1 eclipse.preferences.version=1
encoding/<project>=UTF-8 encoding/<project>=UTF-8
encoding/src=UTF-8
encoding/test=UTF-8
...@@ -25,6 +25,7 @@ CENTRE_NODE=centre ...@@ -25,6 +25,7 @@ CENTRE_NODE=centre
CENTRED_NODE=centred CENTRED_NODE=centred
CIRCLE_SIZE_NODE=circleSize CIRCLE_SIZE_NODE=circleSize
CLUSTERABLE_NODE=clusterable CLUSTERABLE_NODE=clusterable
CLUSTERED_BUNDLE_NODE=clusteredBundle
COLOUR_NODE=colour COLOUR_NODE=colour
COLOUR_PALETTE_NODE=colourPalette COLOUR_PALETTE_NODE=colourPalette
COLOURSC_NODE=colourscheme COLOURSC_NODE=colourscheme
......
...@@ -25,8 +25,8 @@ ...@@ -25,8 +25,8 @@
</scm> </scm>
<build> <build>
<sourceDirectory>src/main/java</sourceDirectory> <sourceDirectory>src</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory> <testSourceDirectory>test</testSourceDirectory>
<plugins> <plugins>
<plugin> <plugin>
...@@ -70,6 +70,7 @@ ...@@ -70,6 +70,7 @@
<manifest> <manifest>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries> <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest> </manifest>
<manifestFile>config/META-INF/MANIFEST.MF</manifestFile>
</archive> </archive>
</configuration> </configuration>
</plugin> </plugin>
......
...@@ -21,6 +21,9 @@ import lu.list.itis.dkd.tui.utility.ScreenCoordinates; ...@@ -21,6 +21,9 @@ import lu.list.itis.dkd.tui.utility.ScreenCoordinates;
import lu.list.itis.dkd.tui.utility.kdtree.KdComparator; import lu.list.itis.dkd.tui.utility.kdtree.KdComparator;
import lu.list.itis.dkd.tui.utility.kdtree.KdTree; import lu.list.itis.dkd.tui.utility.kdtree.KdTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -44,6 +47,12 @@ public class ClusterManager<P extends Positionable> { ...@@ -44,6 +47,12 @@ public class ClusterManager<P extends Positionable> {
private KdTree<P> kdTree; private KdTree<P> kdTree;
// ***************************************************************************
// * Constants *
// ***************************************************************************
private static final Logger LOGGER = LoggerFactory.getLogger(ClusterManager.class.getSimpleName());
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// *************************************************************************** // ***************************************************************************
// * Constructor(s) // * Constructor(s)
...@@ -69,17 +78,6 @@ public class ClusterManager<P extends Positionable> { ...@@ -69,17 +78,6 @@ public class ClusterManager<P extends Positionable> {
// *************************************************************************** // ***************************************************************************
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
private synchronized List<Clusterable> getClusterable(List<P> candidates) {
List<Clusterable> results = new ArrayList<>();
for (P candidate : candidates) {
if (Clusterable.class.isAssignableFrom(candidate.getClass())) {
results.add((Clusterable) candidate);
}
}
return results;
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// *************************************************************************** // ***************************************************************************
// * Class Body // * Class Body
...@@ -107,6 +105,16 @@ public class ClusterManager<P extends Positionable> { ...@@ -107,6 +105,16 @@ public class ClusterManager<P extends Positionable> {
} }
} }
// ---------------------------------------------------------------------------
public void setup() {
if (!this.constituents.isEmpty()) {
kdTree = new KdTree<>(this.constituents, 2, this.comparator);
} else {
LOGGER.info("No clusterable items available! Clustering is disabled!");
}
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** /**
* @param positionables * @param positionables
...@@ -114,19 +122,47 @@ public class ClusterManager<P extends Positionable> { ...@@ -114,19 +122,47 @@ public class ClusterManager<P extends Positionable> {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
public void refresh() { public void refresh() {
kdTree = new KdTree<>(this.constituents, 2, this.comparator);
clustered.clear(); if (kdTree == null)
List<P> coallesced = new ArrayList<>(); return;
for (P candidate : this.constituents) {
if ((candidate instanceof Clusterable) && !coallesced.contains(candidate)) { List<P> retained = new ArrayList<>();
List<P> coallescing = kdTree.findNearest(candidate, radius); List<P> coalesced = new ArrayList<>();
if (coallescing.size() > 1) { List<P> candidates = new ArrayList<>(this.constituents);
List<Clusterable> clusterable = this.getClusterable(coallescing); boolean clusterDetected;
coallesced.addAll(coallescing); int iterations = 0;
((Clusterable) candidate).coalesce(clusterable);
do {
clusterDetected = false;
for (P candidate : candidates) {
if ((candidate instanceof Clusterable) && !coalesced.contains(candidate)) {
List<P> coalescing = kdTree.findNearest(candidate, radius);
boolean isClustered = (!coalescing.isEmpty());
coalescing.removeAll(coalesced);
clusterDetected |= (!coalescing.isEmpty());
if (isClustered) {
coalesced.addAll(coalescing);
retained.removeAll(coalescing);
}
((Clusterable) candidate).setClustered(isClustered);
if (!retained.contains(candidate)) {
retained.add(candidate);
}
} }
clustered.add(candidate);
} }
candidates.clear();
candidates.addAll(retained);
iterations++;
} while (clusterDetected);
synchronized (clustered) {
clustered.clear();
clustered.addAll(retained);
LOGGER.info("Clustering completed in {} iteration(s)! {} Marker(s) remaining!", iterations, clustered.size());
} }
} }
...@@ -138,8 +174,8 @@ public class ClusterManager<P extends Positionable> { ...@@ -138,8 +174,8 @@ public class ClusterManager<P extends Positionable> {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
public List<P> getClusters() { public synchronized List<P> getClusters() {
return this.clustered; return new ArrayList<>(this.clustered);
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
......
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
*/ */
package lu.list.itis.dkd.tui.feature.cluster; package lu.list.itis.dkd.tui.feature.cluster;
import java.util.List;
/** /**
* @author nico.mack@list.lu * @author nico.mack@list.lu
* @since 2.6 * @since 2.6
...@@ -37,7 +35,7 @@ public interface Clusterable { ...@@ -37,7 +35,7 @@ public interface Clusterable {
* *
* @return <code>true</code> if object is currently in coalesced state, <code>false</code> otherwise * @return <code>true</code> if object is currently in coalesced state, <code>false</code> otherwise
*/ */
public boolean isCoalesced(); public boolean isClustered();
/** /**
* Tells clusterable items to collaesce into a single clustered item. * Tells clusterable items to collaesce into a single clustered item.
...@@ -45,13 +43,5 @@ public interface Clusterable { ...@@ -45,13 +43,5 @@ public interface Clusterable {
* @param disocciated * @param disocciated
* @return * @return
*/ */
public Clusterable coalesce(List<Clusterable> disocciated); public void setClustered(boolean isClustered);
}
/** \ No newline at end of file
* Tells a previously clustered item to break up into its constituting parts.
*
* @param coallesced
* @return
*/
public List<Clusterable> dissociate(Clusterable coallesced);
}
...@@ -22,6 +22,9 @@ import lu.list.itis.dkd.tui.utility.Positionable; ...@@ -22,6 +22,9 @@ import lu.list.itis.dkd.tui.utility.Positionable;
import lu.list.itis.dkd.tui.utility.ScreenCoordinates; import lu.list.itis.dkd.tui.utility.ScreenCoordinates;
import lu.list.itis.dkd.tui.utility.kdtree.KdComparator; import lu.list.itis.dkd.tui.utility.kdtree.KdComparator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* @author mack * @author mack
* @since [major].[minor] * @since [major].[minor]
...@@ -31,6 +34,15 @@ public class PositionableComparator implements KdComparator<Positionable> { ...@@ -31,6 +34,15 @@ public class PositionableComparator implements KdComparator<Positionable> {
private Class<? extends CoordinateState> referenceState = ScreenCoordinates.class; private Class<? extends CoordinateState> referenceState = ScreenCoordinates.class;
// ***************************************************************************
// * Constants *
// ***************************************************************************
private static final Logger LOGGER = LoggerFactory.getLogger(PositionableComparator.class.getSimpleName());
/**
* @param state
*/
public void setReferenceState(Class<? extends CoordinateState> state) { public void setReferenceState(Class<? extends CoordinateState> state) {
this.referenceState = state; this.referenceState = state;
} }
...@@ -38,31 +50,34 @@ public class PositionableComparator implements KdComparator<Positionable> { ...@@ -38,31 +50,34 @@ public class PositionableComparator implements KdComparator<Positionable> {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public int compare(Positionable o1, Positionable o2, int axis) { public int compare(Positionable o1, Positionable o2, int axis) {
Point pos1 = o1.getPosition().clone(); if (o1.equals(o2))
Point pos2 = o2.getPosition().clone(); return 0;
pos1.toCoordinates(this.referenceState);
pos2.toCoordinates(this.referenceState); Point pos1 = o1.getPosition().toCoordinates(this.referenceState);
Point pos2 = o2.getPosition().toCoordinates(this.referenceState);
return pos1.compareTo(pos2, axis); return pos1.compareTo(pos2, axis);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public double getDistance(Positionable o1, Positionable o2) { public double getDistance(Positionable o1, Positionable o2) {
Point pos1 = o1.getPosition().clone(); if (o1.equals(o2))
Point pos2 = o2.getPosition().clone(); return 0;
pos1.toCoordinates(this.referenceState);
pos2.toCoordinates(this.referenceState); Point pos1 = o1.getPosition().toCoordinates(this.referenceState);
return pos1.getDistance(pos2); Point pos2 = o2.getPosition().toCoordinates(this.referenceState);
return Math.abs(pos1.getDistance(pos2));
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public double getDistance(Positionable o1, Positionable o2, int axis) { public double getDistance(Positionable o1, Positionable o2, int axis) {
Point pos1 = o1.getPosition().clone(); if (o1.equals(o2))
Point pos2 = o2.getPosition().clone(); return 0;
pos1.toCoordinates(this.referenceState);
pos2.toCoordinates(this.referenceState); Point pos1 = o1.getPosition().toCoordinates(this.referenceState);
return pos1.getDistance(pos2, axis); Point pos2 = o2.getPosition().toCoordinates(this.referenceState);
return Math.abs(pos1.getDistance(pos2, axis));
} }
} }
...@@ -36,8 +36,7 @@ public abstract class Marker implements Positionable, Clusterable, Cloneable { ...@@ -36,8 +36,7 @@ public abstract class Marker implements Positionable, Clusterable, Cloneable {
protected boolean active; protected boolean active;
protected boolean isClusterable; protected boolean isClusterable;
protected boolean isCoalesced; protected boolean isClustered;
protected List<Clusterable> clustered;
private CoronaComparator coronaComparator; private CoronaComparator coronaComparator;
...@@ -58,7 +57,7 @@ public abstract class Marker implements Positionable, Clusterable, Cloneable { ...@@ -58,7 +57,7 @@ public abstract class Marker implements Positionable, Clusterable, Cloneable {
this.isClusterable = builder.isClusterable; this.isClusterable = builder.isClusterable;
this.coronas = builder.coronas; this.coronas = builder.coronas;
this.positions = builder.positions; this.positions = builder.positions;
this.clustered = new ArrayList<>(); this.isClustered = false;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
...@@ -68,7 +67,7 @@ public abstract class Marker implements Positionable, Clusterable, Cloneable { ...@@ -68,7 +67,7 @@ public abstract class Marker implements Positionable, Clusterable, Cloneable {
this.isClusterable = original.isClusterable; this.isClusterable = original.isClusterable;
this.coronas = original.cloneCoronas(); this.coronas = original.cloneCoronas();
this.positions = original.clonePositions(); this.positions = original.clonePositions();
this.clustered = new ArrayList<>(); this.isClustered = false;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
...@@ -208,14 +207,17 @@ public abstract class Marker implements Positionable, Clusterable, Cloneable { ...@@ -208,14 +207,17 @@ public abstract class Marker implements Positionable, Clusterable, Cloneable {
* @return * @return
*/ */
public @Nullable Point getPosition(Integer handleId) { public @Nullable Point getPosition(Integer handleId) {
return positions.get(handleId); if (positions.containsKey(handleId)) {
return positions.get(handleId).clone();
}
return null;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@Override @Override
public @Nullable Point getPosition() { public @Nullable Point getPosition() {
return positions.get(this.getHandleId()); return this.getPosition(this.getHandleId());
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
...@@ -321,49 +323,25 @@ public abstract class Marker implements Positionable, Clusterable, Cloneable { ...@@ -321,49 +323,25 @@ public abstract class Marker implements Positionable, Clusterable, Cloneable {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@Override @Override
public boolean isCoalesced() { public boolean isClustered() {
return this.isCoalesced; return this.isClustered;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@Override @Override
public Clusterable coalesce(List<Clusterable> disocciated) { public void setClustered(boolean isClustered) {
this.clustered.addAll(disocciated); this.isClustered = isClustered;
this.isCoalesced = true;
return this;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@Override @Override
public List<Clusterable> dissociate(Clusterable coalesced) { public boolean equals(Object o) {
List<Clusterable> dissociated = new ArrayList<>(); if (o == this) {
return true;
if (coalesced.equals(this)) {
dissociated.addAll(this.clustered);
this.clustered.clear();
this.isCoalesced = false;
} else {
List<Clusterable> affected = new ArrayList<>();
for (Clusterable member : this.clustered) {
if (member.isCoalesced()) {
List<Clusterable> returned = member.dissociate(coalesced);
if (!returned.isEmpty()) {
dissociated.addAll(returned);
affected.add(member);
}
}
}
this.clustered.removeAll(affected);
} }
return dissociated;
}
// ---------------------------------------------------------------------------
@Override
public boolean equals(Object o) {
if (o instanceof Marker) { if (o instanceof Marker) {
return this.getHandleId().equals(((Marker) o).getHandleId()); return this.getHandleId().equals(((Marker) o).getHandleId());
} }
......
...@@ -26,6 +26,7 @@ import lu.list.itis.dkd.tui.widget.corona.bundle.CoronaBundle; ...@@ -26,6 +26,7 @@ import lu.list.itis.dkd.tui.widget.corona.bundle.CoronaBundle;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.TreeMultimap; import com.google.common.collect.TreeMultimap;
import com.jgoodies.common.base.Strings;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -49,6 +50,7 @@ public class ModalMarker extends TetherableMarker { ...@@ -49,6 +50,7 @@ public class ModalMarker extends TetherableMarker {
protected Multimap<String, CoronaBundle> coronaBundles; protected Multimap<String, CoronaBundle> coronaBundles;
protected List<String> activeBundles; protected List<String> activeBundles;
protected String clusteredBundle;
// *************************************************************************** // ***************************************************************************
// * Constants * // * Constants *
...@@ -70,8 +72,9 @@ public class ModalMarker extends TetherableMarker { ...@@ -70,8 +72,9 @@ public class ModalMarker extends TetherableMarker {
super(builder); super(builder);
coronaBundles = builder.coronaBundles; coronaBundles = builder.coronaBundles;
activeBundles = new ArrayList<>(); clusteredBundle = builder.clusteredBundle;
activeBundles = new ArrayList<>();
this.activateDefaults(); this.activateDefaults();
} }
...@@ -84,6 +87,7 @@ public class ModalMarker extends TetherableMarker { ...@@ -84,6 +87,7 @@ public class ModalMarker extends TetherableMarker {
public ModalMarker(ModalMarker original) { public ModalMarker(ModalMarker original) {
super(original); super(original);
clusteredBundle = original.clusteredBundle;
coronaBundles = TreeMultimap.create(); coronaBundles = TreeMultimap.create();
coronaBundles.putAll(original.coronaBundles); coronaBundles.putAll(original.coronaBundles);
activeBundles = new ArrayList<>(); activeBundles = new ArrayList<>();
...@@ -283,6 +287,16 @@ public class ModalMarker extends TetherableMarker { ...@@ -283,6 +287,16 @@ public class ModalMarker extends TetherableMarker {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@Override
public void setClustered(boolean isClustered) {
super.setClustered(isClustered);
if (Strings.isNotBlank(clusteredBundle)) {
this.activateBundle(clusteredBundle, isClustered);
}
}
// ---------------------------------------------------------------------------
public ModalMarker clone() { public ModalMarker clone() {
return new ModalMarker(this); return new ModalMarker(this);
} }
......
...@@ -19,6 +19,7 @@ package lu.list.itis.dkd.tui.marker.builder; ...@@ -19,6 +19,7 @@ package lu.list.itis.dkd.tui.marker.builder;
import lu.list.itis.dkd.dbc.annotation.Nullable; import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.bootstrapping.BootstrapCallback; import lu.list.itis.dkd.tui.bootstrapping.BootstrapCallback;
import lu.list.itis.dkd.tui.bootstrapping.BootstrapContext; import lu.list.itis.dkd.tui.bootstrapping.BootstrapContext;
import lu.list.itis.dkd.tui.bootstrapping.BootstrappingUtils;
import lu.list.itis.dkd.tui.exception.BuildException; import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.marker.ModalMarker; import lu.list.itis.dkd.tui.marker.ModalMarker;
import lu.list.itis.dkd.tui.utility.Externalization; import lu.list.itis.dkd.tui.utility.Externalization;
...@@ -43,6 +44,7 @@ import org.jdom2.Element; ...@@ -43,6 +44,7 @@ import org.jdom2.Element;
public abstract class BaseModalMarkerBuilder<B extends BaseModalMarkerBuilder<B>> extends BaseTetherableMarkerBuilder<B> { public abstract class BaseModalMarkerBuilder<B extends BaseModalMarkerBuilder<B>> extends BaseTetherableMarkerBuilder<B> {
public Multimap<String, CoronaBundle> coronaBundles; public Multimap<String, CoronaBundle> coronaBundles;
public String clusteredBundle;
// *************************************************************************** // ***************************************************************************
// * Constants * // * Constants *
...@@ -88,6 +90,7 @@ public abstract class BaseModalMarkerBuilder<B extends BaseModalMarkerBuilder<B> ...@@ -88,6 +90,7 @@ public abstract class BaseModalMarkerBuilder<B extends BaseModalMarkerBuilder<B>
coronaBundles.put(bundle.getName(), bundle); coronaBundles.put(bundle.getName(), bundle);
} }
} }
this.clusteredBundle = BootstrappingUtils.getContentAsString(rootElement, Externalization.CLUSTERED_BUNDLE_NODE, BootstrappingUtils.OPTIONAL, null, context);
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
......
...@@ -60,6 +60,7 @@ public class Externalization extends NLS { ...@@ -60,6 +60,7 @@ public class Externalization extends NLS {
public static String CENTRED_NODE; public static String CENTRED_NODE;
public static String CIRCLE_SIZE_NODE; public static String CIRCLE_SIZE_NODE;
public static String CLUSTERABLE_NODE; public static String CLUSTERABLE_NODE;
public static String CLUSTERED_BUNDLE_NODE;
public static String COLOUR_NODE; public static String COLOUR_NODE;
public static String COLOUR_PALETTE_NODE; public static String COLOUR_PALETTE_NODE;
public static String COLOURSC_NODE; public static String COLOURSC_NODE;
......
...@@ -66,13 +66,12 @@ public class Point extends Float implements Comparable<Point> { ...@@ -66,13 +66,12 @@ public class Point extends Float implements Comparable<Point> {
private static final Logger logger = LoggerFactory.getLogger(Point.class.getSimpleName()); private static final Logger logger = LoggerFactory.getLogger(Point.class.getSimpleName());
private static final int NUMBER_OF_DIMENSIONS = 2;
/** Constant identifying the X Axis */ /** Constant identifying the X Axis */
public static final int X_AXIS = 0; public static final int X_AXIS = 0;
/** Constant identifying the Y Axis */ /** Constant identifying the Y Axis */
public static final int Y_AXIS = 1; public static final int Y_AXIS = 1;
protected static enum PointComparator implements Comparator<Point> { protected enum PointComparator implements Comparator<Point> {
x { x {
@Override @Override
public int compare(Point a, Point b) { public int compare(Point a, Point b) {
...@@ -84,6 +83,12 @@ public class Point extends Float implements Comparable<Point> { ...@@ -84,6 +83,12 @@ public class Point extends Float implements Comparable<Point> {
public int compare(Point a, Point b) { public int compare(Point a, Point b) {
return java.lang.Double.compare(a.y, b.y); return java.lang.Double.compare(a.y, b.y);
} }
},
angle {
@Override
public int compare(Point a, Point b) {
return java.lang.Double.compare(AngleUtils.moduloTwoPi(a.getAngle()), AngleUtils.moduloTwoPi(b.getAngle()));