实现GEF线路由

*内容概述

---实现添加自己的线路由

---给出一个案例,线能自动绕开其他的图形.

 

*ManhattanConnectionRouter简介

---算法简介

与节点无关,只关心如何折线,拐点是不断取折点和结束锚点的中间点位置.

这是一个简洁的算法,却给我们提供了几点关键的概念和代码,作为设计线算法的思路.

a)直角线

开始和结束间的线不是水平线,就是垂直线,折角都是直角.

b)给出了线头的方向和线尾的方向的代码

getStartDirection(Connection conn),getEndDirection(Connection conn),getDirection(Rectangle r, Point p)

---缺点

1)线不会避开锚点间的节点,因为算法自身不考虑这个因素.

 

*实现智能连线

---目标

选择路径能够尽量绕开节点,效果是线不会穿过节点.

---源码

智能连线源码/*******************************************************************************
 * Copyright(C) 2011 Arzan Corporation. All rights reserved.
 *
 * Contributors:
 *     Arzan Corporation - initial API and implementation
 *******************************************************************************/
package galaxy.ide.common.draw2d.figures;

import java.util.List;

import org.eclipse.core.runtime.Assert;
import org.eclipse.draw2d.AbstractRouter;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.draw2d.geometry.Vector;

/**
 * 具备自动避开节点的能力, 尽可能绕开的最短路径
 * 
 * @author dzh
 * @version 1.0 2011-12-16 上午10:33:38
 */
public class AIConnectionRouter extends AbstractRouter {

	private IFigure content;

	private static final int DEFAULT_SPACE = 10;

	private PointList points;

	private int space; /* 离障碍物的间隙 */

	private Vector endDirection;

	private Point endPoint;

	private static final Vector UP = new Vector(0, -1),
			DOWN = new Vector(0, 1), LEFT = new Vector(-1, 0),
			RIGHT = new Vector(1, 0);

	public AIConnectionRouter(IFigure content) {
		this(content, DEFAULT_SPACE);
	}

	public AIConnectionRouter(IFigure content, int space) {
		Assert.isNotNull(content);
		Assert.isLegal(space > 0, "Space must be gt zero.");

		this.content = content;
		this.space = space;
		this.points = new PointList();
	}

