From 016a3eca2b9f76f088b1c4b3f9a2de3b32a533bd Mon Sep 17 00:00:00 2001 From: qinoy Date: Tue, 16 May 2023 18:17:38 +0800 Subject: [PATCH] =?UTF-8?q?=E8=8A=82=E7=82=B9=E4=BD=8D=E7=BD=AE=E5=88=86?= =?UTF-8?q?=E5=B8=83=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...oft.apps.coe.method.process.subprocess.jar | Bin 25204 -> 31334 bytes .../process/subprocess/graph/GraphLayout.java | 184 ++++++++++++++++ .../process/subprocess/graph/GraphRender.java | 19 +- .../subprocess/graph/PageRankLayout.java | 197 ++++++++++++++++++ .../process/subprocess/web/SubProcessWeb.java | 14 +- 5 files changed, 399 insertions(+), 15 deletions(-) create mode 100644 com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphLayout.java create mode 100644 com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/PageRankLayout.java diff --git a/com.actionsoft.apps.coe.method.process.subprocess/lib/com.actionsoft.apps.coe.method.process.subprocess.jar b/com.actionsoft.apps.coe.method.process.subprocess/lib/com.actionsoft.apps.coe.method.process.subprocess.jar index f19625b6c6bc96a3e3bfcae5fbbdfd078d61b452..31fbefdb5231fb64e777266999a825e0ccdf98bf 100644 GIT binary patch delta 14440 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 delta 8500 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 nodeList; // 节点集合 + private final boolean[] isPosition; // 节点位置是否确定标记 + private double canvasWidth; // 画布宽度 + private double canvasHeight; // 画布高度 + private final double vertInterval; // 垂直间隔 + private final double horizInterval; // 水平间隔 + private final double[][] position; // 节点坐标 + private final double shapeW; // 图形节点的宽度 + private final double shapeH; // 图形节点的高度 + + + public GraphLayout(int[][] adjMatrix, List nodeList) { + this.adjMatrix = adjMatrix; + this.nodeList = nodeList; + this.isPosition = new boolean[nodeList.size()]; + + this.vertInterval = 50.0; + this.horizInterval = 80.0; + + this.shapeW = 100.0; + this.shapeH = 70.0; + + this.position = new double[nodeList.size()][2]; + } + + /** + * 判断当前节点是否存在后置流程 + * @param nodeIndex 当前节点在集合中的索引 + * @return + */ + private boolean existOutLink(int nodeIndex){ + boolean flag = false; + for (int i = 0; i < adjMatrix[nodeIndex].length; i++) { + if (adjMatrix[nodeIndex][i] == 1){ + flag = true; + break; + } + } + return flag; + } + + /** + * 计算当前节点存在几个后置流程 + * @param nodeIndex + * @return + */ + private int countOutLinks(int nodeIndex){ + int num = 0; + for (int i = 0; i < adjMatrix[nodeIndex].length; i++) { + if (adjMatrix[nodeIndex][i] == 1){ + num++; + } + } + return num; + } + + /** + * 获取当前节点的所有后置节点的索引 + * @param nodeIndex + * @return + */ + private List getRearNodeIndex(int nodeIndex){ + List nodeIndexSet = new ArrayList<>(); + for (int i = 0; i < adjMatrix[nodeIndex].length; i++) { + if (adjMatrix[nodeIndex][i] == 1){ + nodeIndexSet.add(i); + } + } + return nodeIndexSet; + } + + public double getCanvasWidth(){ + return canvasWidth; + } + + public double getCanvasHeight(){ + return canvasHeight; + } + + /** + * 节点布局 + */ + public double[][] layOut(){ + double tempY = 0.0; // 记录第一列的高度 + for (int i = 0; i < nodeList.size(); i++) { + // 第一个节点直接放到画布的左上角 + if (i == 0) { + position[i][0] = 100.0; + position[i][1] = 100.0; + tempY = 100.0; // 更新第一列的高度 + isPosition[i] = true; + if (existOutLink(i)){ // 如果存在后置节点 并且后置节点还未渲染 + calculationRearNodePosition(i); + } + continue; + } + if (!isPosition[i]){ + position[i][0] = 100.0; + position[i][1] = tempY + shapeH + vertInterval; + tempY = position[i][1]; // 更新第一列的高度 + // 存在后置节点 + if (existOutLink(i)){ + calculationRearNodePosition(i); + } + } + } + // 确定画布的宽度与高度 + double w = Arrays.stream(position).mapToDouble(position -> position[0]).max().getAsDouble(); + double h = Arrays.stream(position).mapToDouble(position -> position[1]).max().getAsDouble(); + + this.canvasWidth = w + 200.0; + this.canvasHeight = h + 200.0; + + // 打印节点坐标与画布大小 + System.out.printf("画布(%.2f, %.2f)", canvasWidth, canvasHeight); + for (int i = 0; i < position.length; i++) { + System.out.printf("坐标(%.2f, %.2f)", position[i][0], position[i][1]); + } + + return position; + } + + /** + * 计算当前节点所有后置节点的位置 + * @param nodeIndex + */ + private void calculationRearNodePosition(int nodeIndex){ + // 获取后置节点的索引 + List rearNodeIndexSet = getRearNodeIndex(nodeIndex); + for (int i = 0; i < rearNodeIndexSet.size(); i++) { + int rearNodeIndex = rearNodeIndexSet.get(i).intValue(); + if (!isPosition[rearNodeIndex]) { + position[rearNodeIndex][0] = position[nodeIndex][0] + shapeW + horizInterval; // 上一个节点的x坐标 + 图形宽度 + 间隔 = 当前节点的x坐标 + if (i == 0) { + position[rearNodeIndex][1] = position[nodeIndex][1]; // 上一个节点的y坐标 与 第一个后置节点的y坐标一致 + }else { + position[rearNodeIndex][1] = position[nodeIndex][1] + shapeH + i * vertInterval; // 非第一个后置节点的y坐标 = 上一个节点的y坐标 + 后置节点索引 * 垂直间隔 + } + isPosition[rearNodeIndex] = true; + + if (existOutLink(rearNodeIndex)) { + calculationRearNodePosition(rearNodeIndex); + } + } + } + } + + + public static void main(String[] args) { + // 生成邻接矩阵 + int[][] matrix = { + {0,1,0,0}, + {0,0,1,0}, + {0,0,0,0}, + {1,0,0,0}, + }; + ArrayList nodes = new ArrayList<>(4); + Node node1 = new Node(""); + Node node2 = new Node(""); + Node node3 = new Node(""); + Node node4 = new Node(""); + nodes.add(node1); + nodes.add(node2); + nodes.add(node3); + nodes.add(node4); + GraphLayout graphLayout = new GraphLayout(matrix, nodes); + graphLayout.layOut(); + } +} diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphRender.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphRender.java index 216d5495..7d6804b4 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphRender.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphRender.java @@ -1,16 +1,21 @@ package com.actionsoft.apps.coe.method.process.subprocess.graph; import com.actionsoft.apps.coe.method.process.subprocess.constant.SubProcessConst; +import com.actionsoft.apps.coe.method.process.subprocess.mode.Node; +import com.actionsoft.apps.coe.pal.pal.repository.cache.PALRepositoryCache; import com.actionsoft.apps.coe.pal.pal.repository.designer.manage.CoeDesignerAPIManager; import com.actionsoft.apps.coe.pal.pal.repository.designer.model.BaseModel; import com.actionsoft.apps.coe.pal.pal.repository.designer.util.CoeDesignerUtil; import com.actionsoft.apps.coe.pal.pal.repository.designer.util.ShapeUtil; +import com.actionsoft.apps.coe.pal.pal.repository.model.PALRepositoryModel; import com.actionsoft.bpms.util.UUIDGener; import com.alibaba.fastjson.JSONObject; +import java.util.List; + public class GraphRender { - private final int numVertex; + private final List nodeList; private final double width; // 画布宽度 private final double height; // 画布高度 private final double shapeInterval = 80.0; // 图形节点在画布上的间隔 @@ -20,10 +25,10 @@ public class GraphRender { private final String modelId; - public GraphRender(int numVertex, String modelId, String definition) { - this.numVertex = numVertex; - this.width = numVertex * (shapeInterval + nodeW); - this.height = numVertex * (shapeInterval + nodeH); + public GraphRender(List nodeList, String modelId, String definition, double width, double height) { + this.nodeList = nodeList; + this.width = width; + this.height = height; this.definition = definition; this.modelId = modelId; @@ -35,13 +40,15 @@ public class GraphRender { page.put("width", width); page.put("height", height); JSONObject elements = defineJsonObj.getJSONObject("elements"); - for (int i = 0; i < numVertex; i++) { + for (int i = 0; i < nodeList.size(); i++) { + PALRepositoryModel repositoryModel = PALRepositoryCache.getCache().get(nodeList.get(i).getId()); JSONObject subProcessNode = ShapeUtil.getProcessShapeDefinition(SubProcessConst.SUB_PROCESS_METHOD_ID, "子流程"); String nodeId = UUIDGener.getObjectId(); subProcessNode.put("id", nodeId); JSONObject subProcessNodeProps = subProcessNode.getJSONObject("props"); subProcessNodeProps.put("x", position[i][0]); subProcessNodeProps.put("y", position[i][1]); + subProcessNode.put("text", repositoryModel.getName()); elements.put(nodeId, subProcessNode); } defineJsonObj.put("elements",elements); diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/PageRankLayout.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/PageRankLayout.java new file mode 100644 index 00000000..d06687cd --- /dev/null +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/PageRankLayout.java @@ -0,0 +1,197 @@ +package com.actionsoft.apps.coe.method.process.subprocess.graph; + +import java.util.Arrays; + +/** + * 图模型节点布局 + * 采用 PageRank 算法 + * @author oYang + * @create 2023-05-16 11:10 + */ +public class PageRankLayout { + + private int[][] matrix; // 邻接矩阵 + private double width; // 画布宽度 + private double height; // 画布高度 + private int maxIter; // 最大迭代次数 + private double damp; // 阻尼因子 + private int nodeSize; // 节点数 + private double[] pageRankVal; // PageRank数据 + private final double shapeInterval = 80.0; // 图形节点在画布上的间隔 + private final double nodeW = 100.0; // 图形节点默认宽度 + private final double nodeH = 70.0; // 图形节点默认高度 + + public PageRankLayout(int[][] matrix, int maxIter) { + this.matrix = matrix; + this.maxIter = maxIter; + this.damp = 0.85; + this.nodeSize = matrix.length; + this.pageRankVal = new double[nodeSize]; + + this.width = matrix.length * (shapeInterval + nodeW); + this.height = matrix.length * (shapeInterval + nodeH); + + // 初始化每个节点的 PageRank 值为 1/N + Arrays.fill(pageRankVal, 1.0/nodeSize); + } + + /** + * 计算每个节点的入度加权平均值 + */ + public void calculatePageRankVal(){ + for (int iter = 0; iter < maxIter; iter++) { + double[] nextPR = new double[nodeSize]; + for (int i = 0; i < nodeSize; i++) { + double sum = 0.0; + for (int j = 0; j < nodeSize; j++) { + if (matrix[j][i] == 1) { + sum += pageRankVal[j] / countOutLinks(j); + } + } + nextPR[i] = (1 - damp) / nodeSize + damp * sum; + } + pageRankVal = nextPR; + } + // 输出结果 + for (int i = 0; i < nodeSize; i++) { + System.out.printf("Node %d: PageRank = %.3f\n", i, pageRankVal[i]); + } + } + + + /** + * 根据节点邻接矩阵计算节点的出度 + * @param nodeIndex + * @return + */ + private int countOutLinks(int nodeIndex){ + int count = 0; + for (int i = 0; i < matrix[nodeIndex].length; i++) { + if (matrix[nodeIndex][i] == 1) { + count++; + } + } + return count; + } + + /** + * 根据节点邻接矩阵计算节点入度 + * @param nodeIndex + * @return + */ + private int countInputLinks(int nodeIndex){ + int count = 0; + for (int i = 0; i < matrix.length; i++) { + if (matrix[i][nodeIndex] == 1){ + count++; + } + } + return count; + } + + private void layOut(){ + int n = pageRankVal.length; + // 计算 PageRank 值最大的节点,将该节点定位在画布的中心 + int maxIndex = 0; + double maxPageRank = pageRankVal[0]; + for (int i = 1; i < n; i++) { + if (pageRankVal[i] > maxPageRank) { + maxIndex = i; + maxPageRank = pageRankVal[i]; + } + } + double centerX = width / 2; + double centerY = height / 2; + + // 计算其它节点相对于最大节点的 PageRank 值的比例关系,以及节点数量的平方根 + double scale = Math.sqrt(n); + double[] distX = new double[n]; + double[] distY = new double[n]; + for (int i = 0; i < n; i++) { + if (i == maxIndex) { + distX[i] = 0; + distY[i] = 0; + } else { + double ratio = pageRankVal[i] / pageRankVal[maxIndex]; + distX[i] = ratio * Math.cos(2 * Math.PI * i / n); + distY[i] = ratio * Math.sin(2 * Math.PI * i / n); + } + } + + // 将节点的 PageRank 值乘以比例关系和节点数量的平方根,得到节点在横向和竖向上的缩放比例 + double[] scaleX = new double[n]; + double[] scaleY = new double[n]; + for (int i = 0; i < n; i++) { + scaleX[i] = distX[i] * scale; + scaleY[i] = distY[i] * scale; + } + + // 将横向和竖向上的缩放比例分别乘以画布的宽度和高度,得到节点在画布上的实际坐标 + double[] x = new double[n]; + double[] y = new double[n]; + for (int i = 0; i < n; i++) { + x[i] = centerX + (int)(scaleX[i] * 200); + y[i] = centerY + (int)(scaleY[i] * 200); + } + + for (int i = 0; i < nodeSize; i++) { + System.out.printf("NodeIndex %d: Position [%f%n,%f%n]", i, x[i], y[i]); + } + } + + /** + * 根据节点 PageRank 值计算节点坐标 + * + * @param pageRank 节点 PageRank 值 + * @param canvasWidth 画布宽度 + * @param canvasHeight 画布高度 + * @return 节点坐标数组,每个点都是 (x, y) 形式 + */ + public double[][] computeNodeCoordinates() { + int n = pageRankVal.length; + double[][] nodeCoords = new double[n][2]; + + double maxPageRank = Double.NEGATIVE_INFINITY; + for (double x : pageRankVal) { + maxPageRank = Math.max(maxPageRank, x); + } + + double centerX = width / 2.0; + double centerY = height / 2.0; + double radius = Math.min(centerX, centerY) - 50; // 保留一些边距避免节点被切掉 + for (int i = 0; i < n; i++) { + double angle = 2 * Math.PI * i / n; + double x = centerX + (pageRankVal[i] / maxPageRank) * radius * Math.cos(angle); + double y = centerY + (pageRankVal[i] / maxPageRank) * radius * Math.sin(angle); + nodeCoords[i][0] = x; + nodeCoords[i][1] = y; + } + + return nodeCoords; + } + + + public static void main(String[] args) { + // 生成邻接矩阵 + int[][] matrix = { + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + }; + PageRankLayout pageRankLayout = new PageRankLayout(matrix, 100); + pageRankLayout.calculatePageRankVal(); + double[][] nodeCoords = pageRankLayout.computeNodeCoordinates(); + + // 输出每个节点在直角坐标系中的坐标 + for (int i = 0; i < nodeCoords.length; i++) { + System.out.printf("(%.2f, %.2f)\n", nodeCoords[i][0], nodeCoords[i][1]); + } + } +} diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/web/SubProcessWeb.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/web/SubProcessWeb.java index 471c79b1..2c7163d2 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/web/SubProcessWeb.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/web/SubProcessWeb.java @@ -1,10 +1,7 @@ package com.actionsoft.apps.coe.method.process.subprocess.web; import com.actionsoft.apps.coe.method.process.subprocess.constant.SubProcessConst; -import com.actionsoft.apps.coe.method.process.subprocess.graph.ForceDirectedGraphLayout; -import com.actionsoft.apps.coe.method.process.subprocess.graph.GraphAdjMatrix; -import com.actionsoft.apps.coe.method.process.subprocess.graph.GraphRender; -import com.actionsoft.apps.coe.method.process.subprocess.graph.VertexPreHandle; +import com.actionsoft.apps.coe.method.process.subprocess.graph.*; import com.actionsoft.apps.coe.method.process.subprocess.mode.Node; import com.actionsoft.apps.coe.method.process.subprocess.mode.vo.SubProcessTagVo; import com.actionsoft.apps.coe.pal.constant.CoEConstant; @@ -229,12 +226,11 @@ public class SubProcessWeb extends ActionWeb { // 构建有向图邻接矩阵 GraphAdjMatrix graphAdjMatrix = new GraphAdjMatrix(nodeList); graphAdjMatrix.buildAdjMatrix(nodeIndexMap); - // graphAdjMatrix.printAdjMatrix(); + graphAdjMatrix.printAdjMatrix(); // 获取节点分布 - ForceDirectedGraphLayout graphLayout = new ForceDirectedGraphLayout(graphAdjMatrix.getAdjMatrix()); - graphLayout.run(); - double[][] position = graphLayout.getPosition(); + GraphLayout graphLayout = new GraphLayout(graphAdjMatrix.getAdjMatrix(), nodeList); + double[][] position = graphLayout.layOut(); // 新建模型 PALRepositoryModel parentModel = PALRepositoryCache.getCache().get(locationId); @@ -259,7 +255,7 @@ public class SubProcessWeb extends ActionWeb { baseModel.setDefinition(obj.getString("define")); // 节点渲染 - GraphRender graphRender = new GraphRender(nodeList.size(), model.getId(), baseModel.getDefinition()); + GraphRender graphRender = new GraphRender(nodeList, model.getId(), baseModel.getDefinition(), graphLayout.getCanvasWidth(), graphLayout.getCanvasHeight()); graphRender.handleShapeNodeRender(position); }