Commit 3219dcdc authored by Eric Tobias's avatar Eric Tobias

2.1.3 - Added methods to check whether widgets are to the left, right, above,...

2.1.3 - Added methods to check whether widgets are to the left, right, above, or below and either aligned or all.

+ Added defining shape to BaseWidget
+ Added method to SpatialPositioningManager to retrieve widgets.
parent 9393cac1
2.1.3
+ Added method to retrieve widgets aligned with one another horizontally and vertically.
2.1.2
+ Added ShapeCorona and related builders.
+ Refactoring of the BaseWidget to include a shape.
+ Bug fixed for the spatial classes.
2.1.0
......
......@@ -9,7 +9,7 @@ org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled
org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
......@@ -29,7 +29,7 @@ org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
......@@ -101,6 +101,7 @@ org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedImport=warning
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
......@@ -398,3 +399,4 @@ org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
......@@ -9,10 +9,10 @@ cleanup.add_missing_override_annotations_interface_methods=true
cleanup.add_serial_version_id=true
cleanup.always_use_blocks=true
cleanup.always_use_parentheses_in_expressions=true
cleanup.always_use_this_for_non_static_field_access=true
cleanup.always_use_this_for_non_static_method_access=true
cleanup.convert_functional_interfaces=false
cleanup.convert_to_enhanced_for_loop=true
cleanup.always_use_this_for_non_static_field_access=false
cleanup.always_use_this_for_non_static_method_access=false
cleanup.convert_functional_interfaces=true
cleanup.convert_to_enhanced_for_loop=false
cleanup.correct_indentation=true
cleanup.format_source_code=true
cleanup.format_source_code_changes_only=false
......@@ -38,9 +38,9 @@ cleanup.remove_trailing_whitespaces_ignore_empty=false
cleanup.remove_unnecessary_casts=true
cleanup.remove_unnecessary_nls_tags=true
cleanup.remove_unused_imports=true
cleanup.remove_unused_local_variables=true
cleanup.remove_unused_local_variables=false
cleanup.remove_unused_private_fields=true
cleanup.remove_unused_private_members=true
cleanup.remove_unused_private_members=false
cleanup.remove_unused_private_methods=true
cleanup.remove_unused_private_types=true
cleanup.sort_members=false
......@@ -51,14 +51,15 @@ cleanup.use_blocks_only_for_return_and_throw=false
cleanup.use_lambda=true
cleanup.use_parentheses_in_expressions=true
cleanup.use_this_for_non_static_field_access=true
cleanup.use_this_for_non_static_field_access_only_if_necessary=false
cleanup.use_this_for_non_static_field_access_only_if_necessary=true
cleanup.use_this_for_non_static_method_access=true
cleanup.use_this_for_non_static_method_access_only_if_necessary=false
cleanup.use_this_for_non_static_method_access_only_if_necessary=true
cleanup.use_type_arguments=false
cleanup_profile=_CleanListME
cleanup_profile=_LIST
cleanup_settings_version=2
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=_LIST
formatter_settings_version=12
org.eclipse.jdt.ui.exception.name=e
org.eclipse.jdt.ui.gettersetter.use.is=true
......
......@@ -18,10 +18,8 @@ package lu.list.itis.dkd.tui;
import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.content.Content;
import lu.list.itis.dkd.tui.utility.Calibration;
import lu.list.itis.dkd.tui.utility.TextHelper;
import lu.list.itis.dkd.tui.widget.BaseWidget;
import com.google.common.base.Preconditions;
......@@ -323,15 +321,11 @@ public class TangibleInterfaceManager extends JComponent {
canvas2D.fillRect(0, 0, Calibration.getScreenWidth(), Calibration.getScreenHeight());
if (tangibleApplication.getContentManager() != null) {
for (Content content : tangibleApplication.getContentManager().getDrawableContents()) {
content.paint(canvas2D);
}
tangibleApplication.getContentManager().getDrawableContents().forEach(content -> content.paint(canvas2D));
}
if (tangibleApplication.getObjectManager() != null) {
for (BaseWidget widget : tangibleApplication.getObjectManager().getWidgets()) {
widget.paint(canvas2D);
}
TangibleObjectManager.getWidgets().forEach(widget -> widget.paint(canvas2D));
}
}
......
......@@ -174,6 +174,23 @@ public abstract class TangibleObjectManager {
return blobList.get(identifier);
}
/**
* Method for retrieving all handle IDs associated with the {@link BaseWidget}.
*
* @param widget
* The widget for which to retrieve all mapped handle IDs.
* @return An {@link ArrayList} containing all associated handle IDs.
*/
public static synchronized ArrayList<Integer> getIdentifier(BaseWidget widget) {
ArrayList<Integer> identifiers = new ArrayList<>();
identifiers.addAll(objectList.keySet());
identifiers.addAll(cursorList.keySet());
identifiers.addAll(blobList.keySet());
return identifiers;
}
/**
* Method invoked when the tangible is detected on the table surface for the first time.
*
......
......@@ -22,6 +22,7 @@ import lu.list.itis.dkd.tui.adapter.TangibleObject;
import lu.list.itis.dkd.tui.widget.BaseWidget;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
/**
......@@ -235,6 +236,108 @@ public class SpatialMatrix {
return new ArrayList<>(vertical.subList(index, vertical.size()));
}
/**
* Method used to retrieve all {@link TangibleObject} instances left of any of the given objects
* (by ID). Will call {@link #leftOf(int)} for each ID.
*
* @param objectIds
* The identifiers of all objects for which to retrieve objects to the left of.
* @return
*/
public synchronized List<TangibleObject> leftOf(List<Integer> objectIds) {
LinkedHashSet<TangibleObject> objects = new LinkedHashSet<>();
objectIds.forEach(identifier -> objects.addAll(leftOf(identifier)));
return new ArrayList<>(objects);
}
public synchronized List<TangibleObject> rightOf(List<Integer> objectIds) {
LinkedHashSet<TangibleObject> objects = new LinkedHashSet<>();
objectIds.forEach(identifier -> objects.addAll(rightOf(identifier)));
return new ArrayList<>(objects);
}
public synchronized List<TangibleObject> above(List<Integer> objectIds) {
LinkedHashSet<TangibleObject> objects = new LinkedHashSet<>();
objectIds.forEach(identifier -> objects.addAll(above(identifier)));
return new ArrayList<>(objects);
}
public synchronized List<TangibleObject> below(List<Integer> objectIds) {
LinkedHashSet<TangibleObject> objects = new LinkedHashSet<>();
objectIds.forEach(identifier -> objects.addAll(below(identifier)));
return new ArrayList<>(objects);
}
/**
* The method will return the left-most object, that is, the first object found based based on
* its x-axis coordinate.
*
* @return The object with the smallest x-axis coordinate. Returns the first if more than one
* are tied.
*/
public TangibleObject getLeftMostObject() {
return horizontal.get(0);
}
/**
* The method will return the right-most object, that is, the last object found based based on
* its x-axis coordinate.
*
* @return The object with the largest x-axis coordinate. Returns the last if more than one are
* tied.
*/
public TangibleObject getRightMostObject() {
return horizontal.get(horizontal.size() - 1);
}
/**
* The method will return the top-most object, that is, the first object found based based on
* its y-axis coordinate.
*
* @return The object with the smallest y-axis coordinate. Returns the first if more than one
* are tied.
*/
public TangibleObject getTopObject() {
return vertical.get(0);
}
/**
* The method will return the bottom-most object, that is, the last object found based based on
* its y-axis coordinate.
*
* @return The object with the largest y-axis coordinate. Returns the last if more than one are
* tied.
*/
public TangibleObject getBottomObject() {
return vertical.get(vertical.size() - 1);
}
/**
* Simple getter method for horizontal.
*
* @return The value of horizontal.
*/
public ArrayList<TangibleObject> getHorizontal() {
return horizontal;
}
/**
* Simple getter method for vertical.
*
* @return The value of vertical.
*/
public ArrayList<TangibleObject> getVertical() {
return vertical;
}
String printLists() {
StringBuilder verticalBuilder = new StringBuilder();
StringBuilder horizontalbuilder = new StringBuilder();
......
......@@ -17,14 +17,23 @@
package lu.list.itis.dkd.tui.space;
import lu.list.itis.dkd.tui.TangibleObjectManager;
import lu.list.itis.dkd.tui.adapter.TangibleObject;
import lu.list.itis.dkd.tui.event.SpatialEvent;
import lu.list.itis.dkd.tui.event.SpatialEventListener;
import lu.list.itis.dkd.tui.utility.Point;
import lu.list.itis.dkd.tui.utility.PropertiesFetcher;
import lu.list.itis.dkd.tui.widget.BaseWidget;
import com.google.common.base.Preconditions;
import java.awt.Rectangle;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -43,7 +52,7 @@ import java.util.logging.Logger;
*
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.1
* @version 2.1.2
* @version 2.1.3
*/
public class SpatialPositioningManager implements SpatialEventListener {
......@@ -53,6 +62,20 @@ public class SpatialPositioningManager implements SpatialEventListener {
private SpatialMatrix spatialMatrix = new SpatialMatrix();
/**
* Enumeration showing all alignment types this class supports.
*
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.1
* @version 2.1.3
*/
public enum SpatialAlignment {
/** Whether elements are aligned on a horizontal plane. */
HORIZONTAL,
/** Whether elements are aligned on a vertical plane. */
VERTICAL;
}
/**
* Constructor initializing all fields.
*/
......@@ -69,15 +92,6 @@ public class SpatialPositioningManager implements SpatialEventListener {
return INSTANCE;
}
/**
* Simple getter method for spatialMatrix.
*
* @return The value of spatialMatrix.
*/
public SpatialMatrix getSpatialMatrix() {
return spatialMatrix;
}
/**
* Method for configuring a {@link Logger} with several properties as given by the loader
* properties file.
......@@ -127,18 +141,205 @@ public class SpatialPositioningManager implements SpatialEventListener {
logger.log(Level.SEVERE, "Spatial event type \"" + event.getType() + "\" not recognized!"); //$NON-NLS-1$ //$NON-NLS-2$
break;
}
System.out.println(spatialMatrix.printLists());
System.out.println("Left of " + event.getSource().getObjectId() + ": " +
spatialMatrix.leftOf(event.getSource().getObjectId())); // $NON-NLS-1$
// //$NON-NLS-2$
System.out.println("Right of " + event.getSource().getObjectId() + ": " +
spatialMatrix.rightOf(event.getSource().getObjectId())); // $NON-NLS-1$
// //$NON-NLS-2$
System.out.println("Above " + event.getSource().getObjectId() + ": " +
spatialMatrix.above(event.getSource().getObjectId())); // $NON-NLS-1$
// //$NON-NLS-2$
System.out.println("Below " + event.getSource().getObjectId() + ": " +
spatialMatrix.below(event.getSource().getObjectId())); // $NON-NLS-1$
// //$NON-NLS-2$
// System.out.println(spatialMatrix.printLists());
// System.out.println("Left of " + event.getSource().getObjectId() + ": " +
// spatialMatrix.leftOf(event.getSource().getObjectId())); // $NON-NLS-1$
// // //$NON-NLS-2$
// System.out.println("Right of " + event.getSource().getObjectId() + ": " +
// spatialMatrix.rightOf(event.getSource().getObjectId())); // $NON-NLS-1$
// // //$NON-NLS-2$
// System.out.println("Above " + event.getSource().getObjectId() + ": " +
// spatialMatrix.above(event.getSource().getObjectId())); // $NON-NLS-1$
// // //$NON-NLS-2$
// System.out.println("Below " + event.getSource().getObjectId() + ": " +
// spatialMatrix.below(event.getSource().getObjectId())); // $NON-NLS-1$
// // //$NON-NLS-2$
}
/**
* The method will return the left-most widget, that is, the first widget found based based on
* its x-axis coordinate. The retrieval is based on the underlying handles.
*
* @return The widget with the smallest x-axis coordinate. Returns the first if more than one
* are tied.
*/
public BaseWidget getLeftMostWidget() {
return TangibleObjectManager.getWidget(spatialMatrix.getLeftMostObject().getObjectId());
}
/**
* The method will return the right-most widget, that is, the last widget found based based on
* its x-axis coordinate. The retrieval is based on the underlying handles.
*
* @return The widget with the largest x-axis coordinate. Returns the last if more than one are
* tied.
*/
public BaseWidget getRightMostWidget() {
return TangibleObjectManager.getWidget(spatialMatrix.getRightMostObject().getObjectId());
}
/**
* The method will return the top-most widget, that is, the first widget found based based on
* its y-axis coordinate. The retrieval is based on the underlying handles.
*
* @return The widget with the smallest y-axis coordinate. Returns the first if more than one
* are tied.
*/
public BaseWidget getTopWidget() {
return TangibleObjectManager.getWidget(spatialMatrix.getTopObject().getObjectId());
}
/**
* The method will return the bottom-most widget, that is, the last widget found based based on
* its y-axis coordinate. The retrieval is based on the underlying handles.
*
* @return The widget with the largest y-axis coordinate. Returns the last if more than one are
* tied.
*/
public BaseWidget getBottomWidget() {
return TangibleObjectManager.getWidget(spatialMatrix.getBottomObject().getObjectId());
}
/**
* Method for returning a list of widgets that are to the left of a given widget. The method
* either returns all objects no matter their alignment with this widget, or only returns
* horizontally aligned widgets, that is, more or less on the same horizontal plane as the given
* widget. The alignment is determined by the shape of the widget.
*
* @param widget
* The widget to retrieve all other aligned or non-aligned widgets to the left.
* @param aligned
* Whether all widgets (to the left) are retrieved or only those that are horizontally
* aligned.
* @return A list of widgets that are to the left, either all or only those that are aligned.
*/
public ArrayList<BaseWidget> getWidgetsToLeftOf(BaseWidget widget, boolean aligned) {
ArrayList<BaseWidget> widgets = objectsToWidgets(spatialMatrix.leftOf(TangibleObjectManager.getIdentifier(widget)));
if (aligned) {
widgets.retainAll(getAlignedWidgets(widgets, widget, SpatialAlignment.HORIZONTAL));
}
return widgets;
}
/**
* Method for returning a list of widgets that are to the right of a given widget. The method
* either returns all objects no matter their alignment with this widget, or only returns
* horizontally aligned widgets, that is, more or less on the same horizontal plane as the given
* widget. The alignment is determined by the shape of the widget.
*
* @param widget
* The widget to retrieve all other aligned or non-aligned widgets to the right.
* @param aligned
* Whether all widgets (to the left) are retrieved or only those that are horizontally
* aligned.
* @return A list of widgets that are to the left, either all or only those that are aligned.
*/
public ArrayList<BaseWidget> getWidgetsToRightOf(BaseWidget widget, boolean aligned) {
ArrayList<BaseWidget> widgets = objectsToWidgets(spatialMatrix.rightOf(TangibleObjectManager.getIdentifier(widget)));
if (aligned) {
widgets.retainAll(getAlignedWidgets(widgets, widget, SpatialAlignment.HORIZONTAL));
}
return widgets;
}
/**
* Method for returning a list of widgets that are above of a given widget. The method either
* returns all objects no matter their alignment with this widget, or only returns vertically
* aligned widgets, that is, more or less on the same vertical plane as the given widget. The
* alignment is determined by the shape of the widget.
*
* @param widget
* The widget to retrieve all other aligned or non-aligned widgets above.
* @param aligned
* Whether all widgets (above) are retrieved or only those that are vertically aligned.
* @return A list of widgets that are above, either all or only those that are aligned.
*/
public ArrayList<BaseWidget> getWidgetsAboveOf(BaseWidget widget, boolean aligned) {
ArrayList<BaseWidget> widgets = objectsToWidgets(spatialMatrix.above(TangibleObjectManager.getIdentifier(widget)));
if (aligned) {
widgets.retainAll(getAlignedWidgets(widgets, widget, SpatialAlignment.VERTICAL));
}
return widgets;
}
/**
* Method for returning a list of widgets that are below of a given widget. The method either
* returns all objects no matter their alignment with this widget, or only returns vertically
* aligned widgets, that is, more or less on the same vertical plane as the given widget. The
* alignment is determined by the shape of the widget.
*
* @param widget
* The widget to retrieve all other aligned or non-aligned widgets below.
* @param aligned
* Whether all widgets (above) are retrieved or only those that are vertically aligned.
* @return A list of widgets that are below, either all or only those that are aligned.
*/
public ArrayList<BaseWidget> getWidgetsBelowOf(BaseWidget widget, boolean aligned) {
ArrayList<BaseWidget> widgets = objectsToWidgets(spatialMatrix.below(TangibleObjectManager.getIdentifier(widget)));
if (aligned) {
widgets.retainAll(getAlignedWidgets(widgets, widget, SpatialAlignment.VERTICAL));
}
return widgets;
}
/**
* Method for compiling a set of widgets which are not horizontally aligned with a given widget,
* that is, whose max and min y coordinate given by the bounds of its defining shape, do not
* include any of the centre points of the given widget's handles.<br />
* <b>Note</b> that the method will skip all widgets which do not have a defining shape. If the
* result seems to be off, please make sure all widgets do have a defining shape.
*
* @param widgets
* The list to parse and compile a set of aligned widgets off.
* @param widget
* The widget whose handle centres to use to check for alignment.
* @return A {@link Set} of {@link BaseWidget} such that for at least one of the parameterized
* widget's handle centre y coordinates lies within the inclusive bounds defined by the
* returned widgets defining shape's max and minimum y-axis coordinates.
*/
private Set<BaseWidget> getAlignedWidgets(ArrayList<BaseWidget> widgets, BaseWidget widget, SpatialAlignment alignment) {
Collection<Point> centres = widget.getPositions().values();
Set<BaseWidget> alignedWidgets = new HashSet<>();
for (BaseWidget _widget : widgets) {
if (null == _widget.getDefiningShape()) {
continue;
}
for (Point centre : centres) {
if (areAligned(_widget.getDefiningShape().getBounds(), centre, alignment)) {
alignedWidgets.add(_widget);
}
}
}
return alignedWidgets;
}
private boolean areAligned(Rectangle bounds, Point point, SpatialAlignment alignment) {
switch (alignment) {
case HORIZONTAL:
return (Double.compare(point.getY(), bounds.getMinY()) >= 0 || Double.compare(point.getY(), bounds.getMaxY()) <= 0);
case VERTICAL:
return (Double.compare(point.getX(), bounds.getMinX()) >= 0 || Double.compare(point.getX(), bounds.getMaxX()) <= 0);
default:
logger.log(Level.WARNING, "The spatial alignment constant " + alignment + " could not be recognized!"); //$NON-NLS-1$ //$NON-NLS-2$
return false;
}
}
/**
* Method used to create an {@link ArrayList} of {@link BaseWidget} instances based on an
* existing {@link List} of {@link TangibleObject} instances.
*
* @param objects
* The list of objects to convert.
* @return A list of {@link TangibleObject} instances corresponding to the input list of
* objects. Order is preserved.
*/
public static ArrayList<BaseWidget> objectsToWidgets(List<TangibleObject> objects) {
ArrayList<BaseWidget> widgets = new ArrayList<>(objects.size());
objects.forEach(object -> widgets.add(TangibleObjectManager.getWidget(object.getObjectId())));
return widgets;
}
}
\ No newline at end of file
......@@ -28,6 +28,7 @@ import lu.list.itis.dkd.tui.widget.corona.Corona;
import com.google.common.collect.Multimap;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
......@@ -53,6 +54,8 @@ public class BaseWidget {
protected String name;
@Nullable
protected NetworkAdapter networkAdapter;
@Nullable
protected Shape definingShape;
/**
* Constructor setting all fields as by the values specified by the builder.
......@@ -65,6 +68,7 @@ public class BaseWidget {
positions = builder.positions;
name = builder.name;
networkAdapter = builder.networkAdapter;
definingShape = builder.definingShape;
}
......@@ -229,8 +233,17 @@ public class BaseWidget {
* @return The {@link NetworkAdapter} held by this instance or <code>null</code> if no adapter
* was assigned.
*/
@Nullable
public NetworkAdapter getNetworkAdapter() {
public @Nullable NetworkAdapter getNetworkAdapter() {
return networkAdapter;
}
/**
* Simple getter method for definingShape.
*
* @return The value of definingShape.
*/
public @Nullable Shape getDefiningShape() {
return definingShape;
}
}
\ No newline at end of file
......@@ -26,6 +26,7 @@ import lu.list.itis.dkd.tui.widget.corona.Corona;
import com.google.common.collect.Multimap;
import com.google.common.collect.TreeMultimap;
import java.awt.Shape;
import java.util.HashMap;
/**
......@@ -34,7 +35,7 @@ import java.util.HashMap;
* @author Eric TOBIAS [eric.tobias@list.lu]
* @author Nicolas Gilmard