Commit 9bd42244 authored by Nico Mack's avatar Nico Mack

Implementation of Force Direct Tree Marker

parent fa0ad82e
......@@ -140,8 +140,9 @@ public abstract class TangibleObjectManager {
// = Cursor related primitives
// ============================================================================
private void dropCursor(TangibleObject tangibleObject) {
protected boolean dropCursor(TangibleObject tangibleObject) {
BaseWidget cursor = null;
boolean takenCareOf = false;
try {
cursor = this.newCursorInstance(tangibleObject.getObjectId());
......@@ -153,44 +154,54 @@ public abstract class TangibleObjectManager {
cursorMap.putIfAbsent(tangibleObject.getObjectId(), cursor);
cursorMap.get(tangibleObject.getObjectId()).actionDrop(tangibleObject);
ensureConsistency();
takenCareOf = true;
} else {
TouchEvent event = new TouchEvent(tangibleObject.getObjectId(), tangibleObject.getPosition());
for (BaseWidget widget : objectMap.values()) {
if (widget.isTouchable()) {
widget.touched(event);
takenCareOf |= widget.touched(event);
}
}
}
return takenCareOf;
}
// ---------------------------------------------------------------------------
private void moveCursor(TangibleObject tangibleObject) {
protected boolean moveCursor(TangibleObject tangibleObject) {
boolean takenCareOf = false;
if (cursorMap.containsKey(tangibleObject.getObjectId())) {
cursorMap.get(tangibleObject.getObjectId()).actionMove(tangibleObject);
takenCareOf = true;
} else {
TouchEvent event = new TouchEvent(tangibleObject.getObjectId(), tangibleObject.getPosition());
for (BaseWidget widget : objectMap.values()) {
if (widget.isTouchable()) {
widget.dragged(event);
takenCareOf |= widget.dragged(event);
}
}
}
return takenCareOf;
}
// ---------------------------------------------------------------------------
private void liftCursor(TangibleObject tangibleObject) {
protected boolean liftCursor(TangibleObject tangibleObject) {
boolean takenCareOf = false;
if (cursorMap.containsKey(tangibleObject.getObjectId())) {
cursorMap.remove(tangibleObject.getObjectId()).actionLift(tangibleObject);
takenCareOf = true;
} else {
TouchEvent event = new TouchEvent(tangibleObject.getObjectId(), tangibleObject.getPosition());
for (BaseWidget widget : objectMap.values()) {
if (widget.isTouchable()) {
widget.released(event);
takenCareOf |= widget.released(event);
}
}
}
return takenCareOf;
}
// ---------------------------------------------------------------------------
......
......@@ -13,6 +13,7 @@ import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
......@@ -132,6 +133,16 @@ public abstract class Marker implements Comparable<Marker> {
}
// ---------------------------------------------------------------------------
public Integer getHandleId() {
Iterator<Integer> handleIds = positions.keySet().iterator();
if (handleIds.hasNext()) {
return handleIds.next();
}
return null;
}
// ---------------------------------------------------------------------------
public void setActive(boolean isActive) {
......
......@@ -7,18 +7,23 @@ import lu.list.itis.dkd.tui.widget.tether.TetherManager;
import lu.list.itis.dkd.tui.widget.tether.Tetherable;
import lu.list.itis.dkd.tui.widget.tether.listener.TetherListener;
//***************************************************************************
//* Class Definition and Members *
//***************************************************************************
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
public abstract class TetherableMarker extends Marker implements Tetherable {
protected Point tetherOrigin;
protected double tetheringDistance;
protected boolean draggable;
protected boolean rotatesWithTether;
protected TetherManager tetherManager;
protected Point tetherOrigin;
protected double tetheringDistance;
protected boolean draggable;
protected boolean rotatesWithTether;
protected TetherManager tetherManager;
// ***************************************************************************
// * Constants *
// ***************************************************************************
......@@ -29,67 +34,70 @@ public abstract class TetherableMarker extends Marker implements Tetherable {
// ***************************************************************************
// ---------------------------------------------------------------------------
public TetherableMarker(BaseTetherableMarkerBuilder<?> builder) {
super (builder);
this.tetherManager = new TetherManager(this, builder.exclusive);
/**
* @param builder
*/
public TetherableMarker(BaseTetherableMarkerBuilder<?> builder) {
super(builder);
this.tetherManager = new TetherManager(this, builder.exclusive);
this.tetherManager.setProviders(builder.providers);
this.tetherManager.setReceivers(builder.receivers);
this.tetherOrigin = builder.tetherOrigin;
this.tetheringDistance = builder.tetheringDistance;
this.draggable = builder.draggable;
this.rotatesWithTether = builder.rotatesWithTether;
}
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitives *
// ***************************************************************************
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body *
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
/**
* Method used to keep the conona's positions aligned with the base of the widget.
*
* @pre <code>positions.contains(handleID);</code>
* @post <code>forAll coronas.get(handleID) as corona : corona.getCentre().equals(positions.get(handleID);</code>
*/
// ---------------------------------------------------------------------------
@Override
public void setPosition (Integer handleId, Point position) {
super.setPosition(handleId, position);
if (positions.containsKey(handleId)) {
tetherManager.move(position.clone());
}
}
// ---------------------------------------------------------------------------
@Override
public void updatePositions () {
super.updatePositions();
for (Point position: positions.values()) {
tetherManager.move(position.clone());
}
}
// ---------------------------------------------------------------------------
@Override
public void setActive (boolean isActive) {
super.setActive(isActive);
if (!isActive) {
this.tetherManager.lift();
}
}
// ---------------------------------------------------------------------------
@Override
// ---------------------------------------------------------------------------
@Override
public void setPosition(Integer handleId, Point position) {
super.setPosition(handleId, position);
if (positions.containsKey(handleId)) {
tetherManager.move(position.clone());
}
}
// ---------------------------------------------------------------------------
@Override
public void updatePositions() {
super.updatePositions();
for (Point position : positions.values()) {
tetherManager.move(position.clone());
}
}
// ---------------------------------------------------------------------------
@Override
public void setActive(boolean isActive) {
super.setActive(isActive);
if (!isActive) {
this.tetherManager.lift();
}
}
// ---------------------------------------------------------------------------
@Override
public boolean isProviderFor(Tether tether) {
return this.tetherManager.isProviderFor(tether);
}
......@@ -107,7 +115,7 @@ public abstract class TetherableMarker extends Marker implements Tetherable {
public boolean isPotentialTether() {
return this.tetherManager.isPotentialTether();
}
// ---------------------------------------------------------------------------
@Override
......@@ -185,7 +193,7 @@ public abstract class TetherableMarker extends Marker implements Tetherable {
return this.active;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
@Override
public boolean rotatesWithTether() {
......@@ -197,8 +205,8 @@ public abstract class TetherableMarker extends Marker implements Tetherable {
@Override
public boolean isTetheredWith(Tetherable otherTetherable) {
return this.tetherManager.isTetheredWith(otherTetherable);
}
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * End of Class
......
package lu.list.itis.dkd.tui.marker.builder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.jdom2.Element;
import com.google.common.base.Strings;
import com.google.common.collect.TreeMultimap;
import com.jgoodies.common.base.Preconditions;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.bootstrapping.BootstrapCallback;
import lu.list.itis.dkd.tui.bootstrapping.BootstrapContext;
import lu.list.itis.dkd.tui.bootstrapping.BootstrappingUtils;
import lu.list.itis.dkd.tui.bootstrapping.CoordinateStateBootstrapper;
import lu.list.itis.dkd.tui.bootstrapping.CoronaBootstrapper;
import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.marker.TetherableMarker;
import lu.list.itis.dkd.tui.utility.CoordinateState;
import lu.list.itis.dkd.tui.utility.Externalization;
import lu.list.itis.dkd.tui.utility.Point;
import lu.list.itis.dkd.tui.utility.ScreenCoordinates;
//***************************************************************************
//* Class Definition and Members *
//***************************************************************************
import com.google.common.base.Strings;
import com.jgoodies.common.base.Preconditions;
import org.jdom2.Element;
import java.util.ArrayList;
import java.util.List;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
* @param <B>
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public abstract class BaseTetherableMarkerBuilder<B extends BaseTetherableMarkerBuilder<B>> extends MarkerBuilder<B> {
public double tetheringDistance;
public double tetheringDistance;
public Point tetherOrigin = new Point();
// public int handleID = 0;
public boolean draggable = false;
......@@ -38,7 +40,7 @@ public abstract class BaseTetherableMarkerBuilder<B extends BaseTetherableMarker
public List<String> providers = new ArrayList<>();
public List<String> receivers = new ArrayList<>();
// ***************************************************************************
// * Constants *
// ***************************************************************************
......@@ -48,16 +50,16 @@ public abstract class BaseTetherableMarkerBuilder<B extends BaseTetherableMarker
// * Constructor(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
protected BaseTetherableMarkerBuilder() {}
// ---------------------------------------------------------------------------
protected BaseTetherableMarkerBuilder(Element rootElement) throws BuildException {
super (rootElement);
this.buildFromBootstrap(rootElement, null, null);
super(rootElement);
this.buildFromBootstrap(rootElement, null, null);
}
// ---------------------------------------------------------------------------
/**
* Constructor initializing all fields from an {@link Element} containing as child elements all
......@@ -94,18 +96,11 @@ public abstract class BaseTetherableMarkerBuilder<B extends BaseTetherableMarker
* @param callback
* @throws BuildException
*/
private void buildFromBootstrap(@Nullable Element rootElement, BootstrapContext context, BootstrapCallback callback) throws BuildException {
positions = new HashMap<>();
coronas = TreeMultimap.create();
@Override
protected void buildFromBootstrap(@Nullable Element rootElement, BootstrapContext context, BootstrapCallback callback) throws BuildException {
for (Element handleNode : rootElement.getChild(Externalization.HANDLES_NODE).getChildren(Externalization.HANDLE_NODE)) {
int id = BootstrappingUtils.getContentAsInteger(handleNode, null, BootstrappingUtils.MANDATORY, null, context);
positions.put(id, null);
}
super.buildFromBootstrap(rootElement, context, callback);
name = BootstrappingUtils.getContentAsString(rootElement, Externalization.NAME_NODE, BootstrappingUtils.OPTIONAL, Externalization.EMPTY_STRING, context);
coronas = CoronaBootstrapper.getCoronas(rootElement.getChild(Externalization.CORONAS_NODE), context, callback);
Element tetherableNode = rootElement.getChild(Externalization.TETHERABLE_NODE);
if (null != tetherableNode) {
......@@ -123,9 +118,9 @@ public abstract class BaseTetherableMarkerBuilder<B extends BaseTetherableMarker
this.draggable = BootstrappingUtils.getContentAsBoolean(tetherableNode, Externalization.DRAGGABLE_NODE, BootstrappingUtils.OPTIONAL, false, context);
this.exclusive = BootstrappingUtils.getContentAsBoolean(tetherableNode, Externalization.EXCLUSIVE_NODE, BootstrappingUtils.OPTIONAL, false, context);
this.rotatesWithTether = BootstrappingUtils.getContentAsBoolean(tetherableNode, Externalization.ROTATE_WITH_TETHER_NODE, BootstrappingUtils.OPTIONAL, false, context);
}
}
}
// ---------------------------------------------------------------------------
private double buildDistance(@Nullable Element distanceNode, BootstrapContext context, BootstrapCallback callback) throws BuildException {
......
......@@ -23,6 +23,12 @@ import java.util.HashMap;
// * Class Definition and Members *
// ***************************************************************************
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
* @param <B>
*/
public abstract class MarkerBuilder<B extends MarkerBuilder<B>> {
/** The name that will be given to the widget. */
public String name;
......@@ -30,6 +36,7 @@ public abstract class MarkerBuilder<B extends MarkerBuilder<B>> {
public Multimap<Integer, Corona> coronas;
/** The positions where the markers handles are at. */
public HashMap<Integer, Point> positions;
/** */
public int drawPriority;
// ---------------------------------------------------------------------------
......@@ -48,12 +55,22 @@ public abstract class MarkerBuilder<B extends MarkerBuilder<B>> {
// ---------------------------------------------------------------------------
/**
* @param rootElement
* @throws BuildException
*/
public MarkerBuilder(Element rootElement) throws BuildException {
this.buildFromBootstrap(rootElement, null, null);
}
// ---------------------------------------------------------------------------
/**
* @param rootElement
* @param context
* @param callback
* @throws BuildException
*/
public MarkerBuilder(Element rootElement, BootstrapContext context, BootstrapCallback callback) throws BuildException {
this.buildFromBootstrap(rootElement, context, callback);
}
......@@ -64,7 +81,7 @@ public abstract class MarkerBuilder<B extends MarkerBuilder<B>> {
// ***************************************************************************
// ---------------------------------------------------------------------------
private void buildFromBootstrap(@Nullable Element rootElement, BootstrapContext context, BootstrapCallback callback) throws BuildException {
protected void buildFromBootstrap(@Nullable Element rootElement, BootstrapContext context, BootstrapCallback callback) throws BuildException {
positions = new HashMap<>();
coronas = TreeMultimap.create();
......
package lu.list.itis.dkd.tui.utility;
import java.awt.geom.Point2D;
import java.text.DecimalFormat;
public class Bounds2D {
public final double x1;
public final double y1;
public final double x2;
public final double y2;
public static DecimalFormat format = new DecimalFormat();
static {
format.setDecimalSeparatorAlwaysShown(false);
format.setMaximumFractionDigits(2);
format.setGroupingUsed(false);
}
public Bounds2D(double x1, double y1, double x2, double y2) {
this.x1 = Math.min(x1, x2);
this.y1 = Math.min(y1, y2);
this.x2 = Math.max(x1, x2);
this.y2 = Math.max(y1, y2);
}
public Bounds2D(Vector2D bottomLeft, Vector2D topRight) {
this.x1 = Math.min(bottomLeft.getX(), topRight.getX());
this.y1 = Math.min(bottomLeft.getY(), topRight.getY());
this.x2 = Math.max(bottomLeft.getX(), topRight.getX());
this.y2 = Math.max(bottomLeft.getY(), topRight.getY());
}
public boolean includes(double x, double y) {
boolean includes = true;
includes &= (x >= this.x1) && (x <= this.x2);
includes &= (y >= this.y1) && (y <= this.y2);
return includes;
}
public Bounds2D translate(double x, double y) {
double tx1 = x1 + x;
double tx2 = x2 + x;
double ty1 = y1 + y;
double ty2 = y2 + y;
return new Bounds2D(tx1, ty1, tx2, ty2);
}
public Bounds2D scale(double x, double y) {
double sx1 = x1 * x;
double sx2 = x2 * x;
double sy1 = y1 * y;
double sy2 = y2 * y;
return new Bounds2D(sx1, sy1, sx2, sy2);
}
public Point2D center() {
double cx = (x1 + x2) / 2;
double cy = (y1 + y2) / 2;
return new Point2D.Double(cx, cy);
}
public double getWidth() {
return x2 - x1;
}
public double getHeight() {
return y2 - y1;
}
public Point2D normalize(Point2D point) {
double nx = (point.getX() - x1) / (x2 - x1);
double ny = (point.getY() - y1) / (y2 - y1);
return new Point2D.Double(nx, ny);
}
public Bounds2D clone() {
Bounds2D clone = new Bounds2D(x1, y1, x2, y2);
return clone;
}
public boolean equals(Bounds2D candidate) {
if (candidate == null)
return false;
boolean equals = ((x1 == candidate.x1) &&
(y1 == candidate.y1) &&
(x2 == candidate.x2) &&
(y2 == candidate.y2));
return equals;
}
public static String format(double value) {
return format.format(value);
}
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[x1=").append(format(x1)).append(", y1=").append(format(y1))
.append(", x2=").append(format(x2)).append(", y2=").append(format(y2)).append("]");
return builder.toString();
}
}
......@@ -29,7 +29,7 @@ import org.eclipse.osgi.util.NLS;
* @since 2.2
* @version 2.5.0
*/
@SuppressWarnings("javadoc")
@SuppressWarnings({"javadoc", "squid:S3008"})
public class Externalization extends NLS {
private static final String BUNDLE_NAME = "externalization"; //$NON-NLS-1$
......
......@@ -558,11 +558,18 @@ public class Point extends Float implements KdComparator<Point> {
return PointComparator.values()[axis];
}
/**
* @return
*/
public double magnitude() {
return (Math.sqrt(this.x * this.x) + (this.y * this.y));
}
/** {@inheritDoc} */
@Override
public double getDistance(Point other) {
Point delta = this.subtract(other, false);
return (Math.sqrt(delta.x * delta.x) + (delta.y * delta.y));
return delta.magnitude();
}
/** {@inheritDoc} */
......
......@@ -31,6 +31,11 @@ import lu.list.itis.dkd.dbc.annotation.NonNull;
* @version 2.3.0
*/
public class ScreenCoordinates extends CoordinateState {
private Bounds2D bounds = FULL_SCREEN;
public static final Bounds2D FULL_SCREEN = new Bounds2D(Calibration.getScreenLeft(), Calibration.getScreenTop(), Calibration.getScreenWidth(), Calibration.getScreenHeight());
/**
* Constructing an instance with the provided points.
*
......@@ -45,6 +50,13 @@ public class ScreenCoordinates extends CoordinateState {
context.setLocation(x, y);
}
/**
* @param bounds
*/
public void setBounds(Bounds2D bounds) {
this.bounds = bounds;
}
/**
* {@inheritDoc}
*/
......@@ -78,8 +90,8 @@ public class ScreenCoordinates extends CoordinateState {
/** {@inheritDoc} */
@Override
public Point normalize(Point context) {
double normx = (context.x - Calibration.getScreenLeft()) / Calibration.getScreenWidth();
double normy = (context.y - Calibration.getScreenTop()) / Calibration.getScreenHeight();
double normx = (context.x - bounds.x1) / bounds.x2;
double normy = (context.y - bounds.y1) / bounds.y2;
return new Point((float) normx, (float) normy, context.getAngle(), NormalizedCoordinates.class);
}
......@@ -87,11 +99,8 @@ public class ScreenCoordinates extends CoordinateState {
/** {@inheritDoc} */
@Override
public Point transform(Point normalized) {
// Preconditions.checkArgument((normalized.x >= 0) && (normalized.x <= 1));
// Preconditions.checkArgument((normalized.y >= 0) && (normalized.y <= 1));
double screenx = Calibration.getScreenLeft() + (normalized.x * Calibration.getScreenWidth());
double screeny = Calibration.getScreenTop() + (normalized.y * Calibration.getScreenHeight());
double screenx = bounds.x1 + (normalized.x * bounds.x2);
double screeny = bounds.y1 + (normalized.y * bounds.y2);
return new Point((float) screenx, (float) screeny, normalized.getAngle(), this.getClass());
}
......
......@@ -14,7 +14,7 @@
* 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.graph.forcedirected;
package lu.list.itis.dkd.tui.utility;
/**
* @author mack
......@@ -25,9 +25,9 @@ package lu.list.itis.dkd.tui.utility.graph.forcedirected;
// * Class Definition and Members *
// ***************************************************************************
public class Dimension {
private double x;
private double y;
public class Vector2D {
private volatile double x;
private volatile double y;
// ---------------------------------------------------------------------------
// ***************************************************************************
......@@ -40,7 +40,19 @@ public class Dimension {
*/
// ---------------------------------------------------------------------------
public Dimension(double x, double y) {
public Vector2D() {
this.x = 0;
this.y = 0;
}
// ---------------------------------------------------------------------------
/**
* @param x
* @param y
*/
// ---------------------------------------------------------------------------
public Vector2D(double x, double y) {
this.x = x;
this.y = y;
}
......@@ -56,8 +68,8 @@ public class Dimension {
*/
// ---------------------------------------------------------------------------
public static Dimension random(double scale) {
return new Dimension(scale * (Math.random() - 0.5), scale * (Math.random() - 0.5));
public static Vector2D random(double scale) {
return new Vector2D(scale * (Math.random() - 0.5), scale * (Math.random() - 0.5));
}
// ---------------------------------------------------------------------------
......@@ -68,8 +80,8 @@ public class Dimension {
*/
// ---------------------------------------------------------------------------
public Dimension add(Dimension operand) {
return new Dimension((this.x + operand.x), (this.y + operand.y));
public Vector2D add(Vector2D operand) {
return new Vector2D((this.x + operand.x), (this.y + operand.y));
}
// ---------------------------------------------------------------------------
......@@ -79,8 +91,8 @@ public class Dimension {
*/