Dear users, Please note that, from Monday, August 16, 2019, RSA keys shorter than 2048bit will no longer be accepted for security reasons. Please update your keys as needed before this date. If you need assistance with regard to this process, please contact sia@list.lu

Thank you for your understanding.

Commit ef3f2f34 authored by Nico Mack's avatar Nico Mack

Finalized implementation of KdTree

Implementation of Clusterable interface at Marker Level
parent 15d9a29c
...@@ -24,6 +24,7 @@ CENTER_ON_ZOOM_NODE=centerOnZoom ...@@ -24,6 +24,7 @@ CENTER_ON_ZOOM_NODE=centerOnZoom
CENTRE_NODE=centre CENTRE_NODE=centre
CENTRED_NODE=centred CENTRED_NODE=centred
CIRCLE_SIZE_NODE=circleSize CIRCLE_SIZE_NODE=circleSize
CLUSTERABLE_NODE=clusterable
COLOUR_NODE=colour COLOUR_NODE=colour
COLOUR_PALETTE_NODE=colourPalette COLOUR_PALETTE_NODE=colourPalette
COLOURSC_NODE=colourscheme COLOURSC_NODE=colourscheme
......
...@@ -44,11 +44,11 @@ import org.slf4j.LoggerFactory; ...@@ -44,11 +44,11 @@ import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Vector;
/** /**
* Abstract class holding the basic necessities to have in a tangible application. The class * Abstract class holding the basic necessities to have in a tangible application. The class
...@@ -175,6 +175,9 @@ public abstract class TangibleApplication { ...@@ -175,6 +175,9 @@ public abstract class TangibleApplication {
return document; return document;
} }
/**
*
*/
public void setupAnimation() { public void setupAnimation() {
TridentConfig.getInstance().setPulseSource(new TridentConfig.FixedRatePulseSource(1000 / ANIMATION_FRAME_RATE)); TridentConfig.getInstance().setPulseSource(new TridentConfig.FixedRatePulseSource(1000 / ANIMATION_FRAME_RATE));
} }
...@@ -221,16 +224,6 @@ public abstract class TangibleApplication { ...@@ -221,16 +224,6 @@ public abstract class TangibleApplication {
return objectManager; return objectManager;
} }
/**
* Simple setter method for interfaceManager.
*
* @param interfaceManager
* The value to set interfaceManager to.
*/
// public void setInterfaceManager(TangibleInterfaceManager interfaceManager) {
// TangibleApplication.interfaceManager = interfaceManager;
// }
/** /**
* Simple setter method for objectManager. * Simple setter method for objectManager.
* *
...@@ -261,7 +254,6 @@ public abstract class TangibleApplication { ...@@ -261,7 +254,6 @@ public abstract class TangibleApplication {
public void setContentManager(TangibleContentManager contentManager) { public void setContentManager(TangibleContentManager contentManager) {
this.contentManager = contentManager; this.contentManager = contentManager;
stageManager.setContentManager(contentManager); stageManager.setContentManager(contentManager);
// stageManager.setContent(contentManager.getContents());
} }
/** /**
...@@ -273,11 +265,13 @@ public abstract class TangibleApplication { ...@@ -273,11 +265,13 @@ public abstract class TangibleApplication {
stageManager.repaint(); stageManager.repaint();
} }
/**
*
*/
public void shutdown() { public void shutdown() {
this.disconnect(); this.disconnect();
} }
/** /**
* Method for retrieving all active {@link TangibleObject} cursors from the {@link TuiAdapter} * Method for retrieving all active {@link TangibleObject} cursors from the {@link TuiAdapter}
* client managing the global state of the application. * client managing the global state of the application.
...@@ -286,7 +280,7 @@ public abstract class TangibleApplication { ...@@ -286,7 +280,7 @@ public abstract class TangibleApplication {
* instances. * instances.
*/ */
public Collection<TangibleObject> getActiveCursors() { public Collection<TangibleObject> getActiveCursors() {
return new Vector<>(adapter.getActiveCursors()); return new ArrayList<>(adapter.getActiveCursors());
} }
/** /**
...@@ -297,7 +291,7 @@ public abstract class TangibleApplication { ...@@ -297,7 +291,7 @@ public abstract class TangibleApplication {
* instances. * instances.
*/ */
public Collection<TangibleObject> getActiveObjects() { public Collection<TangibleObject> getActiveObjects() {
return new Vector<>(adapter.getActiveObjects()); return new ArrayList<>(adapter.getActiveObjects());
} }
/** /**
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2019. All rights reserved.
*
* This file is part of TULIP.
*
* TULIP is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, version 3 of the
* License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.feature.cluster;
import lu.list.itis.dkd.tui.utility.Positionable;
import lu.list.itis.dkd.tui.utility.kdtree.KdTree;
import java.util.ArrayList;
import java.util.List;
/**
* @author nico.mack@list.lu
* @since 2.6
* @version 1.0.0
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public class ClusterManager {
private KdTree<Positionable> kdTree;
private double radius;
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* @param radius
*/
// ---------------------------------------------------------------------------
public ClusterManager(double radius) {
this.radius = radius;
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitive(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
private synchronized List<Clusterable> getClusterable(List<Positionable> candidates) {
List<Clusterable> results = new ArrayList<>();
for (Positionable candidate : candidates) {
if (Clusterable.class.isAssignableFrom(candidate.getClass())) {
results.add((Clusterable) candidate);
}
}
return results;
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* @param positionables
*/
// ---------------------------------------------------------------------------
public void refresh(List<Positionable> positionables) {
kdTree = new KdTree<>(positionables, 2, new PositionableComparator());
List<Positionable> coallesced = new ArrayList<>();
List<Clusterable> clusters = new ArrayList<>();
for (Positionable candidate : positionables) {
if ((candidate instanceof Clusterable) && coallesced.contains(candidate)) {
List<Positionable> coallescing = kdTree.findNearest(candidate, radius);
List<Clusterable> clusterable = this.getClusterable(coallescing);
((Clusterable) candidate).coalesce(clusterable);
coallesced.addAll(coallescing);
clusters.add((Clusterable) candidate);
}
}
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * End of Class
// ***************************************************************************
// ---------------------------------------------------------------------------
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2019. All rights reserved.
*
* This file is part of TULIP.
*
* TULIP is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, version 3 of the
* License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.feature.cluster;
import java.util.List;
/**
* @author nico.mack@list.lu
* @since 2.6
* @version 1.0.0
*/
public interface Clusterable {
/**
* Allows whether is theoretically clusterable object is currently setup to allow clustering
*
* @return <code>true</code> if clustering is supported, <code>false</code> otherwise
*/
public boolean isClusterable();
/**
* Checks whether is this clusterable object is currently in coalesced state.
*
* @return <code>true</code> if object is currently in coalesced state, <code>false</code> otherwise
*/
public boolean isCoalesced();
/**
* Tells clusterable items to collaesce into a single clustered item.
*
* @param disocciated
* @return
*/
public Clusterable coalesce(List<Clusterable> disocciated);
/**
* Tells a previously clustered item to break up into its constituting parts.
*
* @param coallesced
* @return
*/
public List<Clusterable> dissociate(Clusterable coallesced);
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2019. All rights reserved.
*
* This file is part of TULIP.
*
* TULIP is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, version 3 of the
* License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.feature.cluster;
import lu.list.itis.dkd.tui.utility.Point;
import lu.list.itis.dkd.tui.utility.Positionable;
import lu.list.itis.dkd.tui.utility.kdtree.KdComparator;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
public class PositionableComparator implements KdComparator<Positionable> {
/** {@inheritDoc} */
@Override
public int compare(Positionable o1, Positionable o2, int axis) {
Point pos1 = o1.getPosition();
Point pos2 = o2.getPosition();
return pos1.compareTo(pos2, axis);
}
/** {@inheritDoc} */
@Override
public double getDistance(Positionable o1, Positionable o2) {
Point pos1 = o1.getPosition();
Point pos2 = o2.getPosition();
return pos1.getDistance(pos2);
}
/** {@inheritDoc} */
@Override
public double getDistance(Positionable o1, Positionable o2, int axis) {
Point pos1 = o1.getPosition();
Point pos2 = o2.getPosition();
return pos1.getDistance(pos2, axis);
}
}
package lu.list.itis.dkd.tui.marker; package lu.list.itis.dkd.tui.marker;
import lu.list.itis.dkd.dbc.annotation.Nullable; import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.feature.cluster.Clusterable;
import lu.list.itis.dkd.tui.marker.builder.MarkerBuilder; import lu.list.itis.dkd.tui.marker.builder.MarkerBuilder;
import lu.list.itis.dkd.tui.utility.CoronaComparator; import lu.list.itis.dkd.tui.utility.CoronaComparator;
import lu.list.itis.dkd.tui.utility.Point; import lu.list.itis.dkd.tui.utility.Point;
...@@ -23,7 +24,7 @@ import java.util.Objects; ...@@ -23,7 +24,7 @@ import java.util.Objects;
// * Class Definition and Members * // * Class Definition and Members *
// *************************************************************************** // ***************************************************************************
public abstract class Marker implements Positionable, Cloneable { public abstract class Marker implements Positionable, Clusterable, Cloneable {
/** The name given to this marker. */ /** The name given to this marker. */
protected String name; protected String name;
...@@ -34,6 +35,10 @@ public abstract class Marker implements Positionable, Cloneable { ...@@ -34,6 +35,10 @@ public abstract class Marker implements Positionable, Cloneable {
protected boolean active; protected boolean active;
protected boolean isClusterable;
protected boolean isCoalesced;
protected List<Clusterable> clustered;
private CoronaComparator coronaComparator; private CoronaComparator coronaComparator;
// *************************************************************************** // ***************************************************************************
...@@ -50,18 +55,18 @@ public abstract class Marker implements Positionable, Cloneable { ...@@ -50,18 +55,18 @@ public abstract class Marker implements Positionable, Cloneable {
public Marker(MarkerBuilder<?> builder) { public Marker(MarkerBuilder<?> builder) {
this.name = builder.name; this.name = builder.name;
this.isClusterable = builder.isClusterable;
this.coronas = builder.coronas; this.coronas = builder.coronas;
this.positions = builder.positions; this.positions = builder.positions;
// this.drawPriority = builder.drawPriority;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
public Marker(Marker original) { public Marker(Marker original) {
this.name = original.name; this.name = original.name;
this.isClusterable = original.isClusterable;
this.coronas = original.cloneCoronas(); this.coronas = original.cloneCoronas();
this.positions = original.clonePositions(); this.positions = original.clonePositions();
// this.drawPriority = builder.drawPriority;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
...@@ -293,6 +298,10 @@ public abstract class Marker implements Positionable, Cloneable { ...@@ -293,6 +298,10 @@ public abstract class Marker implements Positionable, Cloneable {
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* updates the positions of the associated coronas.
*/
// ---------------------------------------------------------------------------
public void updatePositions() { public void updatePositions() {
for (Integer handleId : positions.keySet()) { for (Integer handleId : positions.keySet()) {
...@@ -302,9 +311,58 @@ public abstract class Marker implements Positionable, Cloneable { ...@@ -302,9 +311,58 @@ public abstract class Marker implements Positionable, Cloneable {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@Override
public boolean isClusterable() {
return this.isClusterable;
}
// ---------------------------------------------------------------------------
@Override
public boolean isCoalesced() {
return this.isCoalesced;
}
// ---------------------------------------------------------------------------
@Override
public Clusterable coalesce(List<Clusterable> disocciated) {
this.clustered.addAll(disocciated);
this.isCoalesced = true;
return this;
}
// ---------------------------------------------------------------------------
@Override
public List<Clusterable> dissociate(Clusterable coalesced) {
List<Clusterable> dissociated = new ArrayList<>();
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 @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if ((o != null) && (o instanceof Marker)) { if (o instanceof Marker) {
return this.getHandleId().equals(((Marker) o).getHandleId()); return this.getHandleId().equals(((Marker) o).getHandleId());
} }
return false; return false;
......
...@@ -37,7 +37,7 @@ public abstract class MarkerBuilder<B extends MarkerBuilder<B>> { ...@@ -37,7 +37,7 @@ public abstract class MarkerBuilder<B extends MarkerBuilder<B>> {
/** The positions where the markers handles are at. */ /** The positions where the markers handles are at. */
public HashMap<Integer, Point> positions; public HashMap<Integer, Point> positions;
/** */ /** */
public int drawPriority; public boolean isClusterable;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// *************************************************************************** // ***************************************************************************
...@@ -50,7 +50,7 @@ public abstract class MarkerBuilder<B extends MarkerBuilder<B>> { ...@@ -50,7 +50,7 @@ public abstract class MarkerBuilder<B extends MarkerBuilder<B>> {
coronas = TreeMultimap.create(); coronas = TreeMultimap.create();
positions = new HashMap<>(); positions = new HashMap<>();
name = Externalization.EMPTY_STRING; name = Externalization.EMPTY_STRING;
drawPriority = 0; isClusterable = false;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
...@@ -91,6 +91,7 @@ public abstract class MarkerBuilder<B extends MarkerBuilder<B>> { ...@@ -91,6 +91,7 @@ public abstract class MarkerBuilder<B extends MarkerBuilder<B>> {
} }
name = BootstrappingUtils.getContentAsString(rootElement, Externalization.NAME_NODE, BootstrappingUtils.OPTIONAL, Externalization.EMPTY_STRING, context); name = BootstrappingUtils.getContentAsString(rootElement, Externalization.NAME_NODE, BootstrappingUtils.OPTIONAL, Externalization.EMPTY_STRING, context);
isClusterable = BootstrappingUtils.getContentAsBoolean(rootElement, Externalization.CLUSTERABLE_NODE, BootstrappingUtils.OPTIONAL, Boolean.FALSE, context);
coronas = CoronaBootstrapper.getCoronas(rootElement.getChild(Externalization.CORONAS_NODE), context, callback); coronas = CoronaBootstrapper.getCoronas(rootElement.getChild(Externalization.CORONAS_NODE), context, callback);
} }
......
...@@ -59,6 +59,7 @@ public class Externalization extends NLS { ...@@ -59,6 +59,7 @@ public class Externalization extends NLS {
public static String CENTRE_NODE; public static String CENTRE_NODE;
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 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;
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2016.
*
* This file is part of TULIP.
*
* TULIP is licensed under a dual-licensing scheme. For non-commercial purposes, the LGPL version 3,
* as stated below, is applicable. For all commercial purposes TULIP is licensed under a LIST
* proprietary license. Please contact LIST at tto@list.lu to obtain a commercial license.
*
* For all non-commercial purposes, TULIP is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, version 3 of the License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.utility;
import lu.list.itis.dkd.dbc.annotation.NonNull;
import com.google.common.base.Preconditions;
/**
* Class implementing a {@link CoordinateState} for screen coordinates. The class features methods
* to transition to all other states.
*
* @author Eric Tobias [eric.tobias@list.lu]
* @since 1.0
* @version 2.3.0
*/
public class GridCoordinates extends CoordinateState {
private double horizonalCellMultiplier = 1 / DEFAULT_CELL_SIZE;
private double verticalCellMultiplier = 1 / DEFAULT_CELL_SIZE;
private Bounds2D bounds = DEFAULT_BOUNDS;
public static final double DEFAULT_CELL_SIZE = 0.1d;
private static final Bounds2D DEFAULT_BOUNDS = new Bounds2D(0, 0, 1, 1);
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Constructing an instance with the provided points.
*
* @param context
* The context of the state; the feature the state gives meaning.
* @param x
* The X coordinate of the feature containing this state.
* @param y
* The Y coordinate of the feature containing this state.
*/
public GridCoordinates(@NonNull Point context, float x, float y) {
context.setLocation(x, y);
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* @param horizonalCellSize
* @param verticalCellSize
*/
public void setCellSize(double horizonalCellSize, double verticalCellSize) {
Preconditions.checkArgument((horizonalCellSize > 0) && (horizonalCellSize < 0.5));
Preconditions.checkArgument((verticalCellSize > 0) && (verticalCellSize < 0.5));
this.horizonalCellMultiplier = 1 / horizonalCellSize;
this.verticalCellMultiplier = 1 / verticalCellSize;
}
/**
* {@inheritDoc}
*/
@Override
@Deprecated
public void toTable(@NonNull Point context) {
context.setState(new TableCoordinates(context, Calibration.screenToTableX(context.x), Calibration.screenToTableY(context.y)));
}
/**
* {@inheritDoc}
*/
@Override
@Deprecated
public void toScreen(@NonNull Point context) {
/**
* The conversion is naught as the point's coordinates are already given in this reference scheme!
*/
}
/**
* {@inheritDoc}
*/
@Override
@Deprecated
public void toCamera(@NonNull Point context) {
context.setState(new CameraCoordinates(context, Calibration.screenToCameraX(context.x), Calibration.screenToCameraY(context.y)));
}
@Override
public boolean withinBounds(Point context) {
return DEFAULT_BOUNDS.includes(context);
}
/** {@inheritDoc} */
@Override
public Point normalize(Point context) {
double normx = (context.x - bounds.x1) / bounds.getWidth();
double normy = (context.y - bounds.y1) / bounds.getHeight();
return new Point((float) normx, (float) normy, context.getAngle(), NormalizedCoordinates.class);
}
/** {@inheritDoc} */
@Override
public Point transform(Point normalized) {
double gridx = bounds.x1 + (normalized.x * bounds.getWidth());
double gridy = bounds.y1 + (normalized.y * bounds.getHeight());
gridx = Math.floor(gridx * this.horizonalCellMultiplier + 0.5) / this.horizonalCellMultiplier;
gridy = Math.floor(gridy * this.verticalCellMultiplier + 0.5) / this.verticalCellMultiplier;
return new Point((float) gridx, (float) gridy, normalized.getAngle(), this.getClass());
}
}
\ No newline at end of file
...@@ -27,7 +27,6 @@ import lu.list.itis.dkd.tui.bootstrapping.BootstrapContext; ...@@ -27,7 +27,6 @@ import lu.list.itis.dkd.tui.bootstrapping.BootstrapContext;
import lu.list.itis.dkd.tui.bootstrapping.BootstrappingUtils; import lu.list.itis.dkd.tui.bootstrapping.BootstrappingUtils;
import lu.list.itis.dkd.tui.bootstrapping.CoordinateStateBootstrapper; import lu.list.itis.dkd.tui.bootstrapping.CoordinateStateBootstrapper;
import lu.list.itis.dkd.tui.exception.BuildException; import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.utility.kdtree.KdComparator;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
...@@ -48,7 +47,7 @@ import java.util.Comparator; ...@@ -48,7 +47,7 @@ import java.util.Comparator;
* @version 2.3.0 * @version 2.3.0
*/ */
@NonNullByDefault