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 72faf8f5..b0e77704 100644 Binary files a/com.actionsoft.apps.coe.method.process.subprocess/lib/com.actionsoft.apps.coe.method.process.subprocess.jar and b/com.actionsoft.apps.coe.method.process.subprocess/lib/com.actionsoft.apps.coe.method.process.subprocess.jar differ diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/SubProcessController.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/SubProcessController.java index ca4a35d2..a776663e 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/SubProcessController.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/SubProcessController.java @@ -130,9 +130,9 @@ public class SubProcessController { } @Mapping("com.actionsoft.apps.coe.method.process.subprocess.shape_expand") - public String shapeExpand(UserContext uc, String repositoryId, String shapeId){ - - return ResponseObject.newOkResponse("展开成功").toString(); + public String shapeExpand(UserContext uc, String repositoryId, String shapeId, String direction){ + SubProcessWeb processWeb = new SubProcessWeb(uc); + return processWeb.shapeNodeExpand(repositoryId, shapeId, direction); } } diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/constant/SubProcessConst.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/constant/SubProcessConst.java index 04c50359..0c773c73 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/constant/SubProcessConst.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/constant/SubProcessConst.java @@ -33,4 +33,7 @@ public interface SubProcessConst { double SHAPE_VERT_INTERVAL = 80.0; // 图形间水平间隔 double SHAPE_HORIZ_INTERVAL = 80.0; + + // 范围选择框内边距 + double SCOPE_SHAPE_PADDING = 300; } diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphAdjMatrix.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphAdjMatrix.java index 843b87ad..9a408706 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphAdjMatrix.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphAdjMatrix.java @@ -17,7 +17,6 @@ public class GraphAdjMatrix { private int[][] adjMatrix; // 邻接矩阵 private List vertexList; // 存储节点 - private int numEdges; // 边的数目 /** * 构造函数 初始化邻接矩阵 @@ -26,7 +25,6 @@ public class GraphAdjMatrix { public GraphAdjMatrix(List nodeList) { adjMatrix = new int[nodeList.size()][nodeList.size()]; vertexList = nodeList; - numEdges = 0; } /** @@ -34,7 +32,6 @@ public class GraphAdjMatrix { */ public void addEdge(int u, int v) { adjMatrix[u][v] = 1; // 设置邻接矩阵中相应的位置为 1 - numEdges++; } /** @@ -57,14 +54,6 @@ public class GraphAdjMatrix { return adjMatrix[u][v] == 1; } - /** - * 获取边的树目 - * @return - */ - public int getNumEdges(){ - return numEdges; - } - /** * 获取邻接矩阵 * @return diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphLinkerRender.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphLinkerRender.java index db09bc02..c1e5b7d0 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphLinkerRender.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphLinkerRender.java @@ -3,6 +3,7 @@ package com.actionsoft.apps.coe.method.process.subprocess.graph; import com.actionsoft.apps.coe.method.process.subprocess.constant.LinkerDefConstant; import com.actionsoft.apps.coe.method.process.subprocess.constant.SubProcessConst; import com.actionsoft.apps.coe.method.process.subprocess.mode.Node; +import com.actionsoft.bpms.util.ConsolePrinter; import com.actionsoft.bpms.util.UUIDGener; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -180,15 +181,6 @@ public class GraphLinkerRender { {toX + SubProcessConst.SUB_PROCESS_SHAPE_W / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, endPoint }; - }else if (fromX > toX && fromY < toY){ // 目标节点在第三象限 - double[] startPoint = new double[]{fromX + SubProcessConst.SUB_PROCESS_SHAPE_W / 2, fromY + SubProcessConst.SUB_PROCESS_SHAPE_H}; - double[] endPoint = new double[]{toX + SubProcessConst.SUB_PROCESS_SHAPE_W / 2, toY}; - return new double[][]{ - startPoint, - {fromX + SubProcessConst.SUB_PROCESS_SHAPE_W / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, - {toX + SubProcessConst.SUB_PROCESS_SHAPE_W / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, - endPoint - }; }else if (fromX < toX && fromY < toY){ // 目标节点在第四象限 double[] startPoint = new double[]{fromX + SubProcessConst.SUB_PROCESS_SHAPE_W, fromY + SubProcessConst.SUB_PROCESS_SHAPE_H / 2}; if (fromX + SubProcessConst.SUB_PROCESS_SHAPE_W + SubProcessConst.SHAPE_HORIZ_INTERVAL == toX){ // 相邻节点 存在两个折点 @@ -201,15 +193,17 @@ public class GraphLinkerRender { endPoint }; }else { // 不相邻节点 存在三个折点 - double[] endPoint = new double[]{toX + SubProcessConst.SUB_PROCESS_SHAPE_W / 2, toY + SubProcessConst.SUB_PROCESS_SHAPE_H}; + double[] endPoint = new double[]{toX + SubProcessConst.SUB_PROCESS_SHAPE_W / 2, toY}; return new double[][]{ startPoint, {fromX + SubProcessConst.SUB_PROCESS_SHAPE_W + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + SubProcessConst.SUB_PROCESS_SHAPE_H / 2}, - {fromX + SubProcessConst.SUB_PROCESS_SHAPE_W + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY + SubProcessConst.SHAPE_VERT_INTERVAL / 2}, - {toX + SubProcessConst.SUB_PROCESS_SHAPE_W / 2, toY + SubProcessConst.SUB_PROCESS_SHAPE_H + SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {fromX + SubProcessConst.SUB_PROCESS_SHAPE_W + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + SubProcessConst.SUB_PROCESS_SHAPE_W / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, endPoint }; } + }else if (fromX > toX && fromY < toY){ // 目标节点在第三象限 横向布局的情况下 应该不会出现目标节点在第三象限的情况 + ConsolePrinter.warn("[端到端功能][总图生成模块]处理连线时目标节点在[横向布局]的情况下出现在了第三象限"); } } return new double[2][2]; diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphNodeExpandHandle.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphNodeExpandHandle.java index ec8fc35b..51dd9b8a 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphNodeExpandHandle.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphNodeExpandHandle.java @@ -1,17 +1,23 @@ package com.actionsoft.apps.coe.method.process.subprocess.graph; +import com.actionsoft.apps.coe.method.process.subprocess.constant.LinkerDefConstant; 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.designer.manage.CoeDesignerAPIManager; import com.actionsoft.apps.coe.pal.pal.repository.designer.model.BaseModel; import com.actionsoft.apps.coe.pal.pal.repository.designer.relation.cache.DesignerShapeRelationCache; import com.actionsoft.apps.coe.pal.pal.repository.designer.relation.model.DesignerShapeRelationModel; +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.bpms.util.ConsolePrinter; +import com.actionsoft.bpms.util.UUIDGener; import com.actionsoft.bpms.util.UtilString; import com.actionsoft.exception.AWSException; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import java.util.List; +import java.util.*; +import java.util.concurrent.locks.ReentrantLock; /** * 图节点展开处理 @@ -22,46 +28,59 @@ public class GraphNodeExpandHandle { private String shapeId; // 总图中当前要展开的子流程图形ID private String relationFileId; // 当前要展开的子流程图形所标识模型文件ID private CoeDesignerAPIManager apiManager; - private String childProcessDefine; // 要展开的子流程模型信息 - private String endToEndProcessDefine; // 总图的模型信息 + private JSONObject childProcessDefine; // 要展开的子流程模型信息 + private JSONObject endToEndProcessDefine; // 总图的模型信息 + private JSONObject scopeLimitationShape; // 范围标注框 - public GraphNodeExpandHandle(String repositoryId, String shapeId) { + private double[] scopeLimitationShapeBeforePoi; + + private final ReentrantLock lock = new ReentrantLock(); + + public GraphNodeExpandHandle(String repositoryId, String shapeId) throws AWSException{ this.repositoryId = repositoryId; this.shapeId = shapeId; apiManager = CoeDesignerAPIManager.getInstance(); - readChildProcessDefine(); - readCurrentProcessDefine(); + try { + readChildProcessDefine(); + readCurrentProcessDefine(); + toAssembleScopeLimitationShape(); + } catch (AWSException e) { + throw new AWSException(e); + } } /** * 读取子流程节点的存储信息 * @throws AWSException */ - public void readChildProcessDefine() throws AWSException{ + private void readChildProcessDefine() throws AWSException{ List childProcessModelList = DesignerShapeRelationCache.getListByAttrId(repositoryId, shapeId, SubProcessConst.CHILD_PROCESS); DesignerShapeRelationModel relationModel = childProcessModelList.stream().findFirst().orElse(null); if (relationModel == null) throw new AWSException("未找到当前节点所标识的子流程文件信息"); relationFileId = relationModel.getRelationFileId(); // 先去与总图存储的同级目录下读取 如果为空说明是初次读取 - childProcessDefine = apiManager.getChildProcessDefine(repositoryId, 0, relationFileId); - if (UtilString.isEmpty(childProcessDefine)){ // 初次读取 去源文件目录读取 + String childProcessDefineStr = apiManager.getChildProcessDefine(repositoryId, 0, relationFileId); + if (UtilString.isEmpty(childProcessDefineStr)){ // 初次读取 去源文件目录读取 BaseModel childProcessBaseModel = apiManager.getDefinition(relationFileId, 0); - childProcessDefine = childProcessBaseModel.getDefinition(); + if (childProcessBaseModel == null) + throw new AWSException("当前子流程节点内部可能没有图形元素,可以去添加后展开"); + childProcessDefineStr = childProcessBaseModel.getDefinition(); } + childProcessDefine = JSONObject.parseObject(childProcessDefineStr); } /** * 读取当前总图的存储信息 * @throws AWSException */ - public void readCurrentProcessDefine() throws AWSException{ + private void readCurrentProcessDefine() throws AWSException{ BaseModel baseModel = apiManager.getDefinition(repositoryId, 0); if (baseModel == null) throw new AWSException("未找到当前总图存储的模型信息"); - endToEndProcessDefine = baseModel.getDefinition(); + endToEndProcessDefine = JSONObject.parseObject(baseModel.getDefinition()); } /** @@ -69,16 +88,40 @@ public class GraphNodeExpandHandle { * @return 范围标注框 * @throws AWSException */ - private JSONObject toAssembleScopeLimitationShape() throws AWSException{ + private void toAssembleScopeLimitationShape() throws AWSException{ JSONObject scopeLimitationShape = ShapeUtil.getProcessShapeDefinition(SubProcessConst.SUB_PROCESS_METHOD_ID, "展开范围标注"); - JSONObject childProcessDefineObj = JSONObject.parseObject(childProcessDefine); - JSONObject childProcessPage = childProcessDefineObj.getJSONObject("page"); + JSONObject childProcessPage = childProcessDefine.getJSONObject("page"); + JSONObject childProcessElements = childProcessDefine.getJSONObject("elements"); + JSONObject childProcessEleMaxX = childProcessElements.keySet() + .stream() + .filter(key -> !"linker".equals(childProcessElements.getJSONObject(key).getString("name"))) + .map(key -> childProcessElements.getJSONObject(key).getJSONObject("props")) + .max((o1, o2) -> Double.compare(o1.getDoubleValue("x"), o2.getDoubleValue("x"))).get(); + JSONObject childProcessEleMixX = childProcessElements.keySet() + .stream() + .filter(key -> !"linker".equals(childProcessElements.getJSONObject(key).getString("name"))) + .map(key -> childProcessElements.getJSONObject(key).getJSONObject("props")) + .min((o1, o2) -> Double.compare(o1.getDoubleValue("x"), o2.getDoubleValue("x"))).get(); + JSONObject childProcessEleMaxY = childProcessElements.keySet() + .stream() + .filter(key -> !"linker".equals(childProcessElements.getJSONObject(key).getString("name"))) + .map(key -> childProcessElements.getJSONObject(key).getJSONObject("props")) + .max((o1, o2) -> Double.compare(o1.getDoubleValue("y"), o2.getDoubleValue("y"))).get(); + JSONObject childProcessEleMinY = childProcessElements.keySet() + .stream() + .filter(key -> !"linker".equals(childProcessElements.getJSONObject(key).getString("name"))) + .map(key -> childProcessElements.getJSONObject(key).getJSONObject("props")) + .min((o1, o2) -> Double.compare(o1.getDoubleValue("y"), o2.getDoubleValue("y"))).get(); // 当前节点所标识的子流程文件的 画布宽度与高度 减去边距 - double childProcessPageWidth = childProcessPage.getDoubleValue("width") - childProcessPage.getDoubleValue("padding") * 2; - double childProcessPageHeight = childProcessPage.getDoubleValue("height") - childProcessPage.getDoubleValue("padding") * 2; + // double childProcessPageWidth = childProcessPage.getDoubleValue("width") - childProcessPage.getDoubleValue("padding") * 2; + // double childProcessPageHeight = childProcessPage.getDoubleValue("height") - childProcessPage.getDoubleValue("padding") * 2; + double scopeShapeW = childProcessEleMaxX.getDoubleValue("x") + childProcessEleMaxX.getDoubleValue("w") - childProcessEleMixX.getDoubleValue("x") + SubProcessConst.SCOPE_SHAPE_PADDING; + double scopeShapeH = childProcessEleMaxY.getDoubleValue("y") + childProcessEleMaxY.getDoubleValue("h") - childProcessEleMinY.getDoubleValue("y") + SubProcessConst.SCOPE_SHAPE_PADDING; - JSONObject endToEndProcessDefineObj = JSONObject.parseObject(endToEndProcessDefine); - JSONObject elements = endToEndProcessDefineObj.getJSONObject("elements"); + // 计算下范围选择框在子流程所代表的模型文件的坐标 + scopeLimitationShapeBeforePoi = new double[]{childProcessEleMixX.getDoubleValue("x") - SubProcessConst.SCOPE_SHAPE_PADDING / 2, childProcessEleMinY.getDoubleValue("y") - SubProcessConst.SCOPE_SHAPE_PADDING / 2}; + + JSONObject elements = endToEndProcessDefine.getJSONObject("elements"); // 找到当前要展开的子流程节点 JSONObject currentExpandShape = elements.getJSONObject(shapeId); if (currentExpandShape == null) @@ -87,47 +130,196 @@ public class GraphNodeExpandHandle { double x = props.getDoubleValue("x"); double y = props.getDoubleValue("y"); // 当前要展开的子流程节点的坐标赋给范围标注框 + scopeLimitationShape.put("id", shapeId); JSONObject scopeShapeProps = scopeLimitationShape.getJSONObject("props"); scopeShapeProps.put("x", x); scopeShapeProps.put("y", y); - scopeShapeProps.put("w", childProcessPageWidth); - scopeShapeProps.put("h", childProcessPageHeight); + scopeShapeProps.put("w", scopeShapeW); + scopeShapeProps.put("h", scopeShapeH); scopeShapeProps.put("zindex", 1); - return scopeLimitationShape; + this.scopeLimitationShape = scopeLimitationShape; } /** - * 处理子流程中所有节点的坐标 + * 节点展开 + * @param direction 排布方向 + * @throws Exception + * @return 节点展开后的模型存储信息 */ - public void handleRelationModelNodePosition(){ + public String handleNodeExpand(String direction) throws Exception{ +// Thread t1 = new Thread(() -> { +// // 1、总图节点以及连线处理 +// handleEndToEndGraphNodeAndLinker(direction); +// }); +// t1.start(); +// +// Thread t2 = new Thread(() -> { +// // 2、子流程节点内部元素处理 +// handleRelationModelNodePosition(); +// }); +// t2.start(); +// +// t1.join(); +// t2.join(); + + // 1、总图节点以及连线处理 + handleEndToEndGraphNodeAndLinker(direction); + + // 2、子流程节点内部元素处理 + handleRelationModelNodePosition(); + + // 6、保存总图模型信息 以及 子流程模型信息备份 + // BaseModel baseModel = apiManager.getDefinition(repositoryId, 0); + // baseModel.setDefinition(endToEndProcessDefine.toJSONString()); + // apiManager.storeDefinition(baseModel); + // apiManager.storeChildProcessDefine(baseModel, relationFileId, childProcessDefine.toJSONString()); + return endToEndProcessDefine.toJSONString(); + } + + /** + * 处理总图中的节点与连线 + * 1、删除待展开的节点 + * 2、替换为范围选择框 + * 3、节点x > 范围标注框x || 节点y < 范围标注框y 针对符合条件的节点 进行坐标调整 + * 4、根据旧有连线关系构建邻接矩阵 + * 5、获取当前总图中的所有节点的坐标 + * 6、删除旧有连线关系 + * 7、构建新的连线关系 + */ + private void handleEndToEndGraphNodeAndLinker(String direction){ + // 1、删除当前要展开的节点 + removeEndToEndGraphElements(shapeId); + + // 2、添加到总图中 + addEndToEndGraphElements(scopeLimitationShape); + + // 3、总图中符合范围选择框条件的节点 坐标更新 + handleEndToEndGraphNodeExcluedExpandNode(); + + // 4、构建邻接矩阵 + NodeExpandAdjMatrix expandAdjMatrix = buildEndToEndGraphAdjMatrix(); + expandAdjMatrix.buildAdjMatrix(); + // 5、删除节点展开前的连线 + removeEndToEndGraphOldLinker(); + // 6、获取所有节点坐标 + JSONObject elements = endToEndProcessDefine.getJSONObject("elements"); + double[][] vertexPosition = expandAdjMatrix.getVertexPosition(elements); + // 7、构建新的连线 + NodeExpandLinkerRender linkerRender = new NodeExpandLinkerRender(vertexPosition, expandAdjMatrix, scopeLimitationShape); + JSONArray linkers = linkerRender.toAssembleLinker(direction, shapeId); + for (Object o : linkers) { + JSONObject linker = (JSONObject) o; + addEndToEndGraphElements(linker); + } + } + + /** + * 处理待展开节点所代表的流程文件中的节点的坐标 + */ + private void handleRelationModelNodePosition(){ // 范围标注框 坐标 - JSONObject scopeLimitationShape = toAssembleScopeLimitationShape(); JSONObject scopeShapeProps = scopeLimitationShape.getJSONObject("props"); double scopeShapeX = scopeShapeProps.getDoubleValue("x"); double scopeShapeY = scopeShapeProps.getDoubleValue("y"); + double distanceX = scopeShapeX - scopeLimitationShapeBeforePoi[0]; + double distanceY = scopeShapeY - scopeLimitationShapeBeforePoi[1]; + // 根据范围标注框的坐标 调整子流程所有元素的坐标 - JSONObject childProcessDefineObj = JSONObject.parseObject(childProcessDefine); - JSONObject elements = childProcessDefineObj.getJSONObject("elements"); + JSONObject elements = childProcessDefine.getJSONObject("elements"); for (String key : elements.keySet()) { JSONObject ele = elements.getJSONObject(key); - JSONObject props = ele.getJSONObject("props"); - props.put("x", scopeShapeX + props.getDoubleValue("x")); - props.put("y", scopeShapeY + props.getDoubleValue("y")); // 元素分为两类 一类为图形 一类为连线 - if ("linker".equals(ele.getString("name"))){ // 连线的话折点需要额外处理 + if ("linker".equals(ele.getString("name"))){ // 连线 + JSONObject from = ele.getJSONObject("from"); + from.put("x", distanceX + from.getDoubleValue("x")); + from.put("y", distanceY + from.getDoubleValue("y")); + JSONObject to = ele.getJSONObject("to"); + to.put("x", distanceX + to.getDoubleValue("x")); + to.put("y", distanceY + to.getDoubleValue("y")); + // 连线的话折点也需要处理 JSONArray points = ele.getJSONArray("points"); for (Object p : points) { JSONObject point = (JSONObject) p; - point.put("x", point.getDoubleValue("x") + scopeShapeX); - point.put("y", point.getDoubleValue("y") + scopeShapeY); + point.put("x", point.getDoubleValue("x") + distanceX); + point.put("y", point.getDoubleValue("y") + distanceY); } + }else { // 图形 + JSONObject props = ele.getJSONObject("props"); + props.put("x", distanceX + props.getDoubleValue("x")); + props.put("y", distanceY + props.getDoubleValue("y")); + } + + addEndToEndGraphElements(ele); + } + } + + /** + * 处理总图中的节点 + * 节点x > 范围标注框x || 节点y < 范围标注框y + * 针对符合条件的节点 进行坐标调整 + */ + private void handleEndToEndGraphNodeExcluedExpandNode(){ + JSONObject elements = endToEndProcessDefine.getJSONObject("elements"); + // 范围标注框 大小 位置 + JSONObject scopeShapeProps = scopeLimitationShape.getJSONObject("props"); + double scopeShapeX = scopeShapeProps.getDoubleValue("x"); + double scopeShapeY = scopeShapeProps.getDoubleValue("y"); + double scopeShapeW = scopeShapeProps.getDoubleValue("w"); + double scopeShapeH = scopeShapeProps.getDoubleValue("h"); + + for (String key : elements.keySet()) { + JSONObject ele = elements.getJSONObject(key); + if ("linker".equals(ele.getString("name"))) continue; // 连线先不处理 + JSONObject props = ele.getJSONObject("props"); + if (props.getDoubleValue("x") > scopeShapeX) { // 当前元素在待展开节点的右侧 + props.put("x", props.getDoubleValue("x") + scopeShapeW - SubProcessConst.SUB_PROCESS_SHAPE_W); + } + if (props.getDoubleValue("y") > scopeShapeY) { // 当前元素在待展开节点的下侧 + props.put("y", props.getDoubleValue("y") + scopeShapeH - SubProcessConst.SUB_PROCESS_SHAPE_H); + } + + addEndToEndGraphElements(ele); + } + } + + /** + * 构建节点展开前 端到端总图的邻接矩阵 + * 方便后续节点展开或者闭合进行重新连线 + */ + private NodeExpandAdjMatrix buildEndToEndGraphAdjMatrix(){ + + List nodeIdList = new ArrayList<>(); + List linkerList = new ArrayList<>(); + JSONObject endToEndProcessElements = endToEndProcessDefine.getJSONObject("elements"); + for (String key : endToEndProcessElements.keySet()) { + JSONObject ele = endToEndProcessElements.getJSONObject(key); + if ("linker".equals(ele.getString("name"))) { + linkerList.add(ele); + }else { + nodeIdList.add(key); } } + NodeExpandAdjMatrix expandAdjMatrix = new NodeExpandAdjMatrix(nodeIdList, linkerList); + return expandAdjMatrix; + } - // 找到总图中 x坐标 >= 范围标注框x坐标 y坐标 >= 范围标注框y坐标 的图形 - + /** + * 删除总图中节点展开前的连线 + */ + private void removeEndToEndGraphOldLinker(){ + JSONObject elements = endToEndProcessDefine.getJSONObject("elements"); + Set eleKeys = new HashSet<>(); + for (String key : elements.keySet()) { + JSONObject ele = elements.getJSONObject(key); + if ("linker".equals(ele.getString("name"))){ + eleKeys.add(key); + } + } + for (String eleKey : eleKeys) { + removeEndToEndGraphElements(eleKey); + } } /** @@ -136,6 +328,404 @@ public class GraphNodeExpandHandle { */ public void storeChildProcessDefine(){ BaseModel baseModel = apiManager.getDefinition(relationFileId, 0); - apiManager.storeChildProcessDefine(baseModel, relationFileId, childProcessDefine); + apiManager.storeChildProcessDefine(baseModel, relationFileId, childProcessDefine.toJSONString()); + } + + /** + * 总图中添加元素 + * @param ele + */ + private void addEndToEndGraphElements(JSONObject ele){ + JSONObject elements = endToEndProcessDefine.getJSONObject("elements"); + String id = ele.getString("id"); + lock.lock(); + try{ + elements.put(id, ele); + }finally { + lock.unlock(); + } + } + + /** + * 总图中删除元素 + * @param key + */ + private void removeEndToEndGraphElements(String key){ + JSONObject elements = endToEndProcessDefine.getJSONObject("elements"); + lock.lock(); + try{ + elements.remove(key); + }finally { + lock.unlock(); + } + } +} + +/** + * 节点展开的邻接矩阵 + */ +class NodeExpandAdjMatrix{ + + private int[][] adjMatrix; + private List nodeIds; + private List linkerList; + + public NodeExpandAdjMatrix(List nodeIds, List linkerList) { + this.adjMatrix = new int[nodeIds.size()][nodeIds.size()]; + this.nodeIds = nodeIds; + this.linkerList = linkerList; + } + + /** + * 添加一条从顶点 u 到顶点 v 的有向边。 + */ + public void addEdge(int u, int v) { + adjMatrix[u][v] = 1; // 设置邻接矩阵中相应的位置为 1 + } + + /** + * 获取从顶点 u 出发可以到达的所有顶点。 + */ + public List getNeighbors(int u) { + List neighbors = new ArrayList<>(); + for (int i = 0; i < nodeIds.size(); i++) { + if (adjMatrix[u][i] == 1) { + neighbors.add(i); + } + } + return neighbors; + } + + /** + * 判断从顶点 u 是否可以到达顶点 v。 + */ + public boolean hasEdge(int u, int v) { + return adjMatrix[u][v] == 1; + } + + /** + * 获取邻接矩阵 + * @return + */ + public int[][] getAdjMatrix(){ + return adjMatrix; + } + + public List getNodeIds() { + return nodeIds; + } + + public List getLinkerList() { + return linkerList; + } + + /** + * 构建邻接矩阵 + */ + public void buildAdjMatrix(){ + for (JSONObject linker : linkerList) { + JSONObject from = linker.getJSONObject("from"); + JSONObject to = linker.getJSONObject("to"); + int fromIndex = nodeIds.indexOf(from.getString("id")); + int toIndex = nodeIds.indexOf(to.getString("id")); + addEdge(fromIndex, toIndex); + } + } + + // 输出邻接矩阵 + public void printAdjMatrix() { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < nodeIds.size(); i++) { + for (int j = 0; j < nodeIds.size(); j++) { + sb.append(adjMatrix[i][j]).append(" "); + } + sb.append("\n"); + } + System.out.println(sb.toString()); + } + + /** + * 获取总图中节点展开前的所有节点坐标 + * @param elements + * @return + */ + public double[][] getVertexPosition(JSONObject elements){ + double[][] position = new double[nodeIds.size()][2]; + for (int i = 0; i < nodeIds.size(); i++) { + JSONObject shape = elements.getJSONObject(nodeIds.get(i)); + JSONObject props = shape.getJSONObject("props"); + double x = props.getDoubleValue("x"); + double y = props.getDoubleValue("y"); + position[i][0] = x; + position[i][1] = y; + } + return position; + } +} + +/** + * 节点展开后连线重构 + */ +class NodeExpandLinkerRender{ + + private List nodeIds; // 图形节点ID集合 + private double[][] vertexPosition; // 所有节点的坐标 + private NodeExpandAdjMatrix expandAdjMatrix; // 节点矩阵 + private JSONObject scopeLimitationShape; // 范围标注框 + + public NodeExpandLinkerRender(double[][] vertexPosition, NodeExpandAdjMatrix expandAdjMatrix, JSONObject scopeLimitationShape) { + this.nodeIds = expandAdjMatrix.getNodeIds(); + this.vertexPosition = vertexPosition; + this.expandAdjMatrix = expandAdjMatrix; + this.scopeLimitationShape = scopeLimitationShape; + } + + /** + * 根据连线方向 组装连线 + * @param direction 连线方向 + * @param shapeId 当前待展开节点ID + * @return + */ + public JSONArray toAssembleLinker(String direction, String shapeId){ + JSONArray linkers = new JSONArray(); + int index = nodeIds.indexOf(shapeId); + for (int i = 0; i < vertexPosition.length; i++) { + boolean currentExpandNodeIsStart = false; + if (i == index){ + currentExpandNodeIsStart = true; // 当前待展开的节点此处应是范围选择框为连线的出发点 + } + double[] fromPoi = vertexPosition[i]; + List nextNodeIndex = expandAdjMatrix.getNeighbors(i); + if (nextNodeIndex.size() > 0){ // 说明当前节点有连线 + for (Integer nodeIndex : nextNodeIndex) { + boolean currentExpandNodeIsEnd = false; + if (nodeIndex.intValue() == index){ + currentExpandNodeIsEnd = true; // 当前待展开的节点 此处应是范围选择框为连线的终点 + } + double[] toPoi = vertexPosition[nodeIndex]; + double[][] turnPoi = "horizontal".equals(direction) + ? calculationLinkerPointInHorizLayOut(fromPoi, toPoi, currentExpandNodeIsStart, currentExpandNodeIsEnd) + : calculationLinkerPointInVertLayOut(fromPoi, toPoi, currentExpandNodeIsStart, currentExpandNodeIsEnd); + double[] angleArr = calculationLinkerAngle(fromPoi, toPoi, turnPoi[1], turnPoi[turnPoi.length - 2]); + // 构建连线 + JSONObject linkerObj = JSONObject.parseObject(LinkerDefConstant.linker); + linkerObj.put("id", UUIDGener.getObjectId()); + // 折点 + JSONArray points = new JSONArray(); + for (int j = 0; j < turnPoi.length; j++) { + if (j > 0 && j < turnPoi.length - 1){ + JSONObject pointObj = new JSONObject(); + pointObj.put("x", turnPoi[j][0]); + pointObj.put("y", turnPoi[j][1]); + points.add(pointObj); + } + } + linkerObj.put("points", points); + // 起点与终点 + JSONObject fromObj = new JSONObject(); + fromObj.put("x", turnPoi[0][0]); + fromObj.put("y", turnPoi[0][1]); + fromObj.put("angle", angleArr[0]); + fromObj.put("id", nodeIds.get(i)); + linkerObj.put("from", fromObj); + JSONObject toObj = new JSONObject(); + toObj.put("x", turnPoi[turnPoi.length - 1][0]); + toObj.put("y", turnPoi[turnPoi.length - 1][1]); + toObj.put("angle", angleArr[1]); + toObj.put("id", nodeIds.get(nodeIndex.intValue())); + linkerObj.put("to", toObj); + + linkers.add(linkerObj); + } + } + } + return linkers; + } + + private double[][] calculationLinkerPointInVertLayOut(double[] fromPoi, double[] toPoi, boolean currentExpandNodeIsStart, boolean currentExpandNodeIsEnd) { + double fromX = fromPoi[0],fromY = fromPoi[1],toX = toPoi[0],toY = toPoi[1]; + double scopeShapeW = scopeLimitationShape.getDoubleValue("w"), scopeShapeH = scopeLimitationShape.getDoubleValue("h"); + if (fromY == toY){ // 水平 分析可知 水平方向上不会出现 从左到右直连的情况 只有 右边节点右侧锚点出 向上走 左折 连到左侧节点上方锚点 + double[] startPoi = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + double[] turnPoi1 = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + double[] turnPoi2 = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}; + double[] turnPoi3 = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}; + double[] endPoi = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY}; + return new double[][]{startPoi, turnPoi1, turnPoi2, turnPoi3, endPoi}; + }else if (fromX == toX){ // 垂直 分析可知 垂直方向上应该不会有 toY < fromY 的情况 鉴于数据不确定性 先写上 + double[] startPoi = fromY < toY + ? new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H)} + : new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + double[] endPoi = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY}; + return fromY < toY + ? new double[][]{ + startPoi, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) + SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + endPoi} + : new double[][]{ + startPoi, + {fromX + ((currentExpandNodeIsStart || currentExpandNodeIsEnd) ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + ((currentExpandNodeIsStart || currentExpandNodeIsEnd) ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + endPoi}; + }else { // 分布在四个象限内 + if (fromX > toX && fromY > toY){ // 目标节点在第二象限 + return new double[][]{ + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY} + }; + }else if (fromX > toX && fromY < toY){ // 目标节点在第三象限 + return toY - fromY == SubProcessConst.SHAPE_VERT_INTERVAL + ? new double[][] + { + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H)}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) + SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY} + } + : new double[][] + { + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H)}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) + SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) + SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY} + }; + }else if (fromX < toX && fromY < toY){ // 目标节点在第四象限 + return new double[][]{ + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H)}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) + SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY} + }; + }else { + // fromX < toX && fromY > toY 目标节点在第一象限 分析可知 纵向排布的情况下 应该不会出现目标节点在第一象限的情况 + } + } + return new double[2][2]; + } + + private double[][] calculationLinkerPointInHorizLayOut(double[] fromPoi, double[] toPoi, boolean currentExpandNodeIsStart, boolean currentExpandNodeIsEnd) { + double fromX = fromPoi[0],fromY = fromPoi[1],toX = toPoi[0],toY = toPoi[1]; + double scopeShapeW = scopeLimitationShape.getDoubleValue("w"), scopeShapeH = scopeLimitationShape.getDoubleValue("h"); + if (fromY == toY) { // 水平 方向上 存在从左向右直连的情况 但不存在从右向左直连的情况 水平方向上 从左向右 应是 右出 向上 左折 向下 + return fromX < toX + ? new double[][] + { + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {toX - SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY + (currentExpandNodeIsEnd ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {toX, toY + (currentExpandNodeIsEnd ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2} + } + : new double[][] + { + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY} + }; + }else if (fromX == toX) { // 垂直 + // 节点横向分布 连线按照大原则 垂直 不存在 fromY < toY 的情况 也就是不存在 连线从上到下直连的情况 + double[] startPoint = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + double[] endPoint = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY}; + return new double[][]{startPoint, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + endPoint}; + }else { + if (fromX < toX && fromY > toY){ // 目标节点在第一象限 + double[] startPoint = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + double turnPointX = fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2; + if (fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL == toX){ // 相邻节点 存在两个折点 + double[] endPoint = new double[]{toX, toY + (currentExpandNodeIsEnd ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + return new double[][]{startPoint,{turnPointX, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2},{turnPointX, toY + (currentExpandNodeIsEnd ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, endPoint}; + }else { // 不相邻节点 存在三个折点 + double[] endPoint = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY}; + return new double[][]{ + startPoint, + {turnPointX, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {turnPointX, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + endPoint + }; + } + }else if (fromX > toX && fromY > toY) { // 目标节点在第二象限 无论节点是否相邻 都按照三个折点走 + double[] startPoint = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + double[] endPoint = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY}; + return new double[][]{ + startPoint, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + endPoint + }; + }else if (fromX < toX && fromY < toY){ // 目标节点在第四象限 + double[] startPoint = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + if (fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL == toX){ // 相邻节点 存在两个折点 + double turnPointX = fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2; + double[] endPoint = new double[]{toX, toY + (currentExpandNodeIsEnd ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + return new double[][]{ + startPoint, + {turnPointX, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {turnPointX, toY + (currentExpandNodeIsEnd ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + endPoint + }; + }else { // 不相邻节点 存在三个折点 + double[] endPoint = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY}; + return new double[][]{ + startPoint, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + endPoint + }; + } + }else if (fromX > toX && fromY < toY){ // 目标节点在第三象限 横向布局的情况下 应该不会出现目标节点在第三象限的情况 + ConsolePrinter.warn("[端到端功能][节点展开模块]处理连线时目标节点在[横向布局]的情况下出现在了第三象限"); + } + } + return new double[2][2]; + } + + /** + * 计算angle值 + * @param fromPoi 起始节点坐标 + * @param toPoi 结束节点坐标 + * @param firstTurnPoi 连线第一个折点坐标 + * @param lastTurnPoi 连线最后一个折点坐标 + * @return [0]: from的angle [1]: to的angle + */ + private double[] calculationLinkerAngle(double[] fromPoi, double[] toPoi, double[] firstTurnPoi, double[] lastTurnPoi){ + double fromX = fromPoi[0], fromY = fromPoi[1]; + double toX = toPoi[0], toY = toPoi[1]; + double firstTurnX = firstTurnPoi[0], firstTurnY = firstTurnPoi[1]; + double lastTurnX = lastTurnPoi[0], lastTurnY = lastTurnPoi[1]; + + if (fromY == toY){ // 水平 + return fromX < toX ? new double[]{LinkerDefConstant.ANGLE_LEFT, LinkerDefConstant.ANGLE_RIGHT} : new double[]{LinkerDefConstant.ANGLE_RIGHT, LinkerDefConstant.ANGLE_LEFT}; + }else if (fromX == toX){ // 垂直 + return fromY < toY ? new double[]{LinkerDefConstant.ANGLE_UP, LinkerDefConstant.ANGLE_DOWN} : new double[]{LinkerDefConstant.ANGLE_DOWN, LinkerDefConstant.ANGLE_UP}; + }else { + double fromAngle = 0.0; + if (fromY == firstTurnY){ // 水平 + fromAngle = fromX < firstTurnX ? LinkerDefConstant.ANGLE_LEFT : LinkerDefConstant.ANGLE_RIGHT; + }else if (fromX == firstTurnX){ // 垂直 + fromAngle = fromY < firstTurnY ? LinkerDefConstant.ANGLE_UP : LinkerDefConstant.ANGLE_DOWN; + } + double toAngle = 0.0; + if (toY == lastTurnY){ // 水平 + toAngle = toX < lastTurnX ? LinkerDefConstant.ANGLE_LEFT : LinkerDefConstant.ANGLE_RIGHT; + }else if (toX == lastTurnX){ // 垂直 + toAngle = toY < lastTurnY ? LinkerDefConstant.ANGLE_UP : LinkerDefConstant.ANGLE_DOWN; + } + return new double[]{fromAngle, toAngle}; + } } } 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 d98d09ae..d80a478c 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 @@ -303,50 +303,22 @@ public class SubProcessWeb extends ActionWeb { } - public void shapeNodeExpand(String repositoryId, String shapeId){ - - /* - * 以当前节点展开涉及到动作拆分: - * - * 0、判断当前节点是否展开过了,初次展开的话去源文件中找,否则去备份的文件中找 - * - * 1、准备范围限制框元素,该元素坐标与当前被展开的节点坐标一致,宽度与高度与子流程标识的模型画布-边距的宽度、高度一致 - * 同时,子流程标识的模型内的所有元素 以当前被展开的节点坐标为准,下移、右移 被展开节点坐标距离原点的距离 - * - * 2、找到x大于等于、y大于等于当前被展开节点的所有子流程节点,将这些节点下移、右移到范围框之外 - * - * 3、根据与当前节点的连线,找到总图中所有与当前被展开节点的相连的前置节点与后置节点 - * - * 4、将子流程模型备份一份,为后续对展开后的节点做删除等操作 - * - * */ - - // 获取子流程标识的文件存储信息 - List childProcessModelList = DesignerShapeRelationCache.getListByAttrId(repositoryId, shapeId, SubProcessConst.CHILD_PROCESS); - DesignerShapeRelationModel relationModel = childProcessModelList.stream().findFirst().orElse(null); - if (relationModel == null) - throw new AWSException("未找到当前节点所标识的子流程文件信息"); - String relationFileId = relationModel.getRelationFileId(); - CoeDesignerAPIManager apiManager = CoeDesignerAPIManager.getInstance(); - BaseModel childProcessBaseModel = apiManager.getDefinition(relationFileId, 0); - String childProcessDefinition = childProcessBaseModel.getDefinition(); - JSONObject childProcessDefineObj = JSONObject.parseObject(childProcessDefinition); - JSONObject childProcessPage = childProcessDefineObj.getJSONObject("page"); - // 当前节点所标识的子流程文件的 画布宽度与高度 减去边距 - double childProcessPageWidth = childProcessPage.getDoubleValue("width") - childProcessPage.getDoubleValue("padding") * 2; - double childProcessPageHeight = childProcessPage.getDoubleValue("height") - childProcessPage.getDoubleValue("padding") * 2; - - // 获取总图的存储数据 - BaseModel baseModel = CoeDesignerAPIManager.getInstance().getDefinition(repositoryId, 0); - String definition = baseModel.getDefinition(); - JSONObject defineJsonObj = JSONObject.parseObject(definition); - JSONObject elements = defineJsonObj.getJSONObject("elements"); - JSONObject shapeObj = elements.getJSONObject(shapeId); - JSONObject shapeProps = shapeObj.getJSONObject("props"); - // 当前节点的坐标 - double x = shapeProps.getDoubleValue("x"); - double y = shapeProps.getDoubleValue("y"); - + /** + * 节点展开操作 + * @param repositoryId 当前总图ID + * @param shapeId 待展开的子流程节点ID + * @param direction 布局方向 + */ + public String shapeNodeExpand(String repositoryId, String shapeId, String direction){ + try { + GraphNodeExpandHandle nodeExpandHandle = new GraphNodeExpandHandle(repositoryId, shapeId); + String define = nodeExpandHandle.handleNodeExpand(direction); + ResponseObject ro = ResponseObject.newOkResponse("展开成功"); + ro.setData(define); + return ro.toString(); + } catch (Exception e) { + return ResponseObject.newErrResponse(e.getMessage()).toString(); + } } } diff --git a/com.actionsoft.apps.coe.pal/web/com.actionsoft.apps.coe.pal/lib/designer/scripts/diagraming/designer.method.subprocess.js b/com.actionsoft.apps.coe.pal/web/com.actionsoft.apps.coe.pal/lib/designer/scripts/diagraming/designer.method.subprocess.js index 1b092267..c8878d59 100644 --- a/com.actionsoft.apps.coe.pal/web/com.actionsoft.apps.coe.pal/lib/designer/scripts/diagraming/designer.method.subprocess.js +++ b/com.actionsoft.apps.coe.pal/web/com.actionsoft.apps.coe.pal/lib/designer/scripts/diagraming/designer.method.subprocess.js @@ -7,6 +7,7 @@ $(function(){ const subProcess = new SubProcess(Model, ruuid, sid); subProcess.shapeIconRender(); + // 连线框 鼠标指针样式设置 防止因为连线z-index层级较高 会导致节点展开图标点击不到 $('.shape_box.linker_box').css({ 'pointer-events': 'none' }); @@ -43,7 +44,7 @@ class SubProcess { // 节点展开事件 shapeExpand(event){ let param = event.data; - alert('节点展开事件 ' + param.Model.define.elements[event.data.shapeId].text); + // alert('节点展开事件 ' + param.Model.define.elements[event.data.shapeId].text); // 1、同时只能支持一个子流程节点展开 let elements = param.Model.define.elements; for (let key in elements) { @@ -65,7 +66,9 @@ class SubProcess { shapeId: param.shapeId }, ok: function(r){ - console.log(r); + console.log(JSON.stringify(r.data)); + definition = JSON.stringify(r.data); + Designer.open(definition); }, err: function(r){ $.simpleAlert(r.msg);