	private boolean validConn(Connection conn) {
		if ((conn.getSourceAnchor() == null)
				|| (conn.getTargetAnchor() == null))
			return false;

		// if (conn.getSourceAnchor().getOwner() == null
		// || conn.getTargetAnchor().getOwner() == null)
		// return false;

		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.draw2d.ConnectionRouter#route(org.eclipse.draw2d.Connection)
	 */
	@Override
	public void route(Connection conn) {
		// testChildren(conn);
		Point startPoint = getStartPoint(conn).getCopy();
		// System.out.println("startPoint: " + startPoint);
		conn.translateToRelative(startPoint);
		// System.out.println("relativeStartPoint: " + startPoint);
		points.addPoint(startPoint);

		endPoint = getEndPoint(conn).getCopy();
		// System.out.println("endPoint: " + endPoint);
		conn.translateToRelative(endPoint);

		if (validConn(conn)) {
			Vector sdirection = getStartDirection(conn);
			// System.out.println("startDirection: " + direction);
			endDirection = getEndDirection(conn);

			// System.out.println("endDirection: " + endDirection);
			processPoints(startPoint, sdirection, null);
		}

		points.addPoint(endPoint);
		conn.setPoints(points.getCopy());

		endDirection = null;
		endPoint = null;
		points.removeAllPoints();
	}

	private void processPoints(Point startPoint, Vector direction,
			Rectangle parallelObs) {
		if (direction == UP)
			processUp(startPoint, parallelObs);
		else if (direction == DOWN)
			processDown(startPoint, parallelObs);
		else if (direction == LEFT)
			processLeft(startPoint, parallelObs);
		else
			processRight(startPoint, parallelObs);
	}

	private void processRight(Point startPoint, Rectangle parallelObs) {
		Point newStartPoint = new Point(startPoint);

		int min_xd = 0;
		Rectangle obstracle = null;
		@SuppressWarnings("unchecked")
		List<IFigure> list = content.getChildren();
		for (IFigure f : list) {
			Rectangle fr = f.getBounds();
			if (containPoint(fr, endPoint) || containPoint(fr, startPoint)
					|| fr.x > endPoint.x)
				continue;

			int xd = fr.x - startPoint.x;
			if (xd > 0 && fr.y <= startPoint.y
					&& (fr.y + fr.height) >= startPoint.y) {
				if (xd < min_xd || min_xd == 0) {
					min_xd = xd;
					obstracle = fr;
				}
			}

		}
		if (min_xd == 0) { // no obstacles
			if (parallelObs == null) {
				if (newStartPoint.y == endPoint.y)
					return;

			} else {
				if (newStartPoint.x < parallelObs.x)
					newStartPoint.x -= (parallelObs.x - newStartPoint.x) / 2;
			}

			if (newStartPoint.x < endPoint.x) {
				if (isVertical(endDirection, RIGHT))
					newStartPoint.x = endPoint.x;
				else {
					if (endDirection.equals(RIGHT)) {
						newStartPoint.x = endPoint.x + space;
					} else
						newStartPoint.x += (endPoint.x - newStartPoint.x) / 2;
				}
			}
		} else {
			int x = newStartPoint.x + min_xd - space;
			if (x < newStartPoint.x)
				x = newStartPoint.x + min_xd / 2;
			newStartPoint.x = x;
		}
		if (parallelObs != null) {
			if (newStartPoint.x >= parallelObs.x
					&& newStartPoint.x <= parallelObs.x + parallelObs.width)
				newStartPoint.x = parallelObs.x + parallelObs.width + space;
		}
		if (!newStartPoint.equals(startPoint))
			points.addPoint(newStartPoint);

		// next
		Vector newDirection = UP;
		if (obstracle == null) {
			if (endPoint.y > newStartPoint.y)
				newDirection = DOWN;
		} else {
			if (endPoint.y > obstracle.y)
				newDirection = DOWN;
		}

		processPoints(newStartPoint, newDirection, obstracle);
	}

	private void processLeft(Point startPoint, Rectangle parallelObs) {
		Point newStartPoint = new Point(startPoint);

		int min_xd = 0;
		Rectangle obstracle = null;
		@SuppressWarnings("unchecked")
		List<IFigure> list = content.getChildren();
		for (IFigure f : list) {
			Rectangle fr = f.getBounds();
			if (containPoint(fr, endPoint) || containPoint(fr, startPoint)
					|| (fr.x + fr.width) <= endPoint.x)
				continue;

			int xd = startPoint.x - fr.x - fr.width;
			if (xd > 0 && fr.y <= startPoint.y
					&& (fr.y + fr.height) >= startPoint.y) {
				if (xd < min_xd || min_xd == 0) {
					min_xd = xd;
					obstracle = fr;
				}
			}
		}
		if (min_xd == 0) { // no obstacles
			// not need bend point
			if (parallelObs == null) {
				if (newStartPoint.y == endPoint.y)
					return;
			} else {
				if (newStartPoint.x > parallelObs.x + parallelObs.width)
					newStartPoint.x -= (newStartPoint.x - parallelObs.x - parallelObs.width) / 2;
			}

			if (newStartPoint.x > endPoint.x) {
				if (isVertical(endDirection, LEFT))
					newStartPoint.x = endPoint.x;
				else {
					if (endDirection.equals(LEFT)) {
						newStartPoint.x = endPoint.x - space;
					} else
						newStartPoint.x += (newStartPoint.x - endPoint.x) / 2;
				}
			}
		} else {
			int x = newStartPoint.x + min_xd - space;
			if (x < newStartPoint.x)
				x = newStartPoint.x + min_xd / 2;
			newStartPoint.x = x;
		}
		if (parallelObs != null) {
			if (newStartPoint.x >= parallelObs.x
					&& newStartPoint.x <= (parallelObs.x + parallelObs.width)) {
				newStartPoint.x = parallelObs.x - space;
			}
		}
		if (!newStartPoint.equals(startPoint))
			points.addPoint(newStartPoint);

		// next row point
		Vector newDirection = UP;
		if (obstracle == null) {
			if (endPoint.y > newStartPoint.y)
				newDirection = DOWN;
		} else {
			if (endPoint.y >= obstracle.y)
				newDirection = DOWN;
		}

		processPoints(newStartPoint, newDirection, obstracle);
	}

	private void processDown(Point startPoint, Rectangle parallelObs) {
		Point newStartPoint = new Point(startPoint);

		int min_yd = 0;
		Rectangle obstracle = null;
		@SuppressWarnings("unchecked")
		List<IFigure> list = content.getChildren();
		for (IFigure f : list) {
			Rectangle fr = f.getBounds();
			// System.out.println(fr.width);
			if (containPoint(fr, endPoint) || containPoint(fr, startPoint)
					|| fr.y > endPoint.y)
				continue;

			int yd = fr.y - startPoint.y;
			if (yd > 0 && fr.x <= startPoint.x
					&& (fr.x + fr.width) >= startPoint.x) {
				if (yd < min_yd || min_yd == 0) {
					min_yd = yd;
					obstracle = fr;
				}
			}
		}
		if (min_yd == 0) { // no obstacles
			// not need bend point
			if (parallelObs == null) {
				if (newStartPoint.x == endPoint.x)
					return;
			} else {
				if (parallelObs.y > startPoint.y)
					newStartPoint.y += (parallelObs.y - startPoint.y) / 2;
			}
			if (newStartPoint.y < endPoint.y) {
				if (isVertical(endDirection, DOWN)) {
					newStartPoint.y = endPoint.y; // TODO avoid itself
				} else {
					if (endDirection.equals(DOWN))
						newStartPoint.y = startPoint.y + space;
					else
						newStartPoint.y += (endPoint.y - newStartPoint.y) / 2;
				}
			}
		} else {
			int y = newStartPoint.y + min_yd - space;
			if (y < newStartPoint.y)
				y = newStartPoint.y + min_yd / 2;
			newStartPoint.y = y;
		}

		if (parallelObs != null) {
			if (newStartPoint.y > parallelObs.y
					&& newStartPoint.y < parallelObs.y + parallelObs.height)
				newStartPoint.y = parallelObs.y + parallelObs.height + space;
		}
		if (!newStartPoint.equals(startPoint))
			points.addPoint(newStartPoint);
		// System.out.println("point:" + newStartPoint.toString());

		// next row point
		Vector newDirection = LEFT;
		if (obstracle == null) {
			if (endPoint.x > newStartPoint.x)
				newDirection = RIGHT;
		} else {
			if (endPoint.x > (obstracle.x + obstracle.width))
				newDirection = RIGHT;
		}

		processPoints(newStartPoint, newDirection, obstracle);
	}

	boolean isVertical(Vector v1, Vector v2) {
		double val = v1.x * v2.x + v1.y * v2.y;
		if (val == 0)
			return true;
		return false;
	}

	boolean containPoint(Rectangle r, Point p) {
		return p.x >= r.x && p.x <= r.x + r.width && p.y >= r.y
				&& p.y <= r.y + r.height;
	}

	private void processUp(Point startPoint, Rectangle parallelObs) {
		Point newStartPoint = new Point(startPoint);

		int min_yd = 0;
		Rectangle obstracle = null;
		@SuppressWarnings("unchecked")
		List<IFigure> list = content.getChildren();
		for (IFigure f : list) {
			Rectangle fr = f.getBounds();
			if (containPoint(fr, endPoint) || containPoint(fr, startPoint)
					|| (fr.y + fr.height) <= endPoint.y)
				continue;

			int yd = startPoint.y - fr.y - fr.height;
			if (yd > 0 && fr.x <= startPoint.x
					&& (fr.x + fr.width) >= startPoint.x) {
				if (yd < min_yd || min_yd == 0) {
					min_yd = yd;
					obstracle = fr;
				}
			}
		}
		if (min_yd == 0) { // no obstacles
			// not need bend point
			if (parallelObs == null) {
				if (newStartPoint.x == endPoint.x)
					return;
			} else {
				if (newStartPoint.y > parallelObs.y + parallelObs.height)
					newStartPoint.y -= (newStartPoint.y - parallelObs.y - parallelObs.height) / 2;
			}
			if (newStartPoint.y > endPoint.y) {
				if (isVertical(endDirection, UP))
					newStartPoint.y = endPoint.y;
				else {
					if (endDirection.equals(UP)) {
						newStartPoint.y = endPoint.y - space;
					} else
						newStartPoint.y -= (newStartPoint.y - endPoint.y) / 2;
				}
			}
		} else {
			int y = newStartPoint.y - min_yd + space;
			if (y > newStartPoint.y)
				y = newStartPoint.y - min_yd / 2;
			newStartPoint.y = y;
		}
		if (parallelObs != null) {
			if (newStartPoint.y >= parallelObs.y
					&& newStartPoint.y <= parallelObs.y + parallelObs.height)
				newStartPoint.y = parallelObs.y - space;
		}
		if (!newStartPoint.equals(startPoint))
			points.addPoint(newStartPoint);

		// next row point
		Vector newDirection = LEFT;
		if (obstracle == null) {
			if (endPoint.x > newStartPoint.x)
				newDirection = RIGHT;
		} else {
			if (endPoint.x >= obstracle.x)
				newDirection = RIGHT;
		}

		processPoints(newStartPoint, newDirection, obstracle);
	}

	protected Vector getDirection(Rectangle r, Point p) {
		int i, distance = Math.abs(r.x - p.x);
		Vector direction;

		direction = LEFT;

		i = Math.abs(r.y - p.y);
		if (i <= distance) {
			distance = i;
			direction = UP;
		}

		i = Math.abs(r.bottom() - p.y);
		if (i <= distance) {
			distance = i;
			direction = DOWN;
		}

		i = Math.abs(r.right() - p.x);
		if (i < distance) {
			distance = i;
			direction = RIGHT;
		}

		return direction;
	}

	protected Vector getStartDirection(Connection conn) {
		ConnectionAnchor anchor = conn.getSourceAnchor();
		Point p = getStartPoint(conn);
		Rectangle rect;
		if (anchor.getOwner() == null)
			rect = new Rectangle(p.x - 1, p.y - 1, 2, 2);
		else {
			rect = conn.getSourceAnchor().getOwner().getBounds().getCopy();
			conn.getSourceAnchor().getOwner().translateToAbsolute(rect);
		}
		return getDirection(rect, p);
	}

	protected Vector getEndDirection(Connection conn) {
		ConnectionAnchor anchor = conn.getTargetAnchor();
		Point p = getEndPoint(conn);
		Rectangle rect;
		if (anchor.getOwner() == null)
			rect = new Rectangle(p.x - 1, p.y - 1, 2, 2);
		else {
			rect = conn.getTargetAnchor().getOwner().getBounds().getCopy();
			conn.getTargetAnchor().getOwner().translateToAbsolute(rect);
		}
		return getDirection(rect, p);
	}

	// private IFigure getSourceOwner(Connection conn) {
	// return conn.getSourceAnchor().getOwner();
	// }
	//
	// private IFigure getTargetOwner(Connection conn) {
	// return conn.getTargetAnchor().getOwner();
	// }

	// private void testChildren(Connection conn) {
	// List<Figure> list = content.getChildren();
	// for (Figure f : list) {
	// Rectangle r = f.getBounds();
	// // conn.translateToRelative(r.getCopy());
	// Rectangle rc = r.getCopy();
	// content.translateToRelative(rc.getCopy());
	// System.out.println(rc.toString());
	// // if(f instanceof HandleBounds){
	// //
	// // }
	// }
	// System.out.println("\n");
	// }

}

---源码说明

这里只介绍思路,和关键点

a)实现线路由的基本方式,

