![JAR search and dependency download from the Maven repository](/logo.png)
net.sf.gluebooster.java.booster.basic.math.GeometryBoostUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gb-basic Show documentation
Show all versions of gb-basic Show documentation
Basic classes to support the development of applications. There should be as few dependencies on other frameworks as possible.
The newest version!
package net.sf.gluebooster.java.booster.basic.math;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import edu.uci.ics.jung.algorithms.cluster.BicomponentClusterer;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.UndirectedGraph;
import edu.uci.ics.jung.graph.UndirectedSparseGraph;
import net.sf.gluebooster.java.booster.basic.container.Tuple;
import net.sf.gluebooster.java.booster.basic.gui.awt.BoostedRectangle;
import net.sf.gluebooster.java.booster.basic.gui.awt.ShapeComparator;
import net.sf.gluebooster.java.booster.basic.math.graph.GraphSequenceClusterer;
import net.sf.gluebooster.java.booster.basic.math.graph.JungGraphBoostUtils;
import net.sf.gluebooster.java.booster.basic.transformation.CallableDelegate;
import net.sf.gluebooster.java.booster.essentials.eventsCommands.Callable;
import net.sf.gluebooster.java.booster.essentials.logging.LogBooster;
import net.sf.gluebooster.java.booster.essentials.meta.objects.DisplayConfiguration;
import net.sf.gluebooster.java.booster.essentials.meta.objects.ObjectDescription;
import net.sf.gluebooster.java.booster.essentials.utils.ContainerBoostUtils;
/**
* Geometric objects often are represented here as graphs. The vertices are shapes (example rectangle). Lines bewtween the vertices are edges
*
* @author CBauer
* @defaultParamText xTranslation translation along the x-axis
* @defaultParamText yTranslation translation along the y-axis
* @defaultParamText stopAfterFirstOverlapping should the computation be stopped after the first overlapping (true) or compute all overlappings (false)
*
*/
public class GeometryBoostUtils {
/**
* The logger
*/
private static LogBooster log = new LogBooster(GeometryBoostUtils.class);
/**
* Should each step of computations be displayed. Used for debugging.
*/
private static boolean displayEachStep = false;
/**
* Counter to create unique ids of placeholders
*/
private static int placeholderCounter = 0;
/**
* Translates some objects
*
* @param geometricObjects
* the vertices are translated
* @param xTranslation
* @param yTranslation
*/
public static void translate(
Graph geometricObjects, int xTranslation,
int yTranslation) {
for (Object geometricObject : geometricObjects.getVertices()) {
translate(geometricObject, xTranslation, yTranslation);
}
}
/**
* Translates one object
*
* @param geometricObject
* this object will be translated
*/
public static void translate(Object geometricObject, int xTranslation,
int yTranslation) {
if (geometricObject instanceof Rectangle) {
Rectangle rect = (Rectangle) geometricObject;
setPosition(rect, rect.x + xTranslation, rect.y + yTranslation, 0);
} else {
throw new IllegalStateException(
"geometric object not (yet) supported " + geometricObject);
}
}
/**
* Sets the position of an object
*
* @param geometricObject
* will be repositioned
* @param x
* the new x value
* @param y
* the new y value
* @param translation
* an additional translation to the x and y
*/
public static void setPosition(Object geometricObject, int x, int y,
int translation) {
if (geometricObject instanceof Rectangle) {
Rectangle rect = (Rectangle) geometricObject;
rect.x = translation + x;
rect.y = translation + y;
} else {
throw new IllegalStateException(
"geometric object not (yet) supported " + geometricObject);
}
}
/**
* Repositions some objects so that they don't overlap
*
* @param graph
* the vertices are repositioned
* @param constraints
* additional constraints
*/
public static void repositionWithoutOverlapping(
Graph graph,
// Collection extends Shape> shapes,
// MultiMap extends Shape, ? extends Shape> connectedShapes,
GeometryPositionConstraint constraints) {
boolean doDisplayEachStep = displayEachStep;
if (doDisplayEachStep) {
JungGraphBoostUtils.displayShapes(new DisplayConfiguration("Origin", graph));
if (!GeometryBoostUtils.hasOverlappingVertices(graph, true)
.isEmpty()) {
log.info("Overlapping before repositioning "
+ graph.getVertices().toString());
}
}
if (repositionSpecialStructures(graph, constraints)) {
return;
}
// TODO consider the constraints
addMargin(graph, constraints.getMinimumDistance());
if (doDisplayEachStep) {
JungGraphBoostUtils.displayShapes(new DisplayConfiguration("added margin",
graph));
}
repositionWithoutOverlappingByMovingUpOrRight(graph, constraints);
//repositionWithoutOverlappingByMovingToTheRightAndUp(shapes,
// minimumDistance);
String afterStep = "";
if (doDisplayEachStep) {
afterStep = graph.getVertices().toString();
JungGraphBoostUtils.displayShapes(new DisplayConfiguration(
"after repositionWithoutOverlappingByMovingUp", graph));
if (!GeometryBoostUtils.hasOverlappingVertices(graph, true)
.isEmpty()) {
log.info("Overlapping after repositioning " + afterStep);
}
}
// repositionWithoutOverlappingWithEdges(shapes, connectedShapes);
//
// if (displayEachStep) {
// Refactor.displayShapes(
// "after repositionWithoutOverlappingWithEdges", shapes,
// connectedShapes);
// if (GeometryBoostUtils.isOverlapping(shapes)) {
// log.info("Overlapping after repositionWithoutOverlappingWithEdges. Before: "
// + afterStep + "\n after " + shapes.toString());
// }
// }
}
// TODO refactor before using
// private static void repositionWithoutOverlappingWithEdges(
// Graph graph
// // Collection extends Shape> shapes,
// // MultiMap extends Shape, ? extends Shape> connectedShapes
// ) {
//
// if (!GeometryBoostUtils.hasOverlappingVertices(graph, true).isEmpty()) {
// log.info("shapes are already overlapping. Therefore nothing is done");
// return;
// }
//
// boolean didSomething = false;
//
// int margin = 10;
//
// /*
// * // add edges with the opposite directions (so there are at least two
// * // edges for each connection A-B, one from A to B, one from B to A)
// * MultiHashMap edges = new MultiHashMap(connectedShapes);
// * for (Entry extends Shape, ?> entry : connectedShapes.entrySet()) {
// * Collection extends Shape> collection = (Collection extends
// * Shape>) entry .getValue(); for (Shape s : collection) { edges.put(s,
// * entry.getKey()); } }
// */
//
// int counter = 0;
// // reposition one object then begin anew
// do {
// // Step 1
// // Sort the shapes (vertices) so that the highest y,x is first
// // (first
// // sort by y then by x)
// ArrayList ordered = new ArrayList(graph.getVertices());
// Collections.sort(ordered, ShapeComparator.YX_COMPARATOR);
// // lowest is first
// Collections.reverse(ordered);
// // now the highest is first
// int size = ordered.size();
//
// for (int index = 0; index < size; index++) {
//
// Shape shape = ordered.get(index);
// Rectangle bounds = shape.getBounds();
// int minX = bounds.x;
// int maxX = bounds.x + bounds.width;
// int minY = bounds.y;
// int maxY = bounds.y + bounds.height;
//
// int prefereredMovementUp = bounds.height + 10;
// // use a margin additionally to the height.
//
// // Step A find overlappings with edges
// // Overlappings can only occur with shapes that are higher up
// // (before) or a least their upper end must be at least as high
// // as
// // the lower end of the shape.
// // Handle at most one overlapping (stop if didSomething = true)
// for (int j = 0; j < size && !didSomething; j++) {
// if (j == index) {
// continue; // do not compore the shape with itself
// }
//
// Shape otherShape = ordered.get(j);
//
// Shape endpoint = isOverlappingWithEdgeOf(shape, otherShape,
// edges);
// // If there is an overlapping:
// if (endpoint != null) {
//
// HashSet connectedComponents = new HashSet(
// edges.get(shape));
// while (connectedComponents.remove(shape)) {
// // remove all connections with itself
// }
//
// // has the shape a connected component that is lying
// // more up (y is higher)
// boolean hasUpper = false;
// for (Shape connectedShape : connectedComponents) {
// if (connectedShape.getBounds().y > bounds.y) {
// hasUpper = true;
// }
// }
//
// // If the shape has no edge: move the shape up. If
// // possible it's
// // height (+ a margin); if it would overlap then even
// // more up
//
// // If the shape has an edge to an upper other shape:
// // move the shape
// // up (if possible it's height (+ a margin))
//
// if (connectedComponents.isEmpty() || hasUpper) {
// int up = getPossibleUpMovement(ordered, false,
// shape, bounds.height + margin);
// if (up > 0) {
// translate(shape, 0, up);
// didSomething = true;
// if (!GeometryBoostUtils.hasOverlappingVertices(
// graph,
// true).isEmpty()) {
// log.warn("shapes are overlapping after translation ");
// translate(shape, 0, -up); // undo
// // you may set a breakpoint here for
// // analysis
// getPossibleUpMovement(ordered, false,
// shape, bounds.height + margin);
//
// }
//
// }
// } else {
//
// // Else move the endpoints of the edge up. If
// // possible
// // the height of
// // the shape (+ a margin)
//
// int up1 = getPossibleUpMovement(ordered, false,
// otherShape, bounds.height + margin);
//
// int up2 = getPossibleUpMovement(ordered, false,
// endpoint, bounds.height + margin);
//
// int up = Math.min(up1, up2);
//
// if (up > 0) {
// translate(otherShape, 0, up);
// translate(endpoint, 0, up);
// didSomething = true;
// if (!GeometryBoostUtils.hasOverlappingVertices(
// graph, true).isEmpty()) {
// log.warn("shapes are overlapping after translation ");
// // undo
// translate(otherShape, 0, -up);
// translate(endpoint, 0, -up);
//
// // you may set a breakpoint here for
// // analysis
// getPossibleUpMovement(ordered, false,
// otherShape, bounds.height + margin);
// getPossibleUpMovement(ordered, false,
// endpoint, bounds.height + margin);
//
// }
//
// }
//
// }
// }
// }
// }
// } while (didSomething && counter++ < 100);
//
// }
/**
* Computes how much a shape can be moved up (y-axis) without intersecting other shapes
*
* @param allShapesOrdered
* all shapes
* @param orderedIncremental
* are the shapes ordered incrementally (true) or decrementally (false)
* @param shapeToMoveUp
* the shape to move up
* @param wantedMovementUp
* the wanted distance to move
* @return the computed distance
*/
private static int getPossibleUpMovement(ArrayList allShapesOrdered,
boolean orderedIncremental, Shape shapeToMoveUp,
int wantedMovementUp) {
int result = wantedMovementUp;
Rectangle bounds = shapeToMoveUp.getBounds();
Rectangle sweptArea = new Rectangle(bounds.x, bounds.y, bounds.width,
bounds.height + wantedMovementUp);
int start, end;
if (orderedIncremental) {
start = allShapesOrdered.indexOf(shapeToMoveUp) + 1;
end = allShapesOrdered.size();
} else {
start = 0;
end = allShapesOrdered.indexOf(shapeToMoveUp);
}
int margin = 5;
for (int i = start; i < end; i++) {
Shape shape = allShapesOrdered.get(i);
Rectangle shapeBounds = shape.getBounds();
if (shapeBounds.intersects(sweptArea)) {
result = Math.min(result, shapeBounds.y
- (bounds.y + bounds.height) - margin);
// -5 is margin
// shapeBounds----------
// |
// |
// L shapeBounds.y -----
// # margin
// .
// .
// .result
// .
// .
// bounds.y + bounds.height ----------
// |
// |
// |
// L bounds.y ------------------------
}
}
if (result < 0) {
result = 0;
}
return result;
}
/**
* Test whether a shape is overlapping with an edge
*
* @param graph
* the graph with the edges and vertices
* @param positionOfEdge
* SwingConstants TOP = MinX, MinY Center = Middle of shape
* @param shape
* @param edgeEndpoint
* on edpoint of the edge
* @param connectedShapes
* @return the vertex, the edge and the endpoints of the edge
*/
public static Collection, Void>> hasOverlappingVertexEdge(
Graph graph, boolean stopAfterFirstOverlapping,
int positionOfEdge
/*
, Shape shape,
Shape edgeEndpoint,
MultiMap extends Shape, ? extends Shape> connectedShapes*/) {
HashSet, Void>> result = new HashSet, Void>>();
for (Edge edge: graph.getEdges()){
edu.uci.ics.jung.graph.util.Pair endpoints= graph.getEndpoints(edge);
Rectangle bounds1 = endpoints.getFirst().getBounds();
Rectangle bounds2 = endpoints.getSecond().getBounds();
Line2D.Double line = getLineBetween(endpoints.getFirst(),
endpoints.getSecond(), positionOfEdge);
if (SwingConstants.CENTER == positionOfEdge) {
line = new Line2D.Double(bounds1.getCenterX(),
bounds1.getCenterY(), bounds2.getCenterX(),
bounds2.getCenterY());
} else if (SwingConstants.TOP == positionOfEdge) {
line = new Line2D.Double(bounds1.getMinX(), bounds1.getMinY(),
bounds2.getMinX(), bounds2.getMinY());
} else {
throw new IllegalArgumentException(
"position not supported, only SwingConstants.TOP and CENTER: "
+ positionOfEdge);
}
for (Shape vertex : graph.getVertices()) {
if (vertex != endpoints.getFirst()
&& vertex != endpoints.getSecond()
&& line.intersects(vertex.getBounds())) {
result.add(Tuple.triple(vertex, edge, graph.getEndpoints(edge)));
if (stopAfterFirstOverlapping) {
return result;
}
}
}
}
return result;
}
/**
* Computes the line between two shapes
*
* @param shape1
* the first shape
* @param shape2
* the second shape
* @param positionOfEdge
* where should the edge start/end (SwingConstants)
* @return the computed line
*/
private static Line2D.Double getLineBetween(Shape shape1, Shape shape2,
int positionOfEdge) {
Rectangle bounds1 = shape1.getBounds();
Rectangle bounds2 = shape2.getBounds();
Line2D.Double line;
if (SwingConstants.CENTER == positionOfEdge) {
line = new Line2D.Double(bounds1.getCenterX(),
bounds1.getCenterY(), bounds2.getCenterX(),
bounds2.getCenterY());
} else if (SwingConstants.TOP == positionOfEdge) {
line = new Line2D.Double(bounds1.getMinX(), bounds1.getMinY(),
bounds2.getMinX(), bounds2.getMinY());
} else {
throw new IllegalArgumentException(
"position not supported, only SwingConstants.TOP and CENTER: "
+ positionOfEdge);
}
return line;
}
/**
* Has the graph overlapping edges
*
* @param graph
* the graph which edges are to be inspected
* @param stopAfterFirstOverlapping
* should the algorithm stop after finding the first overlapping
* @param positionOfEdge
* where should the edge start/end (SwingConstants)
* @return overlapping edges
*/
public static Collection> hasOverlappingEdges(
Graph graph, boolean stopAfterFirstOverlapping,
int positionOfEdge) {
HashSet> result = new HashSet>();
for (Edge edge : graph.getEdges()) {
edu.uci.ics.jung.graph.util.Pair endpoints = graph
.getEndpoints(edge);
Line2D.Double line = getLineBetween(endpoints.getFirst(),
endpoints.getSecond(), positionOfEdge);
if (endpoints.getFirst() != endpoints.getSecond()) {
for (Edge edge2 : graph.getEdges()) {
edu.uci.ics.jung.graph.util.Pair endpoints2 = graph
.getEndpoints(edge2);
if (!(endpoints.getFirst() == endpoints2.getFirst()
|| endpoints.getFirst() == endpoints2.getSecond()
|| endpoints.getSecond() == endpoints2.getFirst() || endpoints
.getSecond() == endpoints2.getSecond())) {
Line2D.Double line2 = getLineBetween(
endpoints2.getFirst(), endpoints2.getSecond(),
positionOfEdge);
if (line.intersectsLine(line2)) {
result.add(new MutablePair(edge, edge2));
if (stopAfterFirstOverlapping) {
return result;
}
}
}
}
}
}
return result;
}
/**
* Move all shapes a margin to the right and up, so that no shape has x=0 or y = 0
*
* @param graph
* the graph to inspect
* @param margin
* the new margin of the shapes
*/
private static void addMargin(Graph extends Shape, Edge> graph,
int margin) {
//
//
for (Shape shape : graph.getVertices()) {
translate(shape, margin, margin);
}
if (displayEachStep) {
JungGraphBoostUtils.displayShapes(new DisplayConfiguration("After Step 0",
graph));
}
}
/**
* Repositions shapes by moving them up or right, so that they no longer overlap
*
* @param graph
* contains the shapes
* @param constraints
* additional constraints
*/
private static void repositionWithoutOverlappingByMovingUpOrRight(
Graph extends Shape, Edge> graph,
GeometryPositionConstraint constraints) {
int minimumDistance = constraints.getMinimumDistance();
// Step 1
// first sort the collection according to the position
// lowest y is first, equal y means lowest x is first
ArrayList ordered = new ArrayList(graph.getVertices());
Collections.sort(ordered, ShapeComparator.YX_COMPARATOR);
int size = ordered.size();
// Step 2
// then remove overlapping by moving the remaining shapes to the right
// and up
for (int index = 0; index < size; index++) {
Shape shape = ordered.get(index);
Rectangle bounds = shape.getBounds();
int minY = bounds.y;
int maxY = bounds.y + bounds.height;
for (int j = index + 1; j < size; j++) {
Shape shape2 = ordered.get(j);
Rectangle bounds2 = shape2.getBounds();
int minY2 = bounds2.y;
if (bounds2.intersects(bounds)) {
for (int k = j; k < size; k++) {
Shape shape3 = ordered.get(k);
Rectangle bounds3 = shape3.getBounds();
int translateX = 0;
int translateY = 0;
if (bounds3.y == bounds.y) {
// move to the right
translateX = bounds.width + minimumDistance;
} else {
// move up
translateY = bounds.height + minimumDistance;
}
translate(shape3, translateX, translateY);
}
}
}
}
if (displayEachStep) {
JungGraphBoostUtils.displayShapes(new DisplayConfiguration("End", graph));
}
}
/**
* Are some shapes overlapping?
*
* @param graph
* contains the shapes
* @return overlapping shapes
*/
public static Collection> hasOverlappingVertices(
Graph graph,
boolean stopAfterFirstOverlapping) {
HashSet> result = new HashSet>();
ArrayList shapeslist = new ArrayList(graph.getVertices());
int size = shapeslist.size();
for (int i = 0; i < size; i++) {
Shape shape = shapeslist.get(i);
for (int j = i + 1; j < size; j++) {
Shape shape2 = shapeslist.get(j);
if (shape.intersects(shape2.getBounds())) {
result.add(new MutablePair(shape, shape2));
if (stopAfterFirstOverlapping) {
return result;
}
}
}
}
return result;
}
/**
* Are there overlappings in some shapes and lines
*
* @param graph
* contains the shapes (vertices) and lines (edges).
* @return [overlapping vertices (shapes), overlapping between vertex and edge, overlapping edges]
*/
public static Tuple>, Collection, Void>>, Collection>, Void> hasOverlappings(
Graph graph,
boolean stopAfterFirstOverlapping) {
Tuple>, Collection, Void>>, Collection>, Void> result = new Tuple>, Collection, Void>>, Collection>, Void>();
result.setThird(Collections.EMPTY_SET);
result.setSecond(Collections.EMPTY_SET);
result.setFirst(hasOverlappingVertices(graph, stopAfterFirstOverlapping));
if (stopAfterFirstOverlapping && !result.getFirst().isEmpty()) {
return result;
}
result.setSecond(hasOverlappingVertexEdge(graph,
stopAfterFirstOverlapping, SwingUtilities.TOP));
if (stopAfterFirstOverlapping && !result.getSecond().isEmpty()) {
return result;
}
result.setThird(hasOverlappingEdges(graph, stopAfterFirstOverlapping,
SwingUtilities.TOP));
// if (stopAfterFirstOverlapping && !result.getThird().isEmpty()) {
// return result;
// }
return result;
}
/**
* Computes the dimension of the layout of the graph
*
* @param graphs
* multiple graphs that are inspected
* @return the rectangle that covers all graphs
*/
public static Rectangle getGraphDimension(
Collection> graphs) {
Collection shapes = new ArrayList();
for (Graph extends Shape, ?> graph : graphs) {
shapes.addAll(graph.getVertices());
}
return getDimension(shapes);
}
/**
* Computes the dimension of the layout of the graph
*
* @param graphs
* multiple graphs that are inspected
* @return the rectangle that covers all graphs
*/
public static Rectangle getDimension(Graph extends Shape, ?>... graphs) {
Collection shapes = new ArrayList();
for (Graph extends Shape, ?> graph : graphs) {
shapes.addAll(graph.getVertices());
}
return getDimension(shapes);
}
/**
* Return the rectangle in which all the shapes lie.
*
* @param shapes
* the inspected shapes
* @return the covering rectangle
*/
public static Rectangle getDimension(Collection extends Shape> shapes) {
if (shapes == null || shapes.isEmpty()) {
return new Rectangle(0, 0, 0, 0);
}
// Algorithm get the highest x and and the highest height
// The algorithm can be improved.
int minX = Integer.MAX_VALUE;
int minY = Integer.MAX_VALUE;
int maxX = Integer.MIN_VALUE;
int maxY = Integer.MIN_VALUE;
for (Shape shape : shapes) {
Rectangle bounds = shape.getBounds();
// Currently it is supposed, that the width and heigt is positive
minX = Math.min(minX, bounds.x);
minY = Math.min(minY, bounds.y);
maxX = Math.max(maxX, bounds.x + bounds.width);
maxY = Math.max(maxY, bounds.y + bounds.height);
}
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
}
/**
* Return the bounds of shapes and the maximum of the heights and widths
*
* @param shapes
* the inspected shapes
* @return [ List of rectangle of each shape, dimension off all shapes together
*/
public static Pair, Dimension> getEachBounds(List shapes) {
int maxWidth = 0, maxHeight = 0;
int size = shapes.size();
List boundsList = new ArrayList(size);
for (int i = 0; i < size; i++) {
Rectangle bounds = shapes.get(i).getBounds();
boundsList.add(bounds);
maxWidth = Math.max(maxWidth, bounds.width);
maxHeight = Math.max(maxHeight, bounds.height);
}
return new MutablePair, Dimension>(boundsList,
new Dimension(maxWidth, maxHeight));
}
/**
* Layouts a sequence of nodes
*
* @param graph
* contains the shapes
* @param constraints
* additional constraints
* @return true if the graph is a sequence and the layout was successful
*/
private static boolean positionSequence(Graph graph,
GeometryPositionConstraint constraints) {
boolean checkAfterwards = true;
// default value
if (constraints == null) {
constraints = new GeometryPositionConstraint();
}
int minimumDistance = constraints.getMinimumDistance();
GraphSequenceClusterer sequenceClusterer = new GraphSequenceClusterer();
Set> sequences = sequenceClusterer
.transform((UndirectedGraph) JungGraphBoostUtils
.createUndirectedGraph(graph));
// if the whole graph is no sequence
if (sequences.isEmpty()) {
return false;
} else {
List sequence = new ArrayList(sequences.iterator()
.next());
if (sequence.size() != graph.getVertexCount()) {
// if the sequence is not the whole graph
return false;
// TODO divide and conquer
} else {
// one sequence
// make a row
List ordered = sequenceClusterer
.order(sequence, graph, true);
log.info("todo: try to find a quadratic layout, to minimize width and height");
// determine whether horizontal or vertical
// if there are edges to higher or lower y prefer horizontal
HashSet horizontal = new HashSet(
constraints.getEdgesToHigherY());
horizontal.addAll(constraints.getEdgesToLowerY());
// if there are edges to higher or lower x prefer vertical
HashSet vertical = new HashSet(
constraints.getEdgesToHigherX());
vertical.addAll(constraints.getEdgesToLowerX());
// prefer the direction with more elements
boolean displayVertical = vertical.size() > horizontal.size();
Rectangle bounds = constraints.getBounds();
if (bounds == null) {
bounds = new Rectangle(minimumDistance, minimumDistance,
Integer.MAX_VALUE, Integer.MAX_VALUE);
}
int x = bounds.x;
int y = bounds.y;
Pair, Dimension> boundsPair = getEachBounds(sequence);
int maxWidth = Math.min(boundsPair.getRight().width
+ minimumDistance, bounds.width);
int maxHeight = Math.min(boundsPair.getRight().height
+ minimumDistance, bounds.height);
for (Shape s : ordered) {
int xOffset = 0;
int yOffset = 0;
if (displayVertical) {
y += minimumDistance;
if (constraints.hasEdgeToHigherX(s)) {
xOffset = maxWidth;
// because the edge may otherwise intersect the other
// elements of the sequence
// TODO it would be better if the offset is computed
// dynamically considering the width of s
}
} else { // horizontal
x += minimumDistance;
if (constraints.hasEdgeToHigherY(s)) {
yOffset = maxHeight;
// because the edge may otherwise intersect the other
// elements of the sequence
// TODO it would be better if the offset is computed
// dynamically considering the height of s
}
}
setPosition(s, x + xOffset, y + yOffset, 0);
if (displayVertical) {
y += s.getBounds().height;
} else {
x += s.getBounds().width;
}
}
if (checkAfterwards) {
constraints.check(graph, false);
}
return true;
}
}
}
/**
* Algorithms to reposition special structurs. The repositioning tries to avoid overlappings.
*
* @param graph
* contains the shapes
* @param constraints
* constraints of the shapes
* @return true, if the repositioning was successful
*/
public static boolean repositionSpecialStructures(Graph graph,
GeometryPositionConstraint constraints) {
int minimumDistance = constraints.getMinimumDistance();
int size = graph.getVertices().size();
// put the shapes and their bounds in arrays
// and compute the maxiumul height and width of one shape
Shape[] shape = new Shape[size];
shape = graph.getVertices().toArray(shape);
List shapeList = Arrays.asList(shape);
// Variant 0: try if graph is one sequence.
if (positionSequence(graph, constraints)) {
return true;
}
Pair, Dimension> boundsPair = getEachBounds(shapeList);
List boundsList = boundsPair.getKey();
// Variant 1: otherwise
// Step 1: reposition without constraints.bounds
switch (size) {
case 0:
case 1:
// nothing needs to be done
break;
case 2: // horizontal line ordering depends which element has an edge to
// the left
// index of shapes
int left = 0;
int right = 1;
if (constraints != null
&& (constraints.getEdgesToHigherX().contains(shape[0]) || constraints
.getEdgesToLowerX().contains(shape[1]))) {
left = 1;
right = 0;
}
;
setPosition(shape[left], 0, 0, minimumDistance);
setPosition(shape[right], boundsList.get(left).width
+ minimumDistance, 0,
minimumDistance);
break;
case 3:
case 4:
case 5:
Point2D[] positions = createDefaultShape(shapeList, boundsList,
constraints, graph);
for (int i = 0; i < shape.length; i++) {
setPosition(shape[i], (int) positions[i].getX(),
(int) positions[i].getY(), 0);
}
break;
default:
return false;
}
// Step 2: consider bounds
if (constraints != null && constraints.getBounds() != null) {
Rectangle dim = getDimension(graph.getVertices());
Rectangle constraintBounds = constraints.getBounds();
if (dim.width > constraintBounds.width
|| dim.height > constraintBounds.height) {
scale(graph, dim.getLocation(), new Point2D.Double(
Math.min(
((double) constraintBounds.width)
/ (double) dim.width, 1), Math
.min(((double) constraintBounds.height)
/ (double) dim.height, 1)), false);
dim = getDimension(graph.getVertices());//update
}
translate(graph, -dim.x + constraintBounds.x, -dim.y
+ constraintBounds.y);
return true;
} else {
return true; // all ok
}
}
/**
* Positions some shapes (with as few overlappings as possible)
*
* @param graph
* contains the shapes
* @param constraints
* additional constraints
* @return true if successful
*/
public static boolean position(Graph graph,
GeometryPositionConstraint constraints) throws Exception {
boolean result;
if (repositionSpecialStructures(graph, constraints)) {
result = true;
} else {
result = positionByDivideAndConquer(graph, constraints);
}
constraints.check(graph, false);
checkOverlappings(graph, ObjectDescription.createFlag(true, false), true, true, false,
constraints);
return result;
}
/**
* Positions some shape by using a divide and conquer algorithm.
*
* @param graph
* contains the shapes
* @param constraints
* additional constraints
* @return true, if solved
*/
public static boolean positionByDivideAndConquer(
Graph graph,
GeometryPositionConstraint constraints) throws Exception {
int minimumDistance = constraints.getMinimumDistance();
UndirectedSparseGraph undirectedGraph = JungGraphBoostUtils
.createUndirectedGraph(graph);
int graphSize = undirectedGraph.getVertexCount();
// clusterer to divide the graph into components
ArrayList, Set>>> clusterers = new ArrayList, Set>>>();
clusterers.add(new GraphSequenceClusterer());
clusterers.add(new CallableDelegate(new BicomponentClusterer()));
for (Callable, Set>> clusterer : clusterers) {
Set> components = clusterer.call(undirectedGraph);
// only if it is a real subgraph
Set set1 = ContainerBoostUtils.getOneOf(components);
if (set1 != null && set1.size() != graphSize) {
// to avoid set problems create a duplicate
set1 = new HashSet(set1);
// set1 will be one inner component of the remaining graph
// split the graph in the graph of set1, the remaining graph and
// the edges between the graphs
Tuple, UndirectedGraph, List>, Void> separation = JungGraphBoostUtils
.split(graph, set1);
// do a first layout of graph1/set1 to get a feeling of the
// necessary dimensions
UndirectedGraph graph1 = separation.getFirst();
position(graph1, constraints);
Rectangle set1Dim = getDimension(set1);
List> connectionsBetween = separation
.getThird();
boolean graphsAreConnected = !connectionsBetween.isEmpty();
// the placeholder for the graph set1 within set2 if necessary
Rectangle placeholder = new BoostedRectangle("placeholder"
+ placeholderCounter++);
UndirectedGraph graph2 = separation.getSecond();
if (graphsAreConnected) {
// add the placeholder of graph1 and its connections
// first modify the coordinates, because later modified
// vertices are no longer found in the graph
placeholder.x = 0;
placeholder.y = 0;
Pair, Dimension> boundsPair = getEachBounds(new ArrayList(
set1));
// add some extra space because the graph1 will be layouted
// again later with more constraints
// use the maxiumum of the dimensions because the second
// layout
// may interchange x with y
// add some extra width, because the second layout may be
// larger
int set1DimMax = Math.max(set1Dim.width, set1Dim.height);
placeholder.width = set1DimMax + 2
* boundsPair.getRight().width + minimumDistance;
placeholder.height = set1DimMax + 2
* boundsPair.getRight().height + minimumDistance;
graph2.addVertex(placeholder);
// add the edges to the placeholder
for (Tuple connection1to2 : connectionsBetween) {
graph2.addEdge(connection1to2.getFirst(), placeholder,
connection1to2.getThird());
}
}
// now do a layout of the remaining graph with the placeholder
position(graph2, constraints);
Rectangle graph2Dim = getDimension(graph2.getVertices());
if (constraints.isDisplayPartResults()) {
JungGraphBoostUtils.displayShapes(new DisplayConfiguration(
"surrounding graph", null, new Rectangle(0, 0, 400,
400), graph2));
}
Point newLocationOfSet1;
Dimension translationOfSet1;
if (graphsAreConnected) {
// translate to the position of the placeholder
newLocationOfSet1 = placeholder.getLocation();
Rectangle placeholderBounds = placeholder.getBounds();
// do a new layout of graph1/set1; now with the constraints
// of the surrounding graph 2
GeometryPositionConstraint graph1Constraints = new GeometryPositionConstraint(
new ArrayList(), new ArrayList(),
new ArrayList(), new ArrayList(),
null);
graph1Constraints.setMinimumDistance(constraints
.getMinimumDistance());
Rectangle graph1Bounds = placeholder.getBounds();
graph1Constraints.setBounds(graph1Bounds);
for (Tuple conn : connectionsBetween) {
Shape graph1Vertex = conn.getSecond();
Shape graph2Vertex = conn.getThird();
Rectangle graph2VertexPosition = graph2Vertex
.getBounds();
graph1Constraints.addEdgesToLowerHigher(
graph2VertexPosition, placeholderBounds,
graph1Vertex);
}
position(graph1, graph1Constraints);
if (constraints.isDisplayPartResults()) {
JungGraphBoostUtils.displayShapes(new DisplayConfiguration(
"inner graph 1", graph1));
}
if (!checkOverlappings(graph, ObjectDescription.createFlag(false, false), true,
true, false, constraints)) {
// do the checking and position again for debugging
// purposes.
Rectangle preferred = new Rectangle(0, 0, 500, 500);
JungGraphBoostUtils.displayShapes(new DisplayConfiguration(
"surrounding + inner graph ", null, preferred,
graph, graph2, graph1));
checkOverlappings(graph, ObjectDescription.createFlag(false, false), true,
true, false, constraints);
graph1Constraints.setDisplayPartResults(true);
position(graph1, graph1Constraints);
JungGraphBoostUtils.displayShapes(new DisplayConfiguration(
"after positioned again", new Point(
(int) preferred.getWidth(), 0), null,
graph));
}
} else {
// just to the right
newLocationOfSet1 = new Point(graph2Dim.x + graph2Dim.width
+ minimumDistance, minimumDistance);
translate(set1Dim, newLocationOfSet1, set1);
if (!checkOverlappings(graph, ObjectDescription.createFlag(false, false), true,
true, false, constraints)) {
JungGraphBoostUtils.displayShapes(new DisplayConfiguration(
"overlapping in graph", graph));
log.debug("created overlappings");
}
}
return true;
}
}
// if no subcomponent to divide has been found
// just try to remove overlappings
repositionWithoutOverlapping(undirectedGraph, constraints);
return false;
}
/**
* Computes the translation of a rectangle so that its location ist at a new location.
*
* @param translateThisRectangle
* the rectangle to translate
* @param location
* the new location
* @param shapesToTranslate
* the shapes are translated
* @return the translation
*/
private static Dimension translate(Rectangle translateThisRectangle,
Point location, Collection shapesToTranslate) {
Dimension translation = new Dimension(location.x
- translateThisRectangle.x, location.y
- translateThisRectangle.y);
for (Shape shape : shapesToTranslate) {
translate(shape, translation.width, translation.height);
}
return translation;
}
/**
* Creates a default shape for some corners.
*
* @param corner
* the objects to position in the corners. 1 := corner[1] 2:=corner[2], ...
* @param bounds
* the bounds of the corner objects
* @param constraints
* additional constraints
* @param graph
* contains the corners
* @return the points of the corresponding corners
*/
public static Point2D[] createDefaultShape(List corner,
List bounds,
GeometryPositionConstraint constraints,
Graph graph) {
int size = corner.size();
if (size == 3) {
return createTriangle(corner, bounds, constraints);
} else if (size == 4) {
return createQuadrangle(corner, bounds, constraints);
} else if (size == 5) {
return createPentagon(corner, bounds, constraints, graph);
} else {
throw new IllegalArgumentException("size not yet supported " + size);
}
}
/**
* Creates a quandrangle
*
* @param corner
* the 4 corners
* @param bounds
* the bounds of the corresponding corners
* @param constraints
* additional constraints
* @return the points of the coresponding corners.
*/
public static Point2D[] createQuadrangle(List corner,
List bounds,
GeometryPositionConstraint constraints) {
int size = 4;
Point2D[] result = new Point2D[size];
int dist = constraints.getMinimumDistance();
int maxWidth = 0, maxHeight = 0;
for (int i = 0; i < size; i++) {
maxWidth = Math.max(maxWidth, bounds.get(i).width);
maxHeight = Math.max(maxHeight, bounds.get(i).height);
}
// make a quardangle of the form
// .0..
// 1...
// ...2
// ..3.
int index0 = -1;
int index1 = -1;
int index2 = -1;
int index3 = -1;
ArrayList top = new ArrayList(
constraints.getObjectsWithEdgesToLowerYButNotHigherY());
ArrayList bottom = new ArrayList(
constraints.getObjectsWithEdgesToHigherYButNotLowerY());
ArrayList left = new ArrayList(
constraints.getObjectsWithEdgesToLowerXButNotHigherX());
ArrayList right = new ArrayList(
constraints.getObjectsWithEdgesToHigherXButNotLowerX());
if (top.size() == 1) {
Corner oneCorner = top.get(0);
index0 = corner.indexOf(oneCorner);
} else if (top.size() == 2) {
Corner oneCorner = top.get(0);
Corner twoCorner = top.get(1);
if (right.contains(oneCorner)) {
index0 = corner.indexOf(oneCorner);
index1 = corner.indexOf(twoCorner);
} else {
index0 = corner.indexOf(twoCorner);
index1 = corner.indexOf(oneCorner);
}
} else if (top.size() == 3) {
Corner oneCorner = top.get(0);
Corner twoCorner = top.get(1);
Corner threeCorner = top.get(2);
index0 = corner.indexOf(twoCorner);// the middle
index1 = corner.indexOf(oneCorner);
index2 = corner.indexOf(threeCorner);
}
ArrayList remaining = new ArrayList(Arrays.asList(0,
1, 2, 3));
remaining.removeAll(Arrays.asList(index0, index1, index2, index3));
// fill in remaining indices
if (index0 == -1) {
index0 = remaining.remove(0);
}
if (index1 == -1) {
index1 = remaining.remove(0);
}
if (index2 == -1) {
index2 = remaining.remove(0);
}
if (index3 == -1) {
index3 = remaining.remove(0);
}
// make a square with maxWidht and maxHeight distance between
// the elements to avoid overlapping of edges with shapes
// the square is a 3x3 Matrix with the shapes at the corners.
// Each element has widht = maxWidth, height = maxHeight
result[index0] = new Point2D.Double(dist + bounds.get(index1).width
+ dist, dist);
result[index1] = new Point2D.Double(dist, dist
+ bounds.get(index0).height + dist);
result[index2] = new Point2D.Double(dist + bounds.get(index1).width
+ dist + bounds.get(index0).width + dist
+ bounds.get(index3).width + dist, dist
+ bounds.get(index0).height + dist + bounds.get(index1).height
+ dist);
result[index3] = new Point2D.Double(dist + bounds.get(index1).width
+ dist + bounds.get(index0).width + dist, dist
+ bounds.get(index0).height + dist + bounds.get(index1).height
+ dist + bounds.get(index2).height + dist);
return result;
}
/**
* Creates a triangle
*
* @param corner
* the objects to position in the corners. 1 := corner[1] 2:=corner[2], ...
* @param bounds
* the bounds of the corner objects
* @param constraints
* additional constraints
* @return the points of the corresponding corners
*/
public static Point2D[] createTriangle(List corner,
List bounds,
GeometryPositionConstraint constraints) {
Point2D[] result = new Point2D[3];
int dist = constraints.getMinimumDistance();
List top = constraints
.getObjectsWithEdgesToLowerYButNotHigherY();
if (top.size() == 2) {
// the list is ordered from left to right
// make triangle
// ..left...........right
// .......remaining......
// TODO better consider additionally if left or right have edges to
// the right or left
int leftIndex = corner.indexOf(top.get(0));
int rightIndex = corner.indexOf(top.get(1));
ArrayList remaining = new ArrayList(
Arrays.asList(0, 1, 2));
remaining.removeAll(Arrays.asList(leftIndex, rightIndex));
if (remaining.size() != 1) {
throw new IllegalStateException("bug in code");
}
int downIndex = remaining.get(0);
result[leftIndex] = new Point2D.Double(dist, dist);
result[rightIndex] = new Point2D.Double(dist
+ bounds.get(leftIndex).width + dist
+ bounds.get(downIndex).width + dist, dist);
result[downIndex] = new Point2D.Double(dist
+ bounds.get(leftIndex).width + dist, dist
+ Math.max(bounds.get(leftIndex).height,
bounds.get(rightIndex).height) + dist);
} else {
// TODO other variants
log.info("other variants missing");
// Default Triangle
// .1.
// 2..
// ..3
Point2D point1, point2, point3;
point1 = new Point2D.Double(dist + bounds.get(2).width + dist, dist);
point2 = new Point2D.Double(dist, dist + bounds.get(1).height
+ dist);
point3 = new Point2D.Double(dist + bounds.get(2).width + dist
+ bounds.get(1).width + dist, dist + bounds.get(1).height
+ dist + bounds.get(2).height + dist);
result[0] = point1;
result[1] = point2;
result[2] = point3;
}
return result;
// setPosition(shape[oneIndex], 0, 0, minimumDistance);
// setPosition(shape[twoIndex], bounds[0].width + minimumDistance,
// 0,
// minimumDistance);
// setPosition(shape[threeIndex], bounds[0].width,
// Math.max(bounds[0].height, bounds[1].height)
// + minimumDistance, minimumDistance);
// break;
}
/**
* Does scaling of the vertices of a graph
*
* @param graph
* contains the vertices to scale
* @param origin
* the origin of the scaling
* @param scale
* the multiplicand
* @param scaleVertexSize
* should the size of the vertices scale, too
*
*/
public static void scale(Graph graph, Point origin,
Point2D scale, boolean scaleVertexSize) {
if (scaleVertexSize) {
log.debug("scale vertex size not yet supported");
}
for (Shape shape : graph.getVertices()) {
Rectangle bounds = shape.getBounds();
double newX = origin.x + ((double) bounds.x - origin.x)
* scale.getX();
double newY = origin.y + ((double) bounds.y - origin.y)
* scale.getY();
setPosition(shape, (int) newX, (int) newY, 0);
}
}
/**
* Creates a pentagon.
*
* @param corner
* the 5 corners
* @param bounds
* the bounds of the corresponding corners
* @param constraints
* additional constraints
* @param graph
* graph with the edges between the corners
* @return the points of the corners
*/
public static Point2D[] createPentagon(List corner,
List bounds,
GeometryPositionConstraint constraints,
Graph graph) {
int size = 5;
int dist = constraints.getMinimumDistance();
int maxWidth = 0, maxHeight = 0;
for (int i = 0; i < size; i++) {
maxWidth = Math.max(maxWidth, bounds.get(i).width);
maxHeight = Math.max(maxHeight, bounds.get(i).height);
}
//Pentagon
// ..0......
// .......4..
//.1......
// ......3...
// ....2
MultiValuedMap cornerPositions = new HashSetValuedHashMap<>();
for (Corner vertex : corner) {
cornerPositions.put(vertex, 0);
cornerPositions.put(vertex, 1);
cornerPositions.put(vertex, 2);
cornerPositions.put(vertex, 3);
cornerPositions.put(vertex, 4);
}
if (constraints != null){
//remove unwanted positions
for (Corner vertex: constraints.getEdgesToLowerY()){
cornerPositions.get(vertex).remove(2);
cornerPositions.get(vertex).remove(3);
}
for (Corner vertex: constraints.getEdgesToHigherY()){
cornerPositions.get(vertex).remove(0);
}
for (Corner vertex: constraints.getEdgesToLowerX()){
cornerPositions.get(vertex).remove(3);
cornerPositions.get(vertex).remove(4);
}
for (Corner vertex: constraints.getEdgesToHigherX()){
cornerPositions.get(vertex).remove(1);
}
}
boolean doContinue = true;
while (doContinue) {
// if one corner has only one possible position then remove that
// position from the others
for (Corner vertex : corner) {
if (cornerPositions.get(vertex).size() == 1) {
Integer position = cornerPositions.get(vertex).iterator()
.next();
for (Corner otherVertex : corner) {
if (otherVertex != vertex) {
cornerPositions.get(otherVertex).remove(position);
}
}
}
}
doContinue = false;
// if one corner has preferred opportunities use one
for (Corner vertex : corner) {
Collection positions = cornerPositions.get(vertex);
Integer position = null;
if (positions.size() > 1) {
if (positions.contains(0) && constraints.getEdgesToLowerY().contains(vertex)){
position = 0;
} else if (positions.contains(1) && constraints.getEdgesToLowerX().contains(vertex)){
position = 1;
} else if (positions.contains(2) && constraints.getEdgesToHigherY().contains(vertex)){
position = 2;
} else if (positions.contains(3) && constraints.getEdgesToHigherX().contains(vertex)){
position = 3;
} else if (positions.contains(4) && constraints.getEdgesToHigherX().contains(vertex)){
position = 4;
} else {
// position = positions.iterator().next();
}
if (position != null) {
cornerPositions.remove(vertex);
cornerPositions.put(vertex, position);
doContinue = true;
break;
}
}
}
// if nothing has been done yet and one corner has multiple
// opportunites, use one
if (!doContinue) {
for (Corner vertex : corner) {
Collection positions = cornerPositions.get(vertex);
if (positions.size() > 1) {
Integer position = positions.iterator().next();
cornerPositions.remove(vertex);
cornerPositions.put(vertex, position);
doContinue = true;
break;
}
}
}
}
// now all corners have exactly one position from 0 to 4
// create the mapping from the position to the corner
// the index in the array is the position, the value is the index of the
// corresponding corner/bounds
int[] indexOfCorner = new int[size];
for (int i = 0; i < size; i++) {
indexOfCorner[i] = -1;
}
for (int i = 0; i < size; i++) {
Corner c = corner.get(i);
Integer pos = cornerPositions.get(c).iterator().next();
indexOfCorner[pos] = i;
}
// create the pentagram
Point2D point0 = new Point2D.Double(dist
+ bounds.get(indexOfCorner[1]).width
+ dist, dist);
Point2D point1 = new Point2D.Double(dist, dist
+ bounds.get(indexOfCorner[0]).height + dist
+ bounds.get(indexOfCorner[4]).height + dist);
Point2D point2 = new Point2D.Double(dist
+ bounds.get(indexOfCorner[1]).width + dist
+ bounds.get(indexOfCorner[0]).width + dist, dist
+ bounds.get(indexOfCorner[0]).height + dist
+ bounds.get(indexOfCorner[4]).height + dist
+ bounds.get(indexOfCorner[1]).height + dist
+ bounds.get(indexOfCorner[3]).height + dist);
Point2D point3 = new Point2D.Double(dist
+ bounds.get(indexOfCorner[1]).width + dist
+ bounds.get(indexOfCorner[0]).width + dist
+ bounds.get(indexOfCorner[2]).width + dist, dist
+ bounds.get(indexOfCorner[0]).height + dist
+ bounds.get(indexOfCorner[4]).height + dist
+ bounds.get(indexOfCorner[1]).height + dist);
// check whether the point4 should be at the same y-position as point0
boolean point4AtSameHeightAsPoint0 = false;
if (constraints != null) {
if (constraints.getObjectsWithEdgesToLowerYButNotHigherY()
.contains(corner.get(indexOfCorner[4]))
&& !constraints.getEdgesToHigherX().contains(
corner.get(indexOfCorner[4]))) {
point4AtSameHeightAsPoint0 = true;
}
}
// check whether the corner 4 is connected with corner 2
// this is needed to determine the x-position of corner 4
boolean point4ConnectedWithPoint2 = graph.findEdge(
corner.get(indexOfCorner[4]), corner.get(indexOfCorner[2])) != null;
// if this is the case then point 4 must have the same y position as
// point 3 to avoid overlapping of the edge with point 3
Point2D point4 = new Point2D.Double(dist
+ bounds.get(indexOfCorner[1]).width + dist
+ bounds.get(indexOfCorner[0]).width + dist
+ bounds.get(indexOfCorner[2]).width + dist
+ (point4ConnectedWithPoint2 ? 0
: bounds.get(indexOfCorner[3]).width + dist), dist
+ (point4AtSameHeightAsPoint0 ? 0
: bounds.get(indexOfCorner[0]).height
+ dist));
Point2D[] points = { point0, point1, point2, point3, point4 };
Point2D[] result = new Point2D[size];
for (int i = 0; i < size; i++) {
result[indexOfCorner[i]] = points[i];
}
// Point2D point0 = new Point2D.Double(dist, dist);
// Point2D point1 = new Point2D.Double(dist, dist + 2 * maxHeight);
// Point2D point2 = new Point2D.Double(dist + 2 * maxWidth, dist + 3
// * maxHeight);
// Point2D point3 = new Point2D.Double(dist + 4 * maxWidth, dist + 2
// * maxHeight);
// Point2D point4 = new Point2D.Double(dist + 4 * maxWidth, dist);
// result[0] = point0;
// result[1] = point1;
// result[2] = point2;
// result[3] = point3;
// result[4] = point4;
return result;
}
/**
* Checks whether there are overlappings in a graph.
*
* @param graph
* contains the (overlapping) shapes.
*
* @param overlappingAllowedFlag
* are overlappings allowed
* @param checkVertexVertex
* check vertex-vertex-overlapping
* @param checkVertexEdge
* check vertex-edge-overlapping
* @param checkEdgeEdge
* check edge-edge-overlapping
* @param constraintsToCreateTestWhenError
* additional constraints that are used when creating the sourcecode of a test when this check fails
* @return true, if the check is ok, false otherwise (if no exception is thrown)
*/
public static boolean checkOverlappings(Graph graph,
ObjectDescription overlappingAllowedFlag, boolean checkVertexVertex,
boolean checkVertexEdge, boolean checkEdgeEdge,
GeometryPositionConstraint constraintsToCreateTestWhenError) {
if (overlappingAllowedFlag == null) {
return true;
}
Tuple>, Collection, Void>>, Collection>, Void> overlappings = GeometryBoostUtils
.hasOverlappings(graph, true);
Collection> vertexVertexOverlapping = overlappings.getFirst();
Collection, Void>> vertexEdgeOverlapping = overlappings
.getSecond();
Collection> edgeEdgeOverlapping = overlappings.getThird();
if ( (checkVertexVertex && ! vertexVertexOverlapping.isEmpty()) || (checkVertexEdge && ! vertexEdgeOverlapping.isEmpty())|| (checkEdgeEdge && ! edgeEdgeOverlapping.isEmpty())) {
StringBuilder errormessage = new StringBuilder();
String prefix = overlappingAllowedFlag.getValidationInfo().getExceptionTextPrefix();
if (prefix != null) {
errormessage.append(prefix);
}
errormessage.append(" overlappings: ");
if (checkVertexVertex && ! vertexVertexOverlapping.isEmpty()){
errormessage.append("\nvertex-vertex-overlappings: ").append(vertexVertexOverlapping);
}
if (checkVertexEdge && ! vertexEdgeOverlapping.isEmpty()){
errormessage.append("\nvertex-edge-overlappings: ").append(vertexEdgeOverlapping);
}
if (checkEdgeEdge && ! edgeEdgeOverlapping.isEmpty()){
errormessage.append("\nedge-edge-overlappings: ").append(edgeEdgeOverlapping);
}
String testcode = createTestSourcecode(graph,
constraintsToCreateTestWhenError);
log.info("Testcode is\n", testcode);
// todo later: trace
if (overlappingAllowedFlag.getValidationInfo().isThrowExceptionIfValueIsNotValid()) {
throw new IllegalStateException(errormessage.toString());
} else {
log.debug(errormessage);
return false;
}
}
return true;
}
/**
* Create sourcecode from a graph
*
* @param graph
* the objects to describe
* @param constraints
* additional constraints.
* @return "no sourcecode yet"
*/
public static String createTestSourcecode(Graph graph,
GeometryPositionConstraint constraints) {
return "no sourcecode yet";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy