blob: c1f1861a0f5adba45689cc75eeb66d85bc98b6b0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2012, 2013 Red Hat, Inc.
* All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.eclipse.bpmn2.modeler.core.features;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.bpmn2.modeler.core.utils.GraphicsUtil;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.mm.algorithms.styles.Point;
import org.eclipse.graphiti.mm.pictograms.ContainerShape;
import org.eclipse.graphiti.mm.pictograms.Shape;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.graphiti.services.IGaService;
import org.eclipse.graphiti.services.IPeService;
// TODO: Auto-generated Javadoc
/**
* The Class RouteSolver.
*/
public class RouteSolver {
/** The Constant gaService. */
protected final static IGaService gaService = Graphiti.getGaService();
/** The Constant peService. */
protected static final IPeService peService = Graphiti.getPeService();
/** The Constant topMargin. */
static final int topMargin = 50;
/** The Constant bottomMargin. */
static final int bottomMargin = 50;
/** The Constant leftMargin. */
static final int leftMargin = 50;
/** The Constant rightMargin. */
static final int rightMargin = 50;
/** The fp. */
IFeatureProvider fp;
/** The all shapes. */
List<ContainerShape> allShapes;
/** The source. */
Shape source;
/** The target. */
Shape target;
/** The right. */
int top, left, bottom, right;
/** The vertical net. */
RoutingNet verticalNet;
/** The horizontal net. */
RoutingNet horizontalNet;
private boolean rotate = false;
/**
* RouteSolver constructor.
*
* @param fp - Graphiti Feature Provider
* @param allShapes - a list of all shapes that are considered in the routing solution.
*/
public RouteSolver(IFeatureProvider fp, List<ContainerShape> allShapes) {
this.fp = fp;
this.allShapes = new ArrayList<ContainerShape>();
this.allShapes.addAll(allShapes);
initialize();
}
/**
* Solve.
*
* @param source the source
* @param target the target
* @return true, if successful
*/
public boolean solve(Shape source, Shape target) {
this.source = source;
this.target = target;
List< List<RoutingLane> > verticalSolutions = null;
List< List<RoutingLane> > horizontalSolutions = null;
verticalNet.eraseLanes();
horizontalNet.eraseLanes();
Point ps = GraphicsUtil.getShapeCenter(source);
Point pt = GraphicsUtil.getShapeCenter(target);
int dx = ps.getX() - pt.getX();
int dy = ps.getY() - pt.getY();
if (Math.abs(dy) > Math.abs(dx)) {
verticalSolutions = verticalNet.findSolutions(source, target);
verticalNet.drawLanes();
// verticalNet.drawConnections();
}
else {
horizontalSolutions = horizontalNet.findSolutions(source, target);
horizontalNet.drawLanes();
// horizontalNet.drawConnections();
}
// if (verticalSolutions!=null && verticalSolutions.size()>0) {
// for (int i=0; i<verticalSolutions.size(); ++i) {
// verticalNet.drawSolution(verticalSolutions.get(i), i);
// if (i>16)
// break;
// }
// }
// if (horizontalSolutions!=null && horizontalSolutions.size()>0) {
// for (int i=0; i<horizontalSolutions.size(); ++i) {
// horizontalNet.drawSolution(horizontalSolutions.get(i), i);
// if (i>16)
// break;
// }
// }
return true;
}
/**
* Initialize.
*
* @return true, if successful
*/
public boolean initialize() {
if (allShapes.size()<2)
return false;
rotate = false;
verticalNet = new RoutingNet(fp);
Rectangle r = calculateDiagramBounds();
sortAllShapes();
top = r.y;
left = r.x;
bottom = top + r.height;
right = left + r.width;
calculateRoutingNet(verticalNet);
verticalNet.link();
rotate = true;
horizontalNet = new RoutingNet(fp);
r = calculateDiagramBounds();
sortAllShapes();
top = r.y;
left = r.x;
bottom = top + r.height;
right = left + r.width;
calculateRoutingNet(horizontalNet);
horizontalNet.link();
rotate = false;
return true;
}
/**
* Calculate diagram bounds.
*
* @return the rectangle
*/
protected Rectangle calculateDiagramBounds() {
// find bounding rectangle that contains all shapes
int left = Integer.MAX_VALUE;
int right = Integer.MIN_VALUE;
int top = Integer.MAX_VALUE;
int bottom = Integer.MIN_VALUE;
int x, y;
for (ContainerShape s : allShapes) {
Rectangle r = getBounds(s);
x = r.x;
if (x < left)
left = x;
x = r.right();
if (x > right)
right = x;
y = r.y;
if (y < top)
top = y;
y = r.bottom();
if (y > bottom)
bottom = y;
}
left -= leftMargin;
top -= topMargin;
bottom += bottomMargin;
right += rightMargin;
return new Rectangle(left, top, right-left, bottom-top);
}
/**
* Calculate routing net.
*
* @param net the net
*/
protected void calculateRoutingNet(RoutingNet net) {
net.add(left, top, leftMargin, bottom-top);
for (int i=0; i<allShapes.size(); ++i) {
ContainerShape shape = allShapes.get(i);
if (GraphicsUtil.getDebugText(shape).contains("Task_1")) { //$NON-NLS-1$
GraphicsUtil.debug = true;
}
else
GraphicsUtil.debug = false;
// get bounding rectangle for current shape
Rectangle shapeBounds = getBounds(shape);
// The rectangular region below the current shape will be sliced
// into smaller rectangles (a.k.a. "Routing Lanes"). To do this we'll
// create a horizontal slicer that keeps track of the location
// and width of each void defined by the top edge of the current
// shape, and the left/right edges of the shapes below it.
Slice slice = new Slice(shapeBounds.x, shapeBounds.right());
List<ContainerShape> below = getShapesBelow(shape);
for (ContainerShape shapeBelow : below) {
Rectangle shapeBelowBounds = getBounds(shapeBelow);
if (slice.remove(shapeBelowBounds.x, shapeBelowBounds.right()) == 0)
break;
}
List<Integer> cuts = slice.getCuts();
int c1 = cuts.get(0);
for (int ic=1; ic<cuts.size(); ++ic) {
int c2 = cuts.get(ic);
int x = c1;
int y = shapeBounds.y+shapeBounds.height;
int w = c2-c1;
int h = Integer.MIN_VALUE;
for (ContainerShape shapeBelow : below) {
Rectangle shapeBelowBounds = getBounds(shapeBelow);
if (shapeBelowBounds.x<=c1 && c1<shapeBelowBounds.right()) {
h = shapeBelowBounds.y - shapeBounds.y - shapeBounds.height;
break;
}
}
if (h==Integer.MIN_VALUE) {
h = bottom - shapeBounds.y - shapeBounds.height;
}
net.add(x, y, w, h);
c1 = c2;
}
// calculate lanes above the current shape
slice = new Slice(shapeBounds.x, shapeBounds.right());
List<ContainerShape> above = getShapesAbove(shape);
for (ContainerShape shapeAbove : above) {
Rectangle shapeAboveBounds = getBounds(shapeAbove);
if (slice.remove(shapeAboveBounds.x, shapeAboveBounds.right()) == 0)
break;
}
// Now we'll do the same thing for lane above the current shape,
// but only those that extend all the way to the top of the diagram.
// We don't want any overlapping lane.
cuts = slice.getCuts();
c1 = cuts.get(0);
for (int ic=1; ic<cuts.size(); ++ic) {
int c2 = cuts.get(ic);
int x = c1;
int y = top;
int w = c2-c1;
int h = Integer.MIN_VALUE;
for (ContainerShape shapeAbove : above) {
Rectangle shapeAboveBounds = getBounds(shapeAbove);
if (shapeAboveBounds.x<=c1 && c1<shapeAboveBounds.right()) {
h = shapeAboveBounds.y - shapeBounds.y - shapeBounds.height;
break;
}
}
if (h==Integer.MIN_VALUE) {
h = shapeBounds.y - top;
// only add the lane if there is no shape above this region.
net.add(x, y, w, h);
}
c1 = c2;
}
addTrailingAisle(net, shape);
}
net.add(right-rightMargin, top, rightMargin, bottom-top);
// rotate the horizontal lane nodes
net.rotate(rotate);
}
/**
* Adds the trailing aisle.
*
* @param net the net
* @param shape the shape
*/
protected void addTrailingAisle(RoutingNet net, ContainerShape shape) {
Rectangle shapeBounds = getBounds(shape);
int x = shapeBounds.right();
int size = allShapes.size();
for (int i=0; i<size; ++i) {
ContainerShape s = allShapes.get(i);
if (s!=shape) {
Rectangle b = getBounds(s);
if (b.x<=x && x<b.right())
return;
}
}
for (int i=0; i<size; ++i) {
ContainerShape s = allShapes.get(i);
if (s==shape) {
for (int n=i+1; n<size; ++n) {
ContainerShape nextShape = allShapes.get(n);
Rectangle nextShapeBounds = getBounds(nextShape);
if (nextShapeBounds.x>shapeBounds.right()) {
net.add(shapeBounds.right(),top, nextShapeBounds.x - shapeBounds.right(), bottom-top);
return;
}
}
}
}
}
/**
* Gets the shapes below.
*
* @param shape the shape
* @return the shapes below
*/
protected List<ContainerShape> getShapesBelow(ContainerShape shape) {
final Rectangle bounds = getBounds(shape);
List<ContainerShape> shapes = new ArrayList<ContainerShape>();
for (ContainerShape s : allShapes) {
if (s!=shape) {
Rectangle b = getBounds(s);
if (b.x>bounds.right())
break;
int d = b.y - bounds.y;
if (
d>=0 && (
(bounds.x<=b.x && b.x<=bounds.right()) ||
(bounds.x<=b.right() && b.right()<=bounds.right()) ||
(b.x<=bounds.x && bounds.right()<=b.right())
)
) {
shapes.add(s);
}
}
}
// sort the rectangles by increasing distance from the bottom of the given shape
Collections.sort(shapes, new Comparator<ContainerShape>() {
@Override
public int compare(ContainerShape s1, ContainerShape s2) {
Rectangle b1 = getBounds(s1);
Rectangle b2 = getBounds(s2);
int d1 = b1.y - bounds.bottom();
int d2 = b2.y - bounds.bottom();
int i = d1 - d2;
if (i==0) {
i = b1.x - b2.x;
}
return i;
}
});
for (int i=0; i<shapes.size(); ++i) {
ContainerShape s = shapes.get(i);
Rectangle b = getBounds(s);
if (b.x<=bounds.x && b.right()>=bounds.right()) {
++i;
while (i<shapes.size()) {
shapes.remove(i);
}
}
}
return shapes;
}
/**
* Gets the shapes above.
*
* @param shape the shape
* @return the shapes above
*/
protected List<ContainerShape> getShapesAbove(ContainerShape shape) {
final Rectangle bounds = getBounds(shape);
List<ContainerShape> shapes = new ArrayList<ContainerShape>();
for (ContainerShape s : allShapes) {
if (s!=shape) {
Rectangle b = getBounds(s);
if (b.x>bounds.right())
break;
int d = b.y - bounds.y;
if (
d<=0 && (
(bounds.x<=b.x && b.x<=bounds.right()) ||
(bounds.x<=b.right() && b.right()<=bounds.right()) ||
(b.x<=bounds.x && bounds.right()<=b.right())
)
) {
shapes.add(s);
}
}
}
// sort the rectangles by increasing distance from the top of the given shape
Collections.sort(shapes, new Comparator<ContainerShape>() {
@Override
public int compare(ContainerShape s1, ContainerShape s2) {
Rectangle b1 = getBounds(s1);
Rectangle b2 = getBounds(s2);
int d1 = b1.y - bounds.y;
int d2 = b2.y - bounds.y;
int i = d1 - d2;
if (i==0) {
i = b1.x - b2.x;
}
return i;
}
});
for (int i=0; i<shapes.size(); ++i) {
ContainerShape s = shapes.get(i);
Rectangle b = getBounds(s);
if (b.x<=bounds.x && b.right()>=bounds.right()) {
++i;
while (i<shapes.size()) {
shapes.remove(i);
}
}
}
return shapes;
}
/**
* The Class Slice.
*/
class Slice {
/** The parent. */
protected Slice parent;
/** The right. */
protected int left, right;
/** The cuts. */
List<Integer> cuts = new ArrayList<Integer>();
/** The children. */
List<Slice> children = new ArrayList<Slice>();
/**
* Instantiates a new slice.
*
* @param left the left
* @param right the right
*/
public Slice(int left, int right) {
this(null,left,right);
}
/**
* Instantiates a new slice.
*
* @param parent the parent
* @param left the left
* @param right the right
*/
protected Slice(Slice parent, int left, int right) {
this.parent = parent;
this.left = left;
this.right = right;
cuts.add(left);
cuts.add(right);
}
/**
* Removes the.
*
* @param l the l
* @param r the r
* @return the int
*/
public int remove(int l, int r) {
if (r<left || right<l) {
for (Slice s : children) {
s.remove(l, r);
}
}
else if (l<=left && left<=r && r<=right) {
for (Slice s : children) {
s.remove(l, r);
}
cut(left);
left = r;
}
else if (l<=left && right<=r) {
for (Slice s : children) {
s.remove(l, left);
s.remove(right, r);
}
cut(left);
cut(right);
right = left;
}
else if (l>left && r<right) {
children.add(new Slice(this,left,l));
children.add(new Slice(this,r,right));
cut(left);
left = l;
cut(right);
right = r;
}
else if (left<=l && l<=right && r>=right) {
for (Slice s : children) {
s.remove(r, right);
}
cut(right);
right = l;
}
return length();
}
/**
* Length.
*
* @return the int
*/
public int length() {
int length = right - left;
for (Slice s : children) {
length += s.length();
}
return length;
}
/**
* Cut.
*
* @param point the point
*/
protected void cut(int point) {
if (!cuts.contains(point))
cuts.add(point);
}
/**
* Gets the cuts.
*
* @return the cuts
*/
public List<Integer> getCuts() {
cut(left);
cut(right);
for (Slice s : children) {
for (Integer p : s.getCuts())
cut(p);
}
if (parent==null) {
Collections.sort(cuts);
}
return cuts;
}
}
/**
* Sort all shapes.
*/
protected void sortAllShapes() {
Collections.sort(allShapes, new Comparator<ContainerShape>() {
@Override
public int compare(ContainerShape s1, ContainerShape s2) {
Rectangle r1 = getBounds(s1);
Rectangle r2 = getBounds(s2);
int i = r1.x - r2.x;
if (i==0) {
i = r1.y - r2.y;
}
return i;
}
});
}
private Rectangle getBounds(ContainerShape shape) {
return RoutingNet.getBounds(rotate,shape);
}
}