继承org.eclipse.draw2d.AbstractRouter-->自定义route()方法,它是布局算法的入口,算法就在这个方法里实现

-->getStartPoint(Connection conn)获取起点+getEndPoint(Connection connection)获取终点+自定义算法获取的其他点,

所有的这些点全部放入PointList中-->route()的最后关键就是conn.setPoints(points.getCopy());这个方法将会重新绘制连线.

b)这里添加的private IFigure content;就是要避开节点的父容器图形;

c)要注意,conn.translateToRelative(startPoint);

这个方法的作用,把点换算成相对线容器的相对坐标,比如说GEF对figure做了放大缩小后,调用这个方法才能保证节点坐标的相对正确性.

d)解决方法,一个基本的障碍物模型+递推+直角线的方式,读者只需看其中一个方向的处理代码就能明白细节思路了.

下图是processDown时的障碍物模型图

障碍物模型

e)后续优化点,在考虑障碍物时,没有把开始和结束点所在的figure作为障碍物,这样就导致了线穿过开始或者结束figure的情况.

 

*添加路由

一般有2中方式给线添加路由

---统一添加方式

在figure的连接层添加,那么这个线路由将作用于其中的所有线

全局添加ConnectionLayer connLayer = (ConnectionLayer) ((CommonNodeEditPart)editPart).getLayer(org.eclipse.gef.LayerConstants.CONNECTION_LAYER);

connLayer.setConnectionRouter(new AIConnectionRouter(content));

---个别添加,直接调用线自身的方法

void org.eclipse.draw2d.PolylineConnection.setConnectionRouter(ConnectionRouter cr)

 

*总结

---draw2d已提供ManhattanConnectionRouter,BendpointConnectionRouter,ShortestPathConnectionRouter等,可以直接使用或者继承后做些修改.

---当需要自己定制连线路由时,继承AbstractRouter,并自定义route(Connection conn)方法,也不难,麻烦的可能就是算法.

---线的路由算法,设计思路是建立解决方案的基本模型,保证通用正确性处理,兼顾考虑特殊情况并给出特殊优化,这也是算法设计的基本思路之一.

转载于:https://www.cnblogs.com/bronte/articles/2374852.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值