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 78ca958e authored by Nico Mack's avatar Nico Mack

Coronas are now tetherable

Added ShapeUtils class
parent 3970bcd1
......@@ -123,6 +123,7 @@ OBJECT_NODE=object
OBJECTS_NODE=objects
OPACITY_NODE=opacity
ORIGIN_NODE=origin
ORIGIN_OFFSET_NODE=originOffset
OUTER_RADIUS_NODE=outerRadius
PAGE_NODE=page
PATH_NODE=path
......
......@@ -158,6 +158,7 @@ public class Externalization extends NLS {
public static String OBJECTS_NODE;
public static String OPACITY_NODE;
public static String ORIGIN_NODE;
public static String ORIGIN_OFFSET_NODE;
public static String OUTER_RADIUS_NODE;
public static String PAGE_NODE;
public static String PATH_NODE;
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. 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.utility;
import java.awt.Shape;
import java.awt.geom.PathIterator;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
public class ShapeUtils {
// ***************************************************************************
// * Constants *
// ***************************************************************************
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
private ShapeUtils() {
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* @param shape
* @return
*/
// ---------------------------------------------------------------------------
public static Point centroidOf(Shape shape) {
final double flatness = 0.1;
double coords[] = new double[6];
double centroidX = 0;
double centroidY = 0;
double x0 = Double.NaN;
double y0 = Double.NaN;
double x1 = 0;
double y1 = 0;
double partialArea = 0;
double signedArea = 0;
PathIterator pi = shape.getPathIterator(null, flatness);
while (!pi.isDone()) {
int s = pi.currentSegment(coords);
switch (s) {
case PathIterator.SEG_MOVETO:
if (Double.isNaN(x0)) {
x0 = coords[0];
y0 = coords[1];
}
break;
case PathIterator.SEG_LINETO:
x1 = coords[0];
y1 = coords[1];
partialArea = x0 * y1 - x1 * y0;
signedArea += partialArea;
centroidX += (x0 + x1) * partialArea;
centroidY += (y0 + y1) * partialArea;
x0 = x1;
y0 = y1;
break;
case PathIterator.SEG_CLOSE:
// Ignore
break;
case PathIterator.SEG_QUADTO:
throw new AssertionError(
"SEG_QUADTO in flattening path iterator"); //$NON-NLS-1$
case PathIterator.SEG_CUBICTO:
throw new AssertionError(
"SEG_CUBICTO in flattening path iterator"); //$NON-NLS-1$
}
pi.next();
}
signedArea *= 0.5;
centroidX /= (6d * signedArea);
centroidY /= (6d * signedArea);
return new Point((float) centroidX, (float) centroidY, 0f, ScreenCoordinates.class);
}
}
......@@ -264,6 +264,28 @@ public class ModalWidget extends TetherableWidget {
return coronaBundles.get(bundle);
}
// ---------------------------------------------------------------------------
/**
* Method for retrieving all bundled coronas of a particular class.
*
* @param <T>
* The type of the class of bundled corona to retrieve.
* @param clazz
* The class of corona's to retrieve.
* @return A list of coronas matching the criterion.
*/
// ---------------------------------------------------------------------------
public <T> List<T> getBundledCoronas(Class<T> clazz) {
List<T> results = new ArrayList<>();
for (CoronaBundle bundle : coronaBundles.values()) {
results.addAll(bundle.getCoronas(clazz));
}
return results;
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * End of Class *
......
......@@ -25,14 +25,20 @@ 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.ScriptBootstrapper;
import lu.list.itis.dkd.tui.bootstrapping.ShapeBootstrapper;
import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.scripting.Script;
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;
import lu.list.itis.dkd.tui.widget.corona.Corona;
import com.google.common.base.Strings;
import com.jgoodies.common.base.Preconditions;
import org.jdom2.Element;
import java.awt.Shape;
......@@ -95,6 +101,13 @@ public abstract class CoronaBuilder<B extends CoronaBuilder<B>> {
/** */
public List<Script> scripts = null;
public double tetheringDistance;
public Point originOffset = new Point();
public boolean rotatesWithTether = false;
public boolean exclusive = true;
public List<String> providers = new ArrayList<>();
public List<String> receivers = new ArrayList<>();
/**
......@@ -181,8 +194,78 @@ public abstract class CoronaBuilder<B extends CoronaBuilder<B>> {
}
}
}
Element tetherableNode = rootElement.getChild(Externalization.TETHERABLE_NODE);
if (null != tetherableNode) {
this.originOffset = new Point(tetherableNode.getChild(Externalization.ORIGIN_OFFSET_NODE), context, callback);
Element distanceNode = tetherableNode.getChild(Externalization.DISTANCE_NODE);
if (distanceNode != null) {
this.tetheringDistance = this.buildDistance(distanceNode, context, callback);
}
Element providersNode = tetherableNode.getChild(Externalization.PROVIDERS_NODE);
this.providers = this.buildProviders(providersNode, context, callback);
Element receiversNode = tetherableNode.getChild(Externalization.RECEIVERS_NODE);
this.receivers = this.buildReceivers(receiversNode, context, callback);
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 {
Preconditions.checkNotNull(distanceNode, "Distance node MUST not be null!"); //$NON-NLS-1$
double distance = BootstrappingUtils.getContentAsDouble(distanceNode, null, BootstrappingUtils.MANDATORY, null, context);
Point convertor = new Point(ScreenCoordinates.class);
CoordinateState coordinates;
String state = BootstrappingUtils.getContentAsString(distanceNode, Externalization.STATE_NODE, BootstrappingUtils.OPTIONAL, null, context);
if (!Strings.isNullOrEmpty(state)) {
coordinates = CoordinateStateBootstrapper.getCoordinateState(distanceNode, convertor, context, callback);
} else {
coordinates = new ScreenCoordinates(convertor, 0, 0);
}
convertor.setState(coordinates);
convertor.setLocation(distance, distance);
convertor.toCoordinates(ScreenCoordinates.class);
distance = convertor.getX();
return distance;
}
// ---------------------------------------------------------------------------
private List<String> buildProviders(@Nullable Element providersNode, BootstrapContext context, BootstrapCallback callback) throws BuildException {
List<String> providers = new ArrayList<>();
if (providersNode != null) {
for (Element providerNode : providersNode.getChildren(Externalization.PROVIDER_NODE)) {
providers.add(BootstrappingUtils.getContentAsString(providerNode, null, BootstrappingUtils.MANDATORY, null, context));
}
}
return providers;
}
// ---------------------------------------------------------------------------
private List<String> buildReceivers(@Nullable Element receiversNode, BootstrapContext context, BootstrapCallback callback) throws BuildException {
List<String> receivers = new ArrayList<>();
if (receiversNode != null) {
for (Element providerNode : receiversNode.getChildren(Externalization.RECEIVER_NODE)) {
receivers.add(BootstrappingUtils.getContentAsString(providerNode, null, BootstrappingUtils.MANDATORY, null, context));
}
}
return receivers;
}
// ---------------------------------------------------------------------------
/**
* Method used to set the initialTranslation of the corona from its centre.
*
......@@ -191,6 +274,8 @@ public abstract class CoronaBuilder<B extends CoronaBuilder<B>> {
* values of the centre.
* @return An instance of the builder for chain calling.
*/
// ---------------------------------------------------------------------------
@SuppressWarnings("unchecked")
public B withInitialTranslation(Point translation) {
this.initialTranslation = translation;
......
......@@ -35,6 +35,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
......@@ -59,6 +60,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class TetherManager {
private Tetherable tetherable;
private Point originOffset;
private Map<Tetherable, Tether> potentialTethers;
private Map<Tetherable, Tether> currentlyTethered;
private boolean exclusive;
......@@ -91,6 +93,7 @@ public class TetherManager {
* @param exclusive
*/
public TetherManager(Tetherable tetherable, boolean exclusive) {
this.originOffset = null;
this.currentlyTethered = new ConcurrentHashMap<>();
this.potentialTethers = new ConcurrentHashMap<>();
this.tetherable = tetherable;
......@@ -111,6 +114,34 @@ public class TetherManager {
return distance;
}
// ---------------------------------------------------------------------------
/**
* Computes the absolute drawing point in screen coordinates, taking into account the coronas'
* center, its initial translation as well as the optional center of the asset to be drawn.
*
* @param assetCentre
* specifies the optional center of the asset to be drawn. Specify <code>null</code> if not
* required
* @return a point expressed in absolute screen coordinates
*/
// ---------------------------------------------------------------------------
private Point getTetherOrigin(Point newPosition) {
Point tetherOrigin = new Point(ScreenCoordinates.class);
if (this.originOffset != null) {
Point position = newPosition.toCoordinates(ScreenCoordinates.class);
AffineTransform originTransform = AffineTransform.getTranslateInstance(position.x, position.y);
originTransform.rotate(newPosition.getAngle() + this.originOffset.getAngle());
originTransform.translate(this.originOffset.x, this.originOffset.y);
originTransform.transform(tetherOrigin, tetherOrigin);
} else {
tetherOrigin = newPosition.toCoordinates(ScreenCoordinates.class);
}
return tetherOrigin;
}
// ---------------------------------------------------------------------------
/**
* Calculates the intersection of a line given by points a and b with a circle at position origin
......@@ -289,6 +320,17 @@ public class TetherManager {
return this.exclusive;
}
// ---------------------------------------------------------------------------
/**
* Sets the origin offset to be applied to the new position specified when calling the move method.
*
* @param offset
*/
// ---------------------------------------------------------------------------
public void setOriginOffset(Point offset) {
this.originOffset = offset;
}
// ---------------------------------------------------------------------------
/**
* Sets the list of potential providers for tethers managed by this instance.
......@@ -715,9 +757,9 @@ public class TetherManager {
public List<Tether> move(Point newPosition) {
List<Tether> draggedTethers = null;
Point tetherOrigin = newPosition.toScreenCoordinates();
Point tetherOrigin = this.getTetherOrigin(newPosition); // newPosition.toScreenCoordinates();
this.tetherable.setTetherOrigin(newPosition);
this.tetherable.setTetherOrigin(tetherOrigin);
double shortestTether = this.shortestTether();
double tetheringDistance = this.tetherable.getTetheringDistance();
double angle = 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment