Procházet zdrojové kódy

连线布局算法优化;连线支持途径干管

zhaoyk před 2 roky
rodič
revize
283c4a7e2d

+ 13 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/line/LineEnd.java

@@ -19,6 +19,11 @@ public class LineEnd {
 	private XY point;
 
 	/**
+	 * 模式1下,锚点所在的边
+	 */
+	private XY[] anchorSide;
+
+	/**
 	 * 模式2,线段(干管)
 	 */
 	private XY[] line;
@@ -31,6 +36,14 @@ public class LineEnd {
 		this.point = point;
 	}
 
+	public XY[] getAnchorSide() {
+		return anchorSide;
+	}
+
+	public void setAnchorSide(XY[] anchorSide) {
+		this.anchorSide = anchorSide;
+	}
+
 	public XY[] getLine() {
 		return line;
 	}

+ 166 - 9
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/line/LineLayoutManager.java

@@ -1,19 +1,18 @@
 package com.persagy.adm.diagram.core.line;
 
+import cn.hutool.core.collection.CollUtil;
 import com.persagy.adm.diagram.core.model.ConnectPoint;
 import com.persagy.adm.diagram.core.model.DiagramNode;
 import com.persagy.adm.diagram.core.model.EquipmentNode;
 import com.persagy.adm.diagram.core.model.Line;
+import com.persagy.adm.diagram.core.model.base.Container;
 import com.persagy.adm.diagram.core.model.base.XY;
 import com.persagy.adm.diagram.core.build.CalcContext;
 import com.persagy.adm.diagram.core.model.template.MainPipe;
 import com.persagy.adm.diagram.core.util.GeomUtil;
 import org.locationtech.jts.geom.Point;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * 连线布局计算
@@ -64,24 +63,156 @@ public class LineLayoutManager {
 		if(checkLine(p1, p2)) {
 			lines.put(line, new ConnectPoint[]{p1, p2});
 
-			LineEnd[] arr = buildLineEnd(p1, p2);
-			PathBuilder pb = new PathBuilder(line, arr[0], arr[1], exchange);
-			return pb.calcPath(blocks);
+			MainPipe passBy = findPassMainPipe(line);
+			if(passBy != null) {
+				ConnectPoint p3 = new ConnectPoint();
+				p3.setHostObj(passBy);
+				p3.setHostType(MainPipe.TYPE);
+
+				List<XY> points1 = calcPath(p1, p3, false);
+				List<XY> points2 = calcPath(p2, p3, true);
+
+				List<XY> passByPoints = getPassByPoints(points1, points2, passBy);
+				if(passByPoints != null){
+					points1.addAll(passByPoints);
+				}
+				points1.addAll(points2);
+
+				if(exchange) {
+					Collections.reverse(points1);
+				}
+				return points1;
+			} else {
+				return calcPath(p1, p2, exchange);
+			}
 		} else {
 			line.setFlag(Line.FLAG_DUPLICATE);
 			return buildDuplicateMark(p1, p2);
 		}
 	}
 
+	private MainPipe findPassMainPipe(Line line){
+		EquipmentNode fromNode = line.getFrom().getEquipmentNode();
+		EquipmentNode toNode = line.getTo().getEquipmentNode();
+
+		if(fromNode == null || toNode == null){
+			return null;
+		}
+
+		List<MainPipe> mainPipes = context.getTemplate().getMainPipes();
+		if (CollUtil.isNotEmpty(mainPipes)) {
+			for(MainPipe mp : mainPipes) {
+				if (!mp.isBindEquipment() && mp.getPassbyRels() != null && mp.getPassbyRels().contains(line.getRelType())) {
+					boolean flag1 = false, flag2 = false;
+					List<Container> relCons = mp.getRelatedContainerList();
+					if(relCons != null) {
+						for (Container con : relCons) {
+							if(!flag1 && con.isParentOf(fromNode)){
+								flag1 = true;
+							}
+							if(!flag2 && con.isParentOf(toNode)){
+								flag2 = true;
+							}
+							if(flag1 && flag2){
+								break;
+							}
+						}
+					}
+					if (flag1 && flag2) {
+						return mp;
+					}
+				}
+			}
+		}
+		return null;
+	}
+
+	private List<XY> getPassByPoints(List<XY> part1, List<XY> part2, MainPipe passBy){
+		XY p1 = part1.get(part1.size() - 1);
+		XY p2 = part2.get(0);
+		for (List<XY> line : passBy.getLocationPath()) {
+			int idx1 = -1, idx2 = -1;
+			XY pre = null;
+			int iter = 0;
+			for(XY point : line) {
+				if(pre != null) {
+					if(idx1 == -1 && onLine(pre, point, p1)){
+						idx1 = iter;
+					}
+					if(idx2 == -1 && onLine(pre, point, p2)){
+						idx2 = iter;
+					}
+					if(idx1 >= 0 && idx2 >= 0){
+						break;
+					}
+
+					iter++;
+				}
+				pre = point;
+			}
+
+			if(idx1 >= 0 && idx2 >= 0 && idx1 != idx2) {
+				List<XY> passByPoints = new LinkedList<>();
+				boolean r = idx1 > idx2;
+				for(int i = (!r ? idx1 : idx2); i < (!r ? idx2 : idx1); i++){
+					passByPoints.add(line.get(i + 1));
+				}
+				if(r){
+					Collections.reverse(passByPoints);
+				}
+				return passByPoints;
+			}
+		}
+		return null;
+	}
+
+	private boolean onLine(XY lineEnd1, XY lineEnd2, XY point) {
+		int begin, end;
+		if(lineEnd1.x == lineEnd2.x){
+			if(point.x == lineEnd1.x){
+				begin = Math.min(lineEnd1.y, lineEnd2.y);
+				end = Math.max(lineEnd1.y, lineEnd2.y);
+				return point.y >= begin && point.y <= end;
+			}
+		} else {
+			if(point.y == lineEnd1.y){
+				begin = Math.min(lineEnd1.x, lineEnd2.x);
+				end = Math.max(lineEnd1.x, lineEnd2.x);
+				return point.x >= begin && point.x <= end;
+			}
+		}
+		return false;
+	}
+
+	private List<XY> calcPath(ConnectPoint p1, ConnectPoint p2, boolean exchange){
+		LineEnd[] arr = buildLineEnd(p1, p2);
+		List<XY> points = new PathBuilder(arr[0], arr[1], p1).calcPath(blocks);
+		finishPath(points, p1, p2, exchange);
+		return points;
+	}
+
+	private void finishPath(List<XY> points, ConnectPoint p1, ConnectPoint p2, boolean exchange){
+		if (p1.getLocation() != null) {
+			points.add(0, p1.getLocation());
+		}
+		if (p2.getLocation() != null) {
+			points.add(p2.getLocation());
+		}
+
+		if (exchange) {
+			Collections.reverse(points);
+		}
+	}
+
 	private LineEnd[] buildLineEnd(ConnectPoint p1, ConnectPoint p2) {
 		LineEnd[] arr = new LineEnd[2];
 
 		arr[0] = new LineEnd();
-		arr[0].setPoint(anchorEmerge(p1));
+		prepareAnchorLineEnd(arr[0], p1);
 
 		if(p2.getHostObj() instanceof EquipmentNode) {
 			arr[1] = new LineEnd();
-			arr[1].setPoint(anchorEmerge(p2));
+			prepareAnchorLineEnd(arr[1], p2);
 		} else if (p2.getHostObj() instanceof MainPipe) {
 			Point point1 = GeomUtil.getPoint(p1.getLocation());
 			MainPipe mp = (MainPipe) p2.getHostObj();
@@ -125,6 +256,11 @@ public class LineLayoutManager {
 		return arr;
 	}
 
+	private void prepareAnchorLineEnd(LineEnd lineEnd, ConnectPoint connectPoint) {
+		lineEnd.setPoint(anchorEmerge(connectPoint));
+		lineEnd.setAnchorSide(getAnchorSide(connectPoint));
+	}
+
 	/**
 	 * 锚点出线
 	 */
@@ -148,6 +284,27 @@ public class LineLayoutManager {
 		return xy;
 	}
 
+	/**
+	 * 获取锚点所在的节点边路径
+	 */
+	private XY[] getAnchorSide(ConnectPoint point){
+		EquipmentNode en = point.getEquipmentNode();
+		XY loc = en.locationToRoot();
+		XY size = en.getSize();
+		switch (point.getAnchorCode().charAt(0)) {
+			case 'T':
+				return new XY[] {loc, new XY(loc.x + size.x, loc.y)};
+			case 'B':
+				return new XY[] {new XY(loc.x, loc.y + size.y), new XY(loc.x + size.x, loc.y + size.y)};
+			case 'L':
+				return new XY[] {loc, new XY(loc.x, loc.y + size.y)};
+			case 'R':
+				return new XY[] {new XY(loc.x + size.x, loc.y), new XY(loc.x + size.x, loc.y + size.y)};
+			default:
+		}
+		return null;
+	}
+
 	private boolean checkLine(ConnectPoint p1, ConnectPoint p2){
 		for(ConnectPoint[] points : lines.values()) {
 			if(p1.sameWith(points[0]) && p2.sameWith(points[1])){

+ 65 - 49
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/line/PathBuilder.java

@@ -1,15 +1,10 @@
 package com.persagy.adm.diagram.core.line;
 
 import cn.hutool.core.util.ArrayUtil;
-import com.persagy.adm.diagram.core.model.Line;
+import com.persagy.adm.diagram.core.model.ConnectPoint;
 import com.persagy.adm.diagram.core.model.base.XY;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Random;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -29,14 +24,10 @@ public class PathBuilder {
 
     private static int edge = 10;
 
-    private Line line;
-
     private LineEnd end1;
 
     private LineEnd end2;
 
-    private boolean exchange;
-
     private List<XY> points = new ArrayList<>();
 
     private XY currentPoint;
@@ -45,14 +36,12 @@ public class PathBuilder {
 
     private Random random = new Random();
 
-    public PathBuilder(Line line, LineEnd end1, LineEnd end2, boolean exchange) {
-        this.line = line;
+    public PathBuilder(LineEnd end1, LineEnd end2, ConnectPoint from) {
         this.end1 = end1;
         this.end2 = end2;
-        this.exchange = exchange;
 
         setCurrentPoint(end1.getPoint());
-        String fromAnchor = (!exchange ? line.getFrom() : line.getTo()).getAnchorCode();
+        String fromAnchor = from.getAnchorCode();
         switch (fromAnchor.charAt(0)) {
             case 'T':
                 fromDirection = D_DOWN;
@@ -77,18 +66,6 @@ public class PathBuilder {
 
     public List<XY> calcPath(List<Block> blocks) {
         findPath(blocks);
-
-        if (exchange) {
-            Collections.reverse(points);
-        }
-
-        if (line.getFrom().getLocation() != null) {
-            points.add(0, line.getFrom().getLocation());
-        }
-        if (line.getTo().getLocation() != null) {
-            points.add(line.getTo().getLocation());
-        }
-
         return points;
     }
 
@@ -215,14 +192,17 @@ public class PathBuilder {
                     targetPos = stopPos;
                 } else { //移动到垂直参考方向上blockLine变化的位置
                     for (BlockLine crossBl : crossBlockLines) {
-                        boolean valid = !crossReverse ? crossBl.base > trackPos : crossBl.base < targetPos;
-                        if (valid && crossBl.begin <= pos && crossBl.end >= pos) {
-                            if (!reverse) {
-                                targetPos = crossBl.end;
-                            } else {
-                                targetPos = crossBl.begin;
+                        boolean valid = !crossReverse ? crossBl.base > trackPos : crossBl.base < trackPos;
+                        if (valid) {
+                            int safeOffset = 5;
+                            if(!reverse && crossBl.end > pos){
+                                targetPos = crossBl.end + safeOffset;
+                                break;
+                            }
+                            if(reverse && crossBl.begin < pos) {
+                                targetPos = crossBl.begin - safeOffset;
+                                break;
                             }
-                            break;
                         }
                     }
                     //计算失败,尝试下一方向
@@ -244,12 +224,44 @@ public class PathBuilder {
     private Block buildRectArea() {
         int[] xs;
         int[] ys;
-        if (end2.getPoint() != null) {
-            xs = new int[]{currentPoint.x, end2.getPoint().x};
-            ys = new int[]{currentPoint.y, end2.getPoint().y};
+
+        boolean begin = points.size() == 1;
+        if(begin) {
+            xs = new int[4];
+            ys = new int[4];
+            if (LineEnd.isHorizontal(end1.getAnchorSide())) {
+                xs[0] = end1.getAnchorSide()[0].x;
+                xs[1] = end1.getAnchorSide()[1].x;
+                ys[0] = ys[1] = currentPoint.y;
+            } else {
+                xs[0] = xs[1] = currentPoint.x;
+                ys[0] = end1.getAnchorSide()[0].y;
+                ys[1] = end1.getAnchorSide()[1].y;
+            }
+            if (end2.getPoint() != null) {
+                if (LineEnd.isHorizontal(end2.getAnchorSide())) {
+                    xs[2] = end2.getAnchorSide()[0].x;
+                    xs[3] = end2.getAnchorSide()[1].x;
+                    ys[2] = ys[3] = end2.getPoint().y;
+                } else {
+                    xs[2] = xs[3] = end2.getPoint().x;
+                    ys[2] = end2.getAnchorSide()[0].y;
+                    ys[3] = end2.getAnchorSide()[1].y;
+                }
+            } else {
+                xs[2] = end2.getLine()[0].x;
+                xs[3] = end2.getLine()[1].x;
+                ys[2] = end2.getLine()[0].y;
+                ys[3] = end2.getLine()[1].y;
+            }
         } else {
-            xs = new int[]{currentPoint.x, end2.getLine()[0].x, end2.getLine()[1].x};
-            ys = new int[]{currentPoint.y, end2.getLine()[0].y, end2.getLine()[1].y};
+            if (end2.getPoint() != null) {
+                xs = new int[]{currentPoint.x, end2.getPoint().x};
+                ys = new int[]{currentPoint.y, end2.getPoint().y};
+            } else {
+                xs = new int[]{currentPoint.x, end2.getLine()[0].x, end2.getLine()[1].x};
+                ys = new int[]{currentPoint.y, end2.getLine()[0].y, end2.getLine()[1].y};
+            }
         }
 
         XY location = new XY(ArrayUtil.min(xs), ArrayUtil.min(ys));
@@ -265,22 +277,25 @@ public class PathBuilder {
     private int[] findDirection() {
         int[] rtn = new int[4];
 
+        boolean vPriority;
         if (end2.getPoint() != null) {
             XY endPoint = end2.getPoint();
+            vPriority = Math.abs(endPoint.y - currentPoint.y) > Math.abs(endPoint.x - currentPoint.x);
             if (endPoint.x == currentPoint.x) {
-                setDirection(rtn, null, endPoint.y > currentPoint.y);
+                setDirection(rtn, null, endPoint.y > currentPoint.y, vPriority);
             } else if (endPoint.y == currentPoint.y) {
-                setDirection(rtn, endPoint.x > currentPoint.x, null);
+                setDirection(rtn, endPoint.x > currentPoint.x, null, vPriority);
             } else {
-                setDirection(rtn, endPoint.x > currentPoint.x, endPoint.y > currentPoint.y);
+                setDirection(rtn, endPoint.x > currentPoint.x, endPoint.y > currentPoint.y, vPriority);
             }
         } else {
             int trackPos = end2.getLineTrackPos();
             int[] scope = end2.getLineScope();
             if (end2.isLineHorizontal()) {
+                vPriority = Math.abs(end2.getLineTrackPos() - currentPoint.y) > Math.min(Math.abs(scope[0] - currentPoint.x), Math.abs(scope[1] - currentPoint.x));
                 boolean xMatch = scope[0] <= currentPoint.x && scope[1] >= currentPoint.x;
                 if (xMatch) {
-                    setDirection(rtn, null, trackPos > currentPoint.y);
+                    setDirection(rtn, null, trackPos > currentPoint.y, vPriority);
                 } else if (trackPos == currentPoint.y) {
                     //需要垂直交汇
                     rtn[0] = D_DOWN;
@@ -291,9 +306,10 @@ public class PathBuilder {
                     rtn[3] = right ? D_LEFT : D_RIGHT;
                 }
             } else {
-                boolean yMatch = scope[0] <= currentPoint.y && scope[0] >= currentPoint.y;
+                vPriority = Math.min(Math.abs(scope[0] - currentPoint.y), Math.abs(scope[1] - currentPoint.y)) > Math.abs(end2.getLineTrackPos() - currentPoint.x);
+                boolean yMatch = scope[0] <= currentPoint.y && scope[1] >= currentPoint.y;
                 if (yMatch) {
-                    setDirection(rtn, trackPos > currentPoint.x, null);
+                    setDirection(rtn, trackPos > currentPoint.x, null, vPriority);
                 } else if (trackPos == currentPoint.x) {
                     //需要垂直交汇
                     rtn[0] = D_RIGHT;
@@ -306,17 +322,17 @@ public class PathBuilder {
             }
             if (rtn[0] == 0) {
                 XY p0 = end2.getLine()[0];
-                setDirection(rtn, p0.x > currentPoint.x, p0.y > currentPoint.y);
+                setDirection(rtn, p0.x > currentPoint.x, p0.y > currentPoint.y, vPriority);
             }
         }
 
         return rtn;
     }
 
-    private void setDirection(int[] directions, Boolean right, Boolean down) {
+    private void setDirection(int[] directions, Boolean right, Boolean down, boolean vPriority) {
         if (right != null && down != null) {
-            directions[0] = right ? D_RIGHT : D_LEFT;
-            directions[1] = down ? D_DOWN : D_UP;
+            directions[vPriority ? 1 : 0] = right ? D_RIGHT : D_LEFT;
+            directions[vPriority ? 0 : 1] = down ? D_DOWN : D_UP;
             directions[2] = right ? D_LEFT : D_RIGHT;
             directions[3] = down ? D_UP : D_DOWN;
         } else if (right != null) {