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 3b6cf982..74f9ac77 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/constant/SubProcessConst.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/constant/SubProcessConst.java index b2d0510c..d3c81f2d 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 @@ -38,4 +38,23 @@ public interface SubProcessConst { // 范围选择框内边距 double SCOPE_SHAPE_PADDING = 300; + + // 子流程节点上扩展的属性名 用来存储当前子流程节点标识的模型文件信息 + String EXTEND_ATTR = "extendAttr"; + + // 节点展开时 范围框上扩镇的属性名 用来存储自身内部元素 + String INNER_ELEMENTS = "innerElements"; + + // 流程接口图形的唯一标识 + String SHAPE_NAME_PROCEDURE = "procedure"; + + // 范围框 内部前置或者后置流程接口元素 的特殊属性名 + String SHAPE_PROCEDURE_ATTR_MARK = "attrMark"; + + // 水平布局 + String DIRECTION_H = "horizontal"; + + // 垂直布局 + String DIRECTION_V = "vertically"; + } diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/ExpandOrCloseShapeLinkerConvertHandle.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/ExpandOrCloseShapeLinkerConvertHandle.java new file mode 100644 index 00000000..09df5bb8 --- /dev/null +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/ExpandOrCloseShapeLinkerConvertHandle.java @@ -0,0 +1,229 @@ +package com.actionsoft.apps.coe.method.process.subprocess.graph; + +import com.actionsoft.apps.coe.method.process.subprocess.constant.ElementType; +import com.actionsoft.apps.coe.method.process.subprocess.constant.SubProcessConst; +import com.actionsoft.apps.coe.method.process.subprocess.graph.component.AbstractDefinitionHandle; +import com.actionsoft.apps.coe.method.process.subprocess.graph.util.LinkerPointCalculationHandle; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author oYang + * @Description 端到端总图 连线后置处理 + * 针对展开或者闭合节点上的连线进行后置处理 + * 此时总图的状态是节点展开或者闭合已经处理 + * 展开的话连线此时是连在了范围框上 此处处理的是将范围框上的连线连到内部节点上(如果此节点存在的话) + * 闭合的话连线此时是连在了范围框的内部节点上(也有可能是在范围框上 范围框上不处理) 此处处理的是将内部节点的连线连到闭合后的子流程节点上 + * @createTime 2023年06月28日 13:47:00 + */ +public class ExpandOrCloseShapeLinkerConvertHandle { + + private AbstractDefinitionHandle definitionHandle; // 总图的存储模型define处理器 + private LinkerPointCalculationHandle linkerPointCalculationHandle; // 连线上起始点以及折点处理器 + private String direction; // 当前布局方式 + + public ExpandOrCloseShapeLinkerConvertHandle(AbstractDefinitionHandle definitionHandle) { + this.definitionHandle = definitionHandle; + + this.linkerPointCalculationHandle = new LinkerPointCalculationHandle(definitionHandle); + + JSONObject processProperties = definitionHandle.getProcessProperties(); + this.direction = processProperties.getString("direction"); + } + + + public void scopeShapeLinkerHandle(){ + + Set scopeShapeIds = definitionHandle.getElements().keySet().stream().filter(key -> definitionHandle.getElementTypeByKey(key).name().equals(ElementType.SCOPE_NODE.name())).collect(Collectors.toSet()); + if (scopeShapeIds.size() > 0){ + for (String shapeId : scopeShapeIds) { + // 1、准备当前展开的子流程节点的前置连线与后置连线 + JSONArray linkers = definitionHandle.getLinkers(); + // 2、准备当前展开的子流程节点的内部元素中的流程接口 其中代表前置/后置流程文件属性的流程接口上会有 attrMark 属性 + JSONObject scopeShape = definitionHandle.getShapeByKey(shapeId); + JSONArray innerEleKeyArr = scopeShape.getJSONArray(SubProcessConst.INNER_ELEMENTS); + // 获取当前范围框 内部前置或者后置流程接口元素集合 + JSONArray procedureArr = innerEleKeyArr + .stream() + .filter(innerEleKey -> definitionHandle.getElementTypeByKey(innerEleKey.toString()).name().equals(ElementType.INNER_NODE.name())) + .filter(innerEleKey -> SubProcessConst.SHAPE_NAME_PROCEDURE.equals(definitionHandle.getShapeByKey(innerEleKey.toString()).getString("name"))) + .filter(innerEleKey -> definitionHandle.getShapeByKey(innerEleKey.toString()).containsKey(SubProcessConst.SHAPE_PROCEDURE_ATTR_MARK)) + .map(innerEleKey -> definitionHandle.getShapeByKey(innerEleKey.toString())) + .collect(Collectors.toCollection(JSONArray::new)); + + // 3、找到当前节点的所有前置连线 + JSONArray leadNodeLinkers = linkers + .stream() + .filter(l -> shapeId.equals(((JSONObject) l).getJSONObject("to").getString("id"))) + .collect(Collectors.toCollection(JSONArray::new)); + // 3.1、判断当前展开的子流程节点的内部元素中的流程接口上的文件标识 是否存在 前置子流程节点所代表的子流程文件 如果有将当前前置连线转为 与内部流程接口相连 + for (Object o1 : leadNodeLinkers) { + JSONObject leadNodeLinker = (JSONObject) o1; + String leadNodeId = leadNodeLinker.getJSONObject("from").getString("id"); + ElementType leadNodeType = definitionHandle.getElementTypeByKey(leadNodeId); + JSONObject leadNode = definitionHandle.getShapeByKey(leadNodeId); + if (procedureArr.size() > 0){ // 范围框内部存在前置或者后置流程接口 + // 判断当前前置节点的类型 如果未展开 继续判断内部节点是否与其有关联 如果有重新渲染连线 没有不处理 + if (leadNodeType.name().equals(ElementType.OUTER_NODE.name()) || leadNodeType.name().equals(ElementType.SCOPE_NODE.name())){ + // 获取前置节点的扩展属性 + JSONObject extendAttr = leadNode.getJSONObject(SubProcessConst.EXTEND_ATTR); + // 当前前置节点 所代表的子流程文件ID + String leadNodeRelationId = extendAttr.getString("id"); + // 1、未展开 子流程节点 + // 2、此时前置节点为范围框 同时说明之前范围框内部没有节点与当前节点有关联关系 + // 判断当前节点内部是否有流程接口 与范围框有关联关系 因为范围框本身也是一个文件 + for (Object o2 : procedureArr) { + JSONObject procedure = (JSONObject) o2; + JSONObject attrMark = procedure.getJSONObject(SubProcessConst.SHAPE_PROCEDURE_ATTR_MARK); + String procedureNodeRelationId = attrMark.getString("id"); + if (leadNodeRelationId.equals(procedureNodeRelationId)){ // 前置节点与内部流程接口有关联关系 + // 删除当前 前置节点与范围框上的连线 + definitionHandle.removeShape(leadNodeLinker.getString("id")); + // 重新渲染当前 前置节点与范围框的连线 + String procedureId = procedure.getString("id"); + double[] fromBounding = new double[]{definitionHandle.getShapeX(leadNodeId), definitionHandle.getShapeY(leadNodeId), definitionHandle.getShapeW(leadNodeId), definitionHandle.getShapeH(leadNodeId)}; + double[] toBounding = new double[]{definitionHandle.getShapeX(procedureId), definitionHandle.getShapeY(procedureId), definitionHandle.getShapeW(procedureId), definitionHandle.getShapeH(procedureId)}; + JSONObject linker = linkerPointCalculationHandle.toAssembleLinker(direction, leadNodeId, procedureId, fromBounding, toBounding); + // 连线属于内外交叉连线 加个特殊标识属性 + linker.put("elementType", ElementType.CROSS_LINKER.name()); + definitionHandle.addEle(linker.getString("id"), linker); + break; + } + } + }else { // 已展开 内部节点 + // 此时前置节点为某一个范围框的内部节点 找到当前内部节点归属哪个范围框 判断当前范围框是否与当前展开的内部流程接口有关联 + String scopeShapeId = leadNode.getString("scopeShapeId"); + for (Object o2 : procedureArr) { + JSONObject procedure = (JSONObject) o2; + JSONObject attrMark = procedure.getJSONObject(SubProcessConst.SHAPE_PROCEDURE_ATTR_MARK); + String procedureNodeRelationId = attrMark.getString("id"); + if (procedureNodeRelationId.equals(scopeShapeId)){ // 前置内部节点所在的范围框 与 内部流程接口有关联关系 + String procedureId = procedure.getString("id"); + double[] fromBounding = new double[]{definitionHandle.getShapeX(scopeShapeId), definitionHandle.getShapeY(scopeShapeId), definitionHandle.getShapeW(scopeShapeId), definitionHandle.getShapeH(scopeShapeId)}; + double[] toBounding = new double[]{definitionHandle.getShapeX(procedureId), definitionHandle.getShapeY(procedureId), definitionHandle.getShapeW(procedureId), definitionHandle.getShapeH(procedureId)}; + JSONObject linker = linkerPointCalculationHandle.toAssembleLinker(direction, scopeShapeId, procedureId, fromBounding, toBounding); + // 连线属于内外交叉连线 加个特殊标识属性 + linker.put("elementType", ElementType.CROSS_LINKER.name()); + definitionHandle.addEle(linker.getString("id"), linker); + break; + } + } + } + } + } + // 4、根据后置连线找到后置子流程节点 + JSONArray rearNodeLinkers = linkers + .stream() + .filter(l -> shapeId.equals(((JSONObject) l).getJSONObject("from").getString("id"))) + .collect(Collectors.toCollection(JSONArray::new)); + // 4.1、同3.1 + for (Object o1 : rearNodeLinkers) { + JSONObject rearNodeLinker = (JSONObject) o1; + String rearNodeId = rearNodeLinker.getJSONObject("to").getString("id"); + ElementType rearNodeType = definitionHandle.getElementTypeByKey(rearNodeId); + JSONObject rearNode = definitionHandle.getShapeByKey(rearNodeId); + if (procedureArr.size() > 0){ // 范围框内部存在前置或者后置流程接口 + if (rearNodeType.name().equals(ElementType.OUTER_NODE.name()) || rearNodeType.name().equals(ElementType.SCOPE_NODE.name())){ + JSONObject extendAttr = rearNode.getJSONObject(SubProcessConst.EXTEND_ATTR); + // 当前后置节点 所代表的子流程文件ID + String rearNodeRelationId = extendAttr.getString("id"); + for (Object o2 : procedureArr) { + JSONObject procedure = (JSONObject) o2; + JSONObject attrMark = procedure.getJSONObject(SubProcessConst.SHAPE_PROCEDURE_ATTR_MARK); + String procedureNodeRelationId = attrMark.getString("id"); + if (rearNodeRelationId.equals(procedureNodeRelationId)){ // 后置节点与内部流程接口有关联关系 + definitionHandle.removeShape(rearNodeLinker.getString("id")); + // 重新渲染当前 前置节点与范围框的连线 + String procedureId = procedure.getString("id"); + double[] fromBounding = new double[]{definitionHandle.getShapeX(procedureId), definitionHandle.getShapeY(procedureId), definitionHandle.getShapeW(procedureId), definitionHandle.getShapeH(procedureId)}; + double[] toBounding = new double[]{definitionHandle.getShapeX(rearNodeId), definitionHandle.getShapeY(rearNodeId), definitionHandle.getShapeW(rearNodeId), definitionHandle.getShapeH(rearNodeId)}; + JSONObject linker = linkerPointCalculationHandle.toAssembleLinker(direction, procedureId, rearNodeId, fromBounding, toBounding); + // 连线属于内外交叉连线 加个特殊标识属性 + linker.put("elementType", ElementType.CROSS_LINKER.name()); + definitionHandle.addEle(linker.getString("id"), linker); + break; + } + } + }else { + // 此时后置节点为某一个范围框的内部节点 找到当前内部节点归属哪个范围框 判断当前范围框是否与当前展开的内部流程接口有关联 + String scopeShapeId = rearNode.getString("scopeShapeId"); + for (Object o2 : procedureArr) { + JSONObject procedure = (JSONObject) o2; + JSONObject attrMark = procedure.getJSONObject(SubProcessConst.SHAPE_PROCEDURE_ATTR_MARK); + String procedureNodeRelationId = attrMark.getString("id"); + if (procedureNodeRelationId.equals(scopeShapeId)){ // 前置内部节点所在的范围框 与 内部流程接口有关联关系 + String procedureId = procedure.getString("id"); + double[] fromBounding = new double[]{definitionHandle.getShapeX(procedureId), definitionHandle.getShapeY(procedureId), definitionHandle.getShapeW(procedureId), definitionHandle.getShapeH(procedureId)}; + double[] toBounding = new double[]{definitionHandle.getShapeX(scopeShapeId), definitionHandle.getShapeY(scopeShapeId), definitionHandle.getShapeW(scopeShapeId), definitionHandle.getShapeH(scopeShapeId)}; + JSONObject linker = linkerPointCalculationHandle.toAssembleLinker(direction, procedureId, scopeShapeId, fromBounding, toBounding); + // 连线属于内外交叉连线 加个特殊标识属性 + linker.put("elementType", ElementType.CROSS_LINKER.name()); + definitionHandle.addEle(linker.getString("id"), linker); + break; + } + } + } + } + } + } + } + } + + /** + * 针对闭合的节点相关连线 后置处理 + */ + public void closeShapeLinkerConvertHandle(){ +// JSONObject subProcessNode = definitionHandle.getShapeByKey(shapeId); +// JSONObject extendAttr = subProcessNode.getJSONObject(SubProcessConst.EXTEND_ATTR); +// JSONArray linkers = definitionHandle.getLinkers(); +// // 当前节点的前置节点 +// JSONArray leadNodeArr = extendAttr.getJSONArray("leadNodeArr"); +// if (leadNodeArr.size() > 0){ +// for (Object o : leadNodeArr) { +// String leadNodeId = (String) o; +// Set linkerIds = linkers.stream() +// .filter(l -> ((JSONObject) l).getJSONObject("from").getString("id").equals(leadNodeId) && ((JSONObject) l).getJSONObject("to").getString("id").equals(shapeId)) +// .map(l -> ((JSONObject) l).getString("id")) +// .collect(Collectors.toSet()); +// if (linkerIds.size() > 0){ +// for (String linkerId : linkerIds) { +// definitionHandle.removeShape(linkerId); +// } +// } +// // 生成连线 +// double[] fromBounding = new double[]{definitionHandle.getShapeX(leadNodeId), definitionHandle.getShapeY(leadNodeId), definitionHandle.getShapeW(leadNodeId), definitionHandle.getShapeH(leadNodeId)}; +// double[] toBounding = new double[]{definitionHandle.getShapeX(shapeId), definitionHandle.getShapeY(shapeId), definitionHandle.getShapeW(shapeId), definitionHandle.getShapeH(shapeId)}; +// JSONObject linker = linkerPointCalculationHandle.toAssembleLinker(direction, leadNodeId, shapeId, fromBounding, toBounding); +// +// linker.put("elementType", ElementType.OUTER_LINKER.name()); +// definitionHandle.addEle(linker.getString("id"), linker); +// } +// } +// // 当前节点的后置节点 +// JSONArray rearNodeArr = extendAttr.getJSONArray("rearNodeArr"); +// if (rearNodeArr.size() > 0){ +// for (Object o : rearNodeArr) { +// String rearNodeId = (String) o; +// Set linkerIds = linkers.stream() +// .filter(l -> ((JSONObject) l).getJSONObject("from").getString("id").equals(shapeId) && ((JSONObject) l).getJSONObject("to").getString("id").equals(rearNodeId)) +// .map(l -> ((JSONObject) l).getString("id")) +// .collect(Collectors.toSet()); +// if (linkerIds.size() > 0){ +// for (String linkerId : linkerIds) { +// definitionHandle.removeShape(linkerId); +// } +// } +// // 生成连线 +// double[] fromBounding = new double[]{definitionHandle.getShapeX(shapeId), definitionHandle.getShapeY(shapeId), definitionHandle.getShapeW(shapeId), definitionHandle.getShapeH(shapeId)}; +// double[] toBounding = new double[]{definitionHandle.getShapeX(rearNodeId), definitionHandle.getShapeY(rearNodeId), definitionHandle.getShapeW(rearNodeId), definitionHandle.getShapeH(rearNodeId)}; +// JSONObject linker = linkerPointCalculationHandle.toAssembleLinker(direction, shapeId, rearNodeId, fromBounding, toBounding); +// +// linker.put("elementType", ElementType.OUTER_LINKER.name()); +// definitionHandle.addEle(linker.getString("id"), linker); +// } +// } + } +} diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphLayout.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphLayout.java index 7dc87313..feb351a8 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphLayout.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphLayout.java @@ -130,10 +130,10 @@ public class GraphLayout { 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]); - } + // 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; } @@ -205,10 +205,10 @@ public class GraphLayout { 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]); - } + // 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; } diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphNodeCloseHandle.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphNodeCloseHandle.java index 60c07541..ef3e9559 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphNodeCloseHandle.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphNodeCloseHandle.java @@ -7,23 +7,19 @@ import com.actionsoft.apps.coe.method.process.subprocess.graph.component.Abstrac import com.actionsoft.apps.coe.method.process.subprocess.graph.component.AbstractDefinitionHandle; import com.actionsoft.apps.coe.method.process.subprocess.graph.util.DefinitionThreadUnSafe; import com.actionsoft.apps.coe.method.process.subprocess.graph.util.SubProcessNodeDefineUtil; -import com.actionsoft.apps.coe.method.process.subprocess.mode.ScopeShapeMonitor; +import com.actionsoft.apps.coe.method.process.subprocess.graph.util.ScopeShapeMonitor; import com.actionsoft.apps.coe.method.process.subprocess.observers.node.NodeSubject; 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.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.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.*; -import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; /** * 图节点关闭处理 @@ -40,6 +36,8 @@ public class GraphNodeCloseHandle { private ScopeShapeMonitor scopeShapeMonitor; // 范围选择框及其内部元素监视器 private NodeSubject nodeSubject; // 当前操作的节点主题类 + private ExpandOrCloseShapeLinkerConvertHandle linkerConvertHandle; // 范围框内元素与外部元素连线处理器 + public GraphNodeCloseHandle(String repositoryId, String shapeId, String endToEndProcessDefineStr) throws AWSException{ this.repositoryId = repositoryId; this.shapeId = shapeId; @@ -62,6 +60,8 @@ public class GraphNodeCloseHandle { nodeSubject = new NodeSubject(definitionHandle, scopeShapeMonitor, shapeId, "close"); nodeSubject.buildObservers(); + linkerConvertHandle = new ExpandOrCloseShapeLinkerConvertHandle(definitionHandle); + } catch (AWSException e) { throw new AWSException(e); } @@ -74,15 +74,19 @@ public class GraphNodeCloseHandle { */ public String handleNodeClose() throws AWSException { + // 删除现有连线 + removeEndToEndGraphOldLinker(); - // 1、处理范围选择框及其内部节点 + // 处理范围选择框及其内部节点 removeScopeShapeAndInRangeEle(); - // 2、处理总图中的节点与连线 + // 处理总图中的节点与连线 handleEndToEndGraphNodeAndLinker(); scopeShapeMonitor.updateScopeShapeInnerEle(); + linkerConvertHandle.scopeShapeLinkerHandle(); + return definitionHandle.getDefine().toJSONString(); } @@ -91,22 +95,27 @@ public class GraphNodeCloseHandle { JSONObject subProcessNode = buildSubProcessNode(shapeId, scopeLimitationShape); definitionHandle.addEle(shapeId, subProcessNode); + // 范围框闭合 将扩展属性复制到新的子流程节点上 + subProcessNode.put(SubProcessConst.EXTEND_ATTR, scopeLimitationShape.getJSONObject(SubProcessConst.EXTEND_ATTR)); + // 通知其它节点位置更新 nodeSubject.setScopeW(subProcessNode.getJSONObject("props").getDoubleValue("w")); nodeSubject.setScopeH(subProcessNode.getJSONObject("props").getDoubleValue("h")); // 2、根据现有连线关系创建邻接矩阵 - NodeCloseAdjMatrix closeAdjMatrix = buildEndToEndGraphAdjMatrix(); + JSONObject elements = definitionHandle.getElements(); + List nodeList = elements.keySet().stream() + .filter(key -> definitionHandle.getElementTypeByKey(key).name().equals(ElementType.OUTER_NODE.name()) || definitionHandle.getElementTypeByKey(key).name().equals(ElementType.SCOPE_NODE.name())) + .map(key -> definitionHandle.getShapeByKey(key)) + .collect(Collectors.toList()); + NodeCloseAdjMatrix closeAdjMatrix = new NodeCloseAdjMatrix(nodeList); closeAdjMatrix.buildAdjMatrix(); - JSONObject elements = definitionHandle.getElements(); // 更新因节点展开后 坐标发生变化的节点坐标 String direction = definitionHandle.getProcessProperties().getString("direction"); - // graphPartNodePoiRenderAgain(elements, direction, subProcessNode); // 3、收集现有元素坐标 double[][] vertexBounding = closeAdjMatrix.getVertexBounding(elements); - // 4、删除现有连线 - removeEndToEndGraphOldLinker(); + // 5、构建新的连线 NodeCloseLinkerRender linkerRender = new NodeCloseLinkerRender(vertexBounding, closeAdjMatrix, definitionHandle); JSONArray linkers = linkerRender.toAssembleLinker(direction); @@ -126,76 +135,6 @@ public class GraphNodeCloseHandle { page.put("height", h + 300); } - /** - * 节点闭合 部分节点坐标再次更新 - */ - private void graphPartNodePoiRenderAgain(JSONObject elements, String direction, JSONObject subProcessNode){ - // 闭合节点的位置 及大小 - JSONObject props = subProcessNode.getJSONObject("props"); - double x = props.getDoubleValue("x"); - double y = props.getDoubleValue("y"); - - // 当前关闭的节点范围标识框的位置与大小 - double[] scope = SubProcessNodeDefineUtil.calculateSubProcessNodeExpandScope(subProcessNodeDefineHandle); - - // double[] scopeShapeBounding = new double[]{x, y, scope[2], scope[3]}; - - // boolean yIsMove = checkShapeYIsMove(elements, scopeShapeBounding); - - for (String key : elements.keySet()) { - JSONObject ele = elements.getJSONObject(key); - if ("linker".equals(ele.getString("name"))) continue; - if (ele.getString("id").equals(subProcessNode.getString("id"))) continue; - if (scopeShapeMonitor.checkShapeIsScopeInRange(key)) continue; - JSONObject eleProps = ele.getJSONObject("props"); - if ("vertically".equals(direction)){ // 垂直布局 - if (x + scope[2] < eleProps.getDoubleValue("x")) { - double xMoveDistance = -scope[2] + SubProcessConst.SUB_PROCESS_SHAPE_W; - eleProps.put("x", eleProps.getDoubleValue("x") - scope[2] + SubProcessConst.SUB_PROCESS_SHAPE_W); - if (scopeShapeMonitor.checkShapeIsScopeShape(key)){ - scopeShapeMonitor.updateMonitorXInfo(key, true, xMoveDistance); - } - } - if (y +scope[3] < eleProps.getDoubleValue("y")){ - double yMoveDistance = -scope[3] + SubProcessConst.SUB_PROCESS_SHAPE_H; - eleProps.put("y", eleProps.getDoubleValue("y") - scope[3] + SubProcessConst.SUB_PROCESS_SHAPE_H); - if (scopeShapeMonitor.checkShapeIsScopeShape(key)){ - scopeShapeMonitor.updateMonitorYInfo(key, true, yMoveDistance); - } - }else if (y < eleProps.getDoubleValue("y") && eleProps.getDoubleValue("y") < y + scope[3]){ - eleProps.put("y", y); - double yMoveDistance = y - eleProps.getDoubleValue("y"); - if (scopeShapeMonitor.checkShapeIsScopeShape(key)){ - scopeShapeMonitor.updateMonitorYInfo(key, true, yMoveDistance); - } - } - }else { // 横向布局 - if (x + scope[2] < eleProps.getDoubleValue("x")){ // 节点在范围框右侧的节点 - double xMoveDistance = -scope[2] + SubProcessConst.SUB_PROCESS_SHAPE_W; - eleProps.put("x", eleProps.getDoubleValue("x") - scope[2] + SubProcessConst.SUB_PROCESS_SHAPE_W); - if (scopeShapeMonitor.checkShapeIsScopeShape(key)){ - scopeShapeMonitor.updateMonitorXInfo(key, true, xMoveDistance); - } - }else if (x < eleProps.getDoubleValue("x") - && eleProps.getDoubleValue("x") < x + scope[2] - && y + scope[3] < eleProps.getDoubleValue("y")){ - double xMoveDistance = x - eleProps.getDoubleValue("x"); - eleProps.put("x", x); - if (scopeShapeMonitor.checkShapeIsScopeShape(key)){ - scopeShapeMonitor.updateMonitorXInfo(key, true, xMoveDistance); - } - } - if (y + scope[3] < eleProps.getDoubleValue("y")){ // 节点在范围框下方的节点 - double yMoveDistance = -scope[3] + SubProcessConst.SUB_PROCESS_SHAPE_H; - eleProps.put("y", eleProps.getDoubleValue("y") - scope[3] + SubProcessConst.SUB_PROCESS_SHAPE_H); - if (scopeShapeMonitor.checkShapeIsScopeShape(key)){ - scopeShapeMonitor.updateMonitorYInfo(key, true, yMoveDistance); - } - } - } - } - } - /** * 在节点闭合的时候 检查当前闭合的节点的下方节点是否需要上下移动 * @param elements @@ -251,41 +190,15 @@ public class GraphNodeCloseHandle { */ private void removeEndToEndGraphOldLinker(){ JSONObject elements = definitionHandle.getElements(); - Set eleKeys = new HashSet<>(); - for (String key : elements.keySet()) { - JSONObject ele = elements.getJSONObject(key); - if ("linker".equals(ele.getString("name")) && !scopeShapeMonitor.checkShapeIsScopeInRange(key)){ - eleKeys.add(key); - } - } - for (String eleKey : eleKeys) { + Set linkerIds = elements.keySet() + .stream() + .filter(key -> definitionHandle.getElementTypeByKey(key).equals(ElementType.OUTER_LINKER) || definitionHandle.getElementTypeByKey(key).equals(ElementType.CROSS_LINKER)) + .collect(Collectors.toSet()); + for (String eleKey : linkerIds) { definitionHandle.removeShape(eleKey); } } - - /** - * 构建节点展开前 端到端总图的邻接矩阵 - * 方便后续节点展开或者闭合进行重新连线 - */ - private NodeCloseAdjMatrix buildEndToEndGraphAdjMatrix(){ - - List nodeIdList = new ArrayList<>(); - List linkerList = new ArrayList<>(); - JSONObject endToEndProcessElements = definitionHandle.getElements(); - for (String key : endToEndProcessElements.keySet()) { - if (scopeShapeMonitor.checkShapeIsScopeInRange(key)) continue; - JSONObject ele = endToEndProcessElements.getJSONObject(key); - if ("linker".equals(ele.getString("name"))) { - linkerList.add(ele); - }else { - nodeIdList.add(key); - } - } - NodeCloseAdjMatrix closeAdjMatrix = new NodeCloseAdjMatrix(nodeIdList, linkerList); - return closeAdjMatrix; - } - /** * 构建子流程节点 * @param shapeId @@ -312,7 +225,8 @@ public class GraphNodeCloseHandle { */ private void removeScopeShapeAndInRangeEle(){ - Set keys = SubProcessNodeDefineUtil.getInScopeLimitationRangeEles(shapeId, definitionHandle, subProcessNodeDefineHandle); + JSONArray innerEleKeySet = scopeLimitationShape.getJSONArray(SubProcessConst.INNER_ELEMENTS); + Set keys = innerEleKeySet.stream().map(o -> o.toString()).collect(Collectors.toSet()); for (String key : keys) { definitionHandle.removeShape(key); } @@ -329,32 +243,42 @@ public class GraphNodeCloseHandle { class NodeCloseAdjMatrix extends AbstractAdjMatrix { private List nodeIds; - private List linkerList; + private List nodeList; // 当前画布中所有子流程节点以及范围框节点 不包含内部节点与连线 - public NodeCloseAdjMatrix(List nodeIds, List linkerList) { - super(nodeIds.size()); - this.nodeIds = nodeIds; - this.linkerList = linkerList; + public NodeCloseAdjMatrix(List nodeList) { + super(nodeList.size()); + this.nodeList = nodeList; + + this.nodeIds = nodeList.stream().map(o -> o.getString("id")).collect(Collectors.toList()); } 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); + for (JSONObject node : nodeList) { + String currentNodeId = node.getString("id"); + JSONObject extendAttr = node.getJSONObject(SubProcessConst.EXTEND_ATTR); + // 当前节点的前置节点 + JSONArray leadNodeArr = extendAttr.getJSONArray("leadNodeArr"); + if (leadNodeArr.size() > 0){ + for (Object o : leadNodeArr) { + String leadNodeId = (String) o; + addEdge(nodeIds.indexOf(leadNodeId), nodeIds.indexOf(currentNodeId)); + } + } + // 当前节点的后置节点 + JSONArray rearNodeArr = extendAttr.getJSONArray("rearNodeArr"); + if (rearNodeArr.size() > 0){ + for (Object o : rearNodeArr) { + String rearNodeId = (String) o; + addEdge(nodeIds.indexOf(currentNodeId), nodeIds.indexOf(rearNodeId)); + } + } } } 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 7f04417a..10ec4f51 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 @@ -7,25 +7,20 @@ import com.actionsoft.apps.coe.method.process.subprocess.graph.component.Abstrac import com.actionsoft.apps.coe.method.process.subprocess.graph.component.AbstractDefinitionHandle; import com.actionsoft.apps.coe.method.process.subprocess.graph.util.DefinitionThreadUnSafe; import com.actionsoft.apps.coe.method.process.subprocess.graph.util.SubProcessNodeDefineUtil; -import com.actionsoft.apps.coe.method.process.subprocess.mode.Node; -import com.actionsoft.apps.coe.method.process.subprocess.mode.ScopeShapeMonitor; +import com.actionsoft.apps.coe.method.process.subprocess.graph.util.ScopeShapeMonitor; import com.actionsoft.apps.coe.method.process.subprocess.observers.node.NodeSubject; 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.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.*; -import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; /** * 图节点展开处理 @@ -42,6 +37,8 @@ public class GraphNodeExpandHandle { private ScopeShapeMonitor scopeShapeMonitor; // 范围选择框及其内部元素监视器 private NodeSubject nodeSubject; // 当前操作的节点主题类 + private ExpandOrCloseShapeLinkerConvertHandle linkerConvertHandle; // 范围框内元素与外部元素连线处理器 + private double[] scopeLimitationShapeBeforePoi; // 范围选择框在子流程文件中的坐标 public GraphNodeExpandHandle(String repositoryId, String shapeId, String endToEndProcessDefineStr) throws AWSException{ @@ -58,12 +55,13 @@ public class GraphNodeExpandHandle { subProcessNodeDefineHandle = new DefinitionThreadUnSafe(SubProcessNodeDefineUtil.readSubProcessNodeDefine(repositoryId, shapeId)); scopeShapeMonitor = new ScopeShapeMonitor(definitionHandle); - // 1、构建范围框监视器模型 并关联内部元素 scopeShapeMonitor.buildScopeShapeMonitors(); nodeSubject = new NodeSubject(definitionHandle, scopeShapeMonitor, shapeId, "expand"); nodeSubject.buildObservers(); + linkerConvertHandle = new ExpandOrCloseShapeLinkerConvertHandle(definitionHandle); + toAssembleScopeLimitationShape(); } catch (Exception e) { throw new AWSException(e); @@ -113,6 +111,9 @@ public class GraphNodeExpandHandle { minRange.put("y2", y + scope[3]); scopeLimitationShape.put("minRange", minRange); + // 扩展属性 extendAttr 存储的是关联的文件信息 + scopeLimitationShape.put(SubProcessConst.EXTEND_ATTR, currentExpandShape.getJSONObject(SubProcessConst.EXTEND_ATTR)); + this.scopeLimitationShape = scopeLimitationShape; // 通知其它节点位置更新 @@ -128,15 +129,17 @@ public class GraphNodeExpandHandle { */ public String handleNodeExpand() throws AWSException{ - // 2、总图节点以及连线处理 + // 总图节点以及连线处理 handleEndToEndGraphNodeAndLinker(); - // 3、当前展开的子流程节点内部元素 以及 范围框处理 + // 当前展开的子流程节点内部元素 以及 范围框处理 handleRelationModelNodePosition(); - // 4、范围框内的元素 坐标更新 + // 当前节点展开前已存在的其它范围框内的元素 坐标更新 scopeShapeMonitor.updateScopeShapeInnerEle(); + linkerConvertHandle.scopeShapeLinkerHandle(); + return definitionHandle.getDefine().toJSONString(); } @@ -157,17 +160,20 @@ public class GraphNodeExpandHandle { // 2、添加到总图中 definitionHandle.addEle(shapeId, scopeLimitationShape); - // 3、总图中符合范围选择框条件的节点 坐标更新 - // handleEndToEndGraphNodeExcluedExpandNode(); - - // 4、构建邻接矩阵 - NodeExpandAdjMatrix expandAdjMatrix = buildEndToEndGraphAdjMatrix(); - expandAdjMatrix.buildAdjMatrix(); - // expandAdjMatrix.printAdjMatrix(); // 5、删除节点展开前的连线 removeEndToEndGraphOldLinker(); - // 6、获取所有节点坐标 + + // 4、构建邻接矩阵 JSONObject elements = definitionHandle.getElements(); + List nodeList = elements.keySet().stream() + .filter(key -> definitionHandle.getElementTypeByKey(key).name().equals(ElementType.OUTER_NODE.name()) || definitionHandle.getElementTypeByKey(key).name().equals(ElementType.SCOPE_NODE.name())) + .map(key -> definitionHandle.getShapeByKey(key)) + .collect(Collectors.toList()); + NodeExpandAdjMatrix expandAdjMatrix = new NodeExpandAdjMatrix(nodeList); + + expandAdjMatrix.buildAdjMatrix(); + + // 6、获取所有节点坐标 double[][] vertexBounding = expandAdjMatrix.getVertexBounding(elements); // 7、构建新的连线 JSONObject processProperties = definitionHandle.getProcessProperties(); @@ -204,6 +210,7 @@ public class GraphNodeExpandHandle { // 根据范围标注框的坐标 调整子流程所有元素的坐标 JSONObject elements = subProcessNodeDefineHandle.getElements(); + JSONArray innerEleKeyArr = new JSONArray(); for (String key : elements.keySet()) { JSONObject ele = elements.getJSONObject(key); // 元素分为两类 一类为图形 一类为连线 @@ -231,8 +238,15 @@ public class GraphNodeExpandHandle { ele.put("elementType", ElementType.INNER_NODE.name()); } + // 指向范围框 + ele.put("scopeShapeId", scopeLimitationShape.getString("id")); + + // 存入内部元素 + innerEleKeyArr.add(key); + definitionHandle.addEle(key, ele); } + scopeLimitationShape.put(SubProcessConst.INNER_ELEMENTS, innerEleKeyArr); } /** @@ -324,41 +338,16 @@ public class GraphNodeExpandHandle { } - /** - * 构建节点展开前 端到端总图的邻接矩阵 - * 方便后续节点展开或者闭合进行重新连线 - */ - private NodeExpandAdjMatrix buildEndToEndGraphAdjMatrix(){ - - List nodeIdList = new ArrayList<>(); - List linkerList = new ArrayList<>(); - JSONObject endToEndProcessElements = definitionHandle.getElements(); - for (String key : endToEndProcessElements.keySet()) { - if (scopeShapeMonitor.checkShapeIsScopeInRange(key)) continue; // 范围框内的元素 暂不处理 - 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; - } - /** * 删除总图中节点展开前的连线 */ private void removeEndToEndGraphOldLinker(){ JSONObject elements = definitionHandle.getElements(); - Set eleKeys = new HashSet<>(); - for (String key : elements.keySet()) { - JSONObject ele = elements.getJSONObject(key); - if ("linker".equals(ele.getString("name")) && !scopeShapeMonitor.checkShapeIsScopeInRange(key)){ - eleKeys.add(key); - } - } - for (String eleKey : eleKeys) { + Set linkerIds = elements.keySet() + .stream() + .filter(key -> definitionHandle.getElementTypeByKey(key).equals(ElementType.OUTER_LINKER) || definitionHandle.getElementTypeByKey(key).equals(ElementType.CROSS_LINKER)) + .collect(Collectors.toSet()); + for (String eleKey : linkerIds) { definitionHandle.removeShape(eleKey); } } @@ -380,32 +369,42 @@ public class GraphNodeExpandHandle { class NodeExpandAdjMatrix extends AbstractAdjMatrix { private List nodeIds; - private List linkerList; + private List nodeList; // 当前画布中所有子流程节点以及范围框节点 不包含内部节点与连线 - public NodeExpandAdjMatrix(List nodeIds, List linkerList) { - super(nodeIds.size()); - this.nodeIds = nodeIds; - this.linkerList = linkerList; + public NodeExpandAdjMatrix(List nodeList) { + super(nodeList.size()); + this.nodeList = nodeList; + + this.nodeIds = nodeList.stream().map(o -> o.getString("id")).collect(Collectors.toList()); } 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); + for (JSONObject node : nodeList) { + String currentNodeId = node.getString("id"); + JSONObject extendAttr = node.getJSONObject(SubProcessConst.EXTEND_ATTR); + // 当前节点的前置节点 + JSONArray leadNodeArr = extendAttr.getJSONArray("leadNodeArr"); + if (leadNodeArr.size() > 0){ + for (Object o : leadNodeArr) { + String leadNodeId = (String) o; + addEdge(nodeIds.indexOf(leadNodeId), nodeIds.indexOf(currentNodeId)); + } + } + // 当前节点的后置节点 + JSONArray rearNodeArr = extendAttr.getJSONArray("rearNodeArr"); + if (rearNodeArr.size() > 0){ + for (Object o : rearNodeArr) { + String rearNodeId = (String) o; + addEdge(nodeIds.indexOf(currentNodeId), nodeIds.indexOf(rearNodeId)); + } + } } } 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 8ce372f3..4b1f7742 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 @@ -20,10 +20,8 @@ import com.actionsoft.exception.AWSException; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; +import java.util.stream.Collectors; public class GraphRender { @@ -79,6 +77,11 @@ public class GraphRender { subProcessNode.put("text", repositoryModel.getName()); // 增加元素类型属性 subProcessNode.put("elementType", ElementType.OUTER_NODE.name()); + // 增加关联文件的标识 + JSONObject extendAttr = new JSONObject(); + extendAttr.put("id", nodeList.get(i).getId()); + extendAttr.put("name", repositoryModel.getName()); + subProcessNode.put(SubProcessConst.EXTEND_ATTR, extendAttr); // 处理子流程模型节点形状属性 JSONArray dataAttributes = subProcessNode.getJSONArray("dataAttributes"); @@ -194,4 +197,41 @@ public class GraphRender { this.baseModel.setDefinition(defineJsonObj.toJSONString()); CoeDesignerAPIManager.getInstance().storeDefinition(this.baseModel); } + + /** + * 依据生成后的总图的连线关系 在子流程节点属性中加入 前置节点与后置节点信息 + */ + public void addLeadAndRearInfoToElements(){ + JSONObject defineJsonObj = JSONObject.parseObject(this.baseModel.getDefinition()); + JSONObject elements = defineJsonObj.getJSONObject("elements"); + + Set subProcessNodeKeySet = elements.keySet().stream().filter(key -> !"linker".equals(elements.getJSONObject(key).getString("name"))).collect(Collectors.toSet()); + for (String subProcessNodeKey : subProcessNodeKeySet) { + JSONObject subProcessNode = elements.getJSONObject(subProcessNodeKey); + JSONObject extendAttr = subProcessNode.getJSONObject(SubProcessConst.EXTEND_ATTR); + + // 当前节点的前置节点 + JSONArray leadNodeKeyArr = elements.keySet().stream() + .filter(key -> "linker".equals(elements.getJSONObject(key).getString("name"))) + .map(key -> elements.getJSONObject(key)) + .filter(l -> ((JSONObject)l).getJSONObject("to").getString("id").equals(subProcessNodeKey)) + .map(l -> ((JSONObject)l).getJSONObject("from").getString("id")) + .collect(Collectors.toCollection(JSONArray::new)); + + extendAttr.put("leadNodeArr", leadNodeKeyArr); + + // 当前节点的后置节点 + JSONArray rearNodeKeyArr = elements.keySet().stream() + .filter(key -> "linker".equals(elements.getJSONObject(key).getString("name"))) + .map(key -> elements.getJSONObject(key)) + .filter(l -> ((JSONObject)l).getJSONObject("from").getString("id").equals(subProcessNodeKey)) + .map(l -> ((JSONObject)l).getJSONObject("to").getString("id")) + .collect(Collectors.toCollection(JSONArray::new)); + + extendAttr.put("rearNodeArr", rearNodeKeyArr); + } + + this.baseModel.setDefinition(defineJsonObj.toJSONString()); + CoeDesignerAPIManager.getInstance().storeDefinition(this.baseModel); + } } diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/component/AbstractDefinitionHandle.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/component/AbstractDefinitionHandle.java index b0a47037..c51eb8b2 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/component/AbstractDefinitionHandle.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/component/AbstractDefinitionHandle.java @@ -1,7 +1,12 @@ package com.actionsoft.apps.coe.method.process.subprocess.graph.component; +import com.actionsoft.apps.coe.method.process.subprocess.constant.ElementType; +import com.actionsoft.exception.AWSException; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import java.util.List; + /** * @author oYang * @Description TODO @@ -99,9 +104,62 @@ public abstract class AbstractDefinitionHandle { */ public abstract void updateShapeH(String shapeKey, double h); + /** + * 获取图形的 x坐标 + * @param shapeKey + * @return + */ public abstract double getShapeX(String shapeKey); + + /** + * 获取图形的 y坐标 + * @param shapeKey + * @return + */ public abstract double getShapeY(String shapeKey); + + /** + * 获取图形的 w宽度 + * @param shapeKey + * @return + */ public abstract double getShapeW(String shapeKey); + + /** + * 获取图形的 h高度 + * @param shapeKey + * @return + */ public abstract double getShapeH(String shapeKey); + /** + * 获取所有连线 + * @return + * @throws AWSException + */ + public abstract JSONArray getLinkers() throws AWSException; + + /** + * 获取指定的连线 + * @param key + * @return + * @throws AWSException + */ + public abstract JSONObject getLinkerByKey(String key) throws AWSException; + + /** + * 获取指定元素的元素类型 + * @param key + * @return + * @throws AWSException + */ + public abstract ElementType getElementTypeByKey(String key) throws AWSException; + + /** + * 获取元素类型为 SCOPE_NODE 的元素ID集合 + * @return + * @throws AWSException + */ + public abstract List getScopeNodeIds() throws AWSException; + } diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/DefinitionThreadSafe.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/DefinitionThreadSafe.java index 776a7cfc..7658d199 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/DefinitionThreadSafe.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/DefinitionThreadSafe.java @@ -1,9 +1,14 @@ package com.actionsoft.apps.coe.method.process.subprocess.graph.util; +import com.actionsoft.apps.coe.method.process.subprocess.constant.ElementType; import com.actionsoft.apps.coe.method.process.subprocess.graph.component.AbstractDefinitionHandle; +import com.actionsoft.exception.AWSException; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import java.util.List; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; /** * @author oYang @@ -178,4 +183,61 @@ public class DefinitionThreadSafe extends AbstractDefinitionHandle { lock.unlock(); } } + + @Override + public JSONArray getLinkers() throws AWSException { + lock.lock(); + try{ + JSONObject elements = getElements(); + JSONArray linkers = elements.keySet() + .stream() + .filter(key -> "linker".equals(elements.getJSONObject(key).getString("name"))) + .map(key -> elements.getJSONObject(key)) + .collect(Collectors.toCollection(JSONArray::new)); + return linkers; + }finally { + lock.unlock(); + } + } + + @Override + public JSONObject getLinkerByKey(String key) throws AWSException { + lock.lock(); + try{ + JSONObject elements = getElements(); + JSONObject ele = elements.getJSONObject(key); + if (!"linker".equals(ele.getString("name"))){ + throw new AWSException("不存在指定的连线"); + } + return ele; + }finally { + lock.unlock(); + } + } + + @Override + public ElementType getElementTypeByKey(String key) throws AWSException { + lock.lock(); + try { + JSONObject ele = getShapeByKey(key); + if (ele == null){ + throw new AWSException("不存在指定的元素"); + } + return ElementType.valueOf(ele.getString("elementType")); + } finally { + lock.unlock(); + } + } + + @Override + public List getScopeNodeIds() throws AWSException { + lock.lock(); + try { + JSONObject elements = getElements(); + List scopeShapeIds = elements.keySet().stream().filter(key -> elements.getJSONObject(key).getString("elementType").equals(ElementType.SCOPE_NODE.name())).collect(Collectors.toList()); + return scopeShapeIds; + } finally { + lock.unlock(); + } + } } diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/DefinitionThreadUnSafe.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/DefinitionThreadUnSafe.java index 2a1e5ad0..ebc580b4 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/DefinitionThreadUnSafe.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/DefinitionThreadUnSafe.java @@ -1,8 +1,15 @@ package com.actionsoft.apps.coe.method.process.subprocess.graph.util; +import com.actionsoft.apps.coe.method.process.subprocess.constant.ElementType; import com.actionsoft.apps.coe.method.process.subprocess.graph.component.AbstractDefinitionHandle; +import com.actionsoft.exception.AWSException; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + /** * @author oYang * @Description 操作 definition 工具类 线程不安全 @@ -98,4 +105,41 @@ public class DefinitionThreadUnSafe extends AbstractDefinitionHandle { public double getShapeH(String shapeKey) { return getShapeByProps(shapeKey).getDoubleValue("h"); } + + @Override + public JSONArray getLinkers() throws AWSException { + JSONObject elements = getElements(); + JSONArray linkers = elements.keySet() + .stream() + .filter(key -> "linker".equals(elements.getJSONObject(key).getString("name"))) + .map(key -> elements.getJSONObject(key)) + .collect(Collectors.toCollection(JSONArray::new)); + return linkers; + } + + @Override + public JSONObject getLinkerByKey(String key) throws AWSException { + JSONObject elements = getElements(); + JSONObject ele = elements.getJSONObject(key); + if (!"linker".equals(ele.getString("name"))){ + throw new AWSException("不存在指定的连线"); + } + return ele; + } + + @Override + public ElementType getElementTypeByKey(String key) throws AWSException { + JSONObject ele = getShapeByKey(key); + if (ele == null){ + throw new AWSException("不存在指定的元素"); + } + return ElementType.valueOf(ele.getString("elementType")); + } + + @Override + public List getScopeNodeIds() throws AWSException { + JSONObject elements = getElements(); + List scopeShapeIds = elements.keySet().stream().filter(key -> elements.getJSONObject(key).getString("elementType").equals(ElementType.SCOPE_NODE.name())).collect(Collectors.toList()); + return scopeShapeIds; + } } diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/GraphNodeCloseUtil.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/GraphNodeCloseUtil.java deleted file mode 100644 index fd915eef..00000000 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/GraphNodeCloseUtil.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.actionsoft.apps.coe.method.process.subprocess.graph.util; - -/** - * @author oYang - * @Description TODO - * @createTime 2023年06月09日 16:26:00 - */ -public class GraphNodeCloseUtil { -} diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/LinkerPointCalculationHandle.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/LinkerPointCalculationHandle.java new file mode 100644 index 00000000..b8c2d3d5 --- /dev/null +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/LinkerPointCalculationHandle.java @@ -0,0 +1,452 @@ +package com.actionsoft.apps.coe.method.process.subprocess.graph.util; + +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.graph.component.AbstractDefinitionHandle; +import com.actionsoft.bpms.util.ConsolePrinter; +import com.actionsoft.bpms.util.UUIDGener; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +import java.util.Comparator; +import java.util.List; + +/** + * @author oYang + * @Description 连线上所有关键节点(包括起始点以及折点)计算处理 + * 类比起始图形作为直角坐标系的原点 目标图形的位置大体在四个象限内 + * @createTime 2023年06月28日 17:56:00 + */ +public class LinkerPointCalculationHandle { + + private AbstractDefinitionHandle definitionHandle; + + public LinkerPointCalculationHandle(AbstractDefinitionHandle definitionHandle) { + this.definitionHandle = definitionHandle; + } + + /** + * 根据布局方向 组装连线 + * @param direction 布局方向 + * @param fromId 起点节点ID + * @param toId 终点节点ID + * @param fromBounding 起点节点边框信息 + * @param toBounding 终点节点边框信息 + * @return + */ + public JSONObject toAssembleLinker(String direction, String fromId, String toId, double[] fromBounding, double[] toBounding){ + double[][] turnPoi = direction.equals(SubProcessConst.DIRECTION_H) ? horizLayOut(fromBounding, toBounding) : vertLayOut(fromBounding, toBounding); + double[] angleArr = calculationLinkerAngle(turnPoi[0], turnPoi[turnPoi.length - 1], 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", fromId); + 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", toId); + linkerObj.put("to", toObj); + + return linkerObj; + } + + + /** + * 节点水平布局下计算连线 + * @param fromBounding + * @param toBounding + * @return + */ + public double[][] horizLayOut(double[] fromBounding, double[] toBounding){ + double fromX = fromBounding[0],fromY = fromBounding[1], fromW = fromBounding[2], fromH = fromBounding[3]; + double toX = toBounding[0], toY = toBounding[1], toW = toBounding[2], toH = toBounding[3]; + if (fromY == toY) { + return fromX < toX + ? new double[][] + { + {fromX + fromW, fromY + fromH / 2}, + {fromX + getCurrentColMaxW(fromX) + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, fromY + fromH / 2}, + {toX - rowNodeDistanceWrapper(getNearLeftNodeDistance(toBounding)) / 2, toY + toH / 2}, + {toX, toY + toH / 2} + } + : new double[][] + { + {fromX + fromW, fromY + fromH / 2}, + {fromX + fromW + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, fromY + fromH / 2}, + {fromX + fromW + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, fromY - colNodeDistanceWrapper(getNearTopNodeDistance(fromBounding)) / 2}, + {toX + toW / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + {toX + toW / 2, toY} + }; + }else if (fromX == toX) { // 垂直 + // 节点横向分布 连线按照大原则 垂直 不存在 fromY < toY 的情况 也就是不存在 连线从上到下直连的情况 + double[] startPoint = new double[]{fromX + fromW, fromY + fromH / 2}; + double[] endPoint = new double[]{toX + toW / 2, toY}; + return new double[][]{startPoint, + {fromX + getCurrentColMaxW(fromX) + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, fromY + fromH / 2}, + {fromX + getCurrentColMaxW(fromX) + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + {toX + toW / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + endPoint}; + }else { + if (fromX < toX && fromY > toY){ // 目标节点在第一象限 + double[] startPoint = new double[]{fromX + fromW, fromY + fromH / 2}; + double turnPointX = fromX + getCurrentColMaxW(fromX) + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2; + if (fromX + getCurrentColMaxW(fromX) + getNearRightNodeDistance(fromBounding) == toX){ // 相邻节点 存在两个折点 + double[] endPoint = new double[]{toX, toY + toH / 2}; + return new double[][]{startPoint,{turnPointX, fromY + fromH / 2},{turnPointX, toY + toH / 2}, endPoint}; + }else { // 不相邻节点 存在三个折点 + double[] endPoint = new double[]{toX + toW / 2, toY}; + return new double[][]{ + startPoint, + {turnPointX, fromY + fromH / 2}, + {turnPointX, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + {toX + toW / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + endPoint + }; + } + }else if (fromX > toX && fromY > toY) { // 目标节点在第二象限 无论节点是否相邻 都按照三个折点走 + double[] startPoint = new double[]{fromX + fromW, fromY + fromH / 2}; + double[] endPoint = new double[]{toX + toW / 2, toY}; + return new double[][]{ + startPoint, + {fromX + getCurrentColMaxW(fromX) + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, fromY + fromH / 2}, + {fromX + getCurrentColMaxW(fromX) + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + {toX + toW / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + endPoint + }; + }else if (fromX < toX && fromY < toY){ // 目标节点在第四象限 + double[] startPoint = new double[]{fromX + fromW, fromY + fromH / 2}; + if (fromX + getCurrentColMaxW(fromX) + getNearRightNodeDistance(fromBounding) == toX){ // 相邻节点 存在两个折点 + double turnPointX = fromX + getCurrentColMaxW(fromX) + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2; + double[] endPoint = new double[]{toX, toY + toH / 2}; + return new double[][]{ + startPoint, + {turnPointX, fromY + fromH / 2}, + {turnPointX, toY + toH / 2}, + endPoint + }; + }else { // 不相邻节点 存在三个折点 + double[] endPoint = new double[]{toX + toW / 2, toY}; + return new double[][]{ + startPoint, + {fromX + getCurrentColMaxW(fromX) + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, fromY + fromH / 2}, + {fromX + getCurrentColMaxW(fromX) + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + {toX + toW / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + endPoint + }; + } + }else if (fromX > toX && fromY < toY){ // 目标节点在第三象限 横向布局的情况下 应该不会出现目标节点在第三象限的情况 + ConsolePrinter.warn("[端到端功能][节点展开模块]处理连线时目标节点在[横向布局]的情况下出现在了第三象限"); + } + } + return new double[2][2]; + } + + /** + * 节点垂直布局下计算连线 + * @param fromBounding + * @param toBounding + * @return + */ + public double[][] vertLayOut(double[] fromBounding, double[] toBounding){ + double fromX = fromBounding[0],fromY = fromBounding[1], fromW = fromBounding[2], fromH = fromBounding[3]; + double toX = toBounding[0], toY = toBounding[1], toW = toBounding[2], toH = toBounding[3]; + if (fromY == toY){ // 水平 分析可知 水平方向上不会出现 从左到右直连的情况 只有 右边节点右侧锚点出 向上走 左折 连到左侧节点上方锚点 + double[] startPoi = new double[]{fromX + fromW, fromY + fromH / 2}; + double[] turnPoi1 = new double[]{fromX + fromW + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, fromY + fromH / 2}; + double[] turnPoi2 = new double[]{fromX + fromW + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}; + double[] turnPoi3 = new double[]{toX + toW / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}; + double[] endPoi = new double[]{toX + toW / 2, toY}; + return new double[][]{startPoi, turnPoi1, turnPoi2, turnPoi3, endPoi}; + }else if (fromX == toX){ // 垂直 分析可知 垂直方向上应该不会有 toY < fromY 的情况 鉴于数据不确定性 先写上 + double[] startPoi = fromY < toY + ? new double[]{fromX + fromW / 2, fromY + fromH} + : new double[]{fromX +fromW, fromY + fromH / 2}; + double[] endPoi = new double[]{toX + toW / 2, toY}; + return fromY < toY + ? new double[][]{ + startPoi, + {fromX + fromW / 2, fromY + getCurrentRowMaxH(fromY) + colNodeDistanceWrapper(getNearBootomNodeDistance(fromBounding)) / 2}, + {toX + toW / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + endPoi} + : new double[][]{ + startPoi, + {fromX + getCurrentColMaxW(fromX) + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, fromY + fromH / 2}, + {fromX + getCurrentColMaxW(fromX) + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + {toX + toW / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + endPoi}; + }else { // 分布在四个象限内 + if (fromX > toX && fromY > toY){ // 目标节点在第二象限 + return new double[][]{ + {fromX + fromW, fromY + fromH / 2}, + {fromX + getCurrentColMaxW(fromX) + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, fromY + fromH / 2}, + {fromX + getCurrentColMaxW(fromX) + rowNodeDistanceWrapper(getNearRightNodeDistance(fromBounding)) / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + {toX + toW / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + {toX + toW / 2, toY} + }; + }else if (fromX > toX && fromY < toY){ // 目标节点在第三象限 + return toY - getCurrentRowMaxH(fromY) == getNearBootomNodeDistance(fromBounding) // 相邻行节点 + ? new double[][] + { + {fromX + fromW / 2, fromY + fromH}, + {fromX + fromW / 2, fromY + getCurrentRowMaxH(fromY) + colNodeDistanceWrapper(getNearBootomNodeDistance(fromBounding)) / 2}, + {toX + toW / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + {toX + toW / 2, toY} + } + : new double[][] + { + {fromX + fromW / 2, fromY + fromH}, + {fromX + fromW / 2, fromY + getCurrentRowMaxH(fromY) + colNodeDistanceWrapper(getNearBootomNodeDistance(fromBounding)) / 2}, + {toX + toW + rowNodeDistanceWrapper(getNearRightNodeDistance(toBounding)) / 2, fromY + getCurrentRowMaxH(fromY) + colNodeDistanceWrapper(getNearBootomNodeDistance(fromBounding)) / 2}, + {toX + toW + rowNodeDistanceWrapper(getNearRightNodeDistance(toBounding)) / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + {toX + toW / 2, toY - colNodeDistanceWrapper(getNearTopNodeDistance(toBounding)) / 2}, + {toX + toW / 2, toY} + }; + }else if (fromX < toX && fromY < toY){ // 目标节点在第四象限 + return new double[][]{ + {fromX + fromW / 2, fromY + fromH}, + {fromX + fromW / 2, fromY + getCurrentRowMaxH(fromY) + colNodeDistanceWrapper(getNearBootomNodeDistance(fromBounding)) / 2}, + {toX + toW / 2, fromY + getCurrentRowMaxH(fromY) + colNodeDistanceWrapper(getNearBootomNodeDistance(fromBounding)) / 2}, + {toX + toW / 2, toY} + }; + }else { + // fromX < toX && fromY > toY 目标节点在第一象限 分析可知 纵向排布的情况下 应该不会出现目标节点在第一象限的情况 + } + } + return new double[2][2]; + } + + /** + * 获取当前同一列图形的最大宽度 在水平布局中 + * @param fromShapeX 当前图形的 x 坐标 + * @return + */ + private double getCurrentColMaxW(double shapeX){ + JSONObject elements = definitionHandle.getElements(); + JSONObject props = elements.keySet().stream() + .filter(key -> !"linker".equals(elements.getJSONObject(key).getString("name")) && shapeX == elements.getJSONObject(key).getJSONObject("props").getDoubleValue("x")) + .map(key -> elements.getJSONObject(key).getJSONObject("props")) + .max(Comparator.comparing(o -> o.getDoubleValue("w"))).get(); + return props.getDoubleValue("w"); + } + + /** + * 获取当前同一行图形的最大高度 在垂直布局中 + * @param fromShapeY + * @return + */ + private double getCurrentRowMaxH(double shapeY){ + JSONObject elements = definitionHandle.getElements(); + JSONObject props = elements.keySet().stream() + .filter(key -> !"linker".equals(elements.getJSONObject(key).getString("name")) && shapeY == elements.getJSONObject(key).getJSONObject("props").getDoubleValue("y")) + .map(key -> elements.getJSONObject(key).getJSONObject("props")) + .max(Comparator.comparing(o -> o.getDoubleValue("h"))).get(); + return props.getDoubleValue("h"); + } + + /** + * 获取当前图形 水平方向上 左侧相邻节点 可能为空 + * @param shapeBounding [x, y, w, h] + * @return JSONObject shape + */ + private JSONObject getNearLeftNode(double[] shapeBounding){ + double shapeX = shapeBounding[0], shapeY = shapeBounding[1]; + JSONObject elements = definitionHandle.getElements(); + JSONObject leftNode = elements.keySet().stream() + .filter(key -> !"linker".equals(elements.getJSONObject(key).getString("name"))) + .filter(key -> shapeY == elements.getJSONObject(key).getJSONObject("props").getDoubleValue("y")) + .filter(key -> shapeX > elements.getJSONObject(key).getJSONObject("props").getDoubleValue("x")) + .map(key -> elements.getJSONObject(key)) + .max(Comparator.comparing(o -> o.getJSONObject("props").getDoubleValue("x"))).orElse(null); + return leftNode; + } + + /** + * 获取当前图形 水平方向上 与左侧相邻节点的间距 如果为空 则返回默认间距 + * @param shapeBounding [x, y, w, h] + * @return + */ + private double getNearLeftNodeDistance(double[] shapeBounding){ + JSONObject nearLeftNode = getNearLeftNode(shapeBounding); + if (nearLeftNode == null){ + return SubProcessConst.SHAPE_HORIZ_INTERVAL; + } + double shapeX = shapeBounding[0]; + JSONObject props = nearLeftNode.getJSONObject("props"); + return shapeX - props.getDoubleValue("x") + props.getDoubleValue("w"); + } + + /** + * 获取当前图形 水平方向上 右侧相邻节点 可能为空 + * @param shapeBounding [x, y, w, h] + * @return + */ + private JSONObject getNearRightNode(double[] shapeBounding){ + double shapeX = shapeBounding[0], shapeY = shapeBounding[1]; + JSONObject elements = definitionHandle.getElements(); + JSONObject rightNode = elements.keySet().stream() + .filter(key -> !"linker".equals(elements.getJSONObject(key).getString("name"))) + .filter(key -> shapeY == elements.getJSONObject(key).getJSONObject("props").getDoubleValue("y")) + .filter(key -> shapeX < elements.getJSONObject(key).getJSONObject("props").getDoubleValue("x")) + .map(key -> elements.getJSONObject(key)) + .min(Comparator.comparing(o -> o.getJSONObject("props").getDoubleValue("x"))).orElse(null); + return rightNode; + } + + /** + * 获取当前图形 水平方向上 与右侧相邻节点的间距 如果为空 则返回默认间距 + * @param shapeBounding [x, y, w, h] + * @return + */ + private double getNearRightNodeDistance(double[] shapeBounding){ + JSONObject nearRightNode = getNearRightNode(shapeBounding); + if (nearRightNode == null){ + return SubProcessConst.SHAPE_HORIZ_INTERVAL; + } + double shapeX = shapeBounding[0], shapeW = shapeBounding[2]; + JSONObject props = nearRightNode.getJSONObject("props"); + return props.getDoubleValue("x") - shapeX + shapeW; + } + + /** + * 获取当前图形 垂直方向上 与上侧相邻节点 可能为空 + * @param shapeBounding [x, y, w, h] + * @return + */ + private JSONObject getNearTopNode(double[] shapeBounding){ + double shapeX = shapeBounding[0], shapeY = shapeBounding[1]; + JSONObject elements = definitionHandle.getElements(); + JSONObject topNode = elements.keySet().stream() + .filter(key -> !"linker".equals(elements.getJSONObject(key).getString("name"))) + .filter(key -> shapeX == elements.getJSONObject(key).getJSONObject("props").getDoubleValue("x")) + .filter(key -> shapeY > elements.getJSONObject(key).getJSONObject("props").getDoubleValue("y")) + .map(key -> elements.getJSONObject(key)) + .max(Comparator.comparing(o -> o.getJSONObject("props").getDoubleValue("y"))).orElse(null); + return topNode; + } + + /** + * 获取当前图形 垂直方向上 与上侧相邻节点的间距 如果为空 则返回默认间距 + * @param shapeBounding [x, y, w, h] + * @return + */ + private double getNearTopNodeDistance(double[] shapeBounding){ + JSONObject nearTopNode = getNearTopNode(shapeBounding); + if (nearTopNode == null){ + return SubProcessConst.SHAPE_VERT_INTERVAL; + } + JSONObject props = nearTopNode.getJSONObject("props"); + double shapeY = shapeBounding[1]; + return shapeY - props.getDoubleValue("y") + props.getDoubleValue("h"); + } + + /** + * 获取当前图形 垂直方向上 与下侧相邻节点 可能为空 + * @param shapeBounding [x, y, w, h] + * @return + */ + private JSONObject getNearBootomNode(double[] shapeBounding){ + double shapeX = shapeBounding[0], shapeY = shapeBounding[1]; + JSONObject elements = definitionHandle.getElements(); + JSONObject bottomNode = elements.keySet().stream() + .filter(key -> !"linker".equals(elements.getJSONObject(key).getString("name"))) + .filter(key -> shapeX == elements.getJSONObject(key).getJSONObject("props").getDoubleValue("x")) + .filter(key -> shapeY < elements.getJSONObject(key).getJSONObject("props").getDoubleValue("y")) + .map(key -> elements.getJSONObject(key)) + .min(Comparator.comparing(o -> o.getJSONObject("props").getDoubleValue("y"))).orElse(null); + return bottomNode; + } + + /** + * 获取当前图形 垂直方向上 与下侧相邻节点的间距 如果为空 则返回默认间距 + * @param shapeBounding [x, y, w, h] + * @return + */ + private double getNearBootomNodeDistance(double[] shapeBounding){ + JSONObject nearBootomNode = getNearBootomNode(shapeBounding); + if (nearBootomNode == null){ + return SubProcessConst.SHAPE_VERT_INTERVAL; + } + JSONObject props = nearBootomNode.getJSONObject("props"); + double shapeY = shapeBounding[1], shapeH = shapeBounding[3]; + return props.getDoubleValue("y") - shapeY + shapeH; + } + + + /** + * 如果水平方向上节点间距大于默认间距 返回默认间距 否则返回实际间距 + * @param distance + * @return + */ + private double rowNodeDistanceWrapper(double distance){ + if (distance > SubProcessConst.SHAPE_HORIZ_INTERVAL){ + return SubProcessConst.SHAPE_HORIZ_INTERVAL; + } + return distance; + } + + /** + * 如果垂直方向上节点间距大于默认间距 返回默认间距 否则返回实际间距 + * @param distance + * @return + */ + private double colNodeDistanceWrapper(double distance){ + if (distance > SubProcessConst.SHAPE_VERT_INTERVAL){ + return SubProcessConst.SHAPE_VERT_INTERVAL; + } + return distance; + } + + /** + * 计算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/mode/ScopeShapeMonitor.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/ScopeShapeMonitor.java similarity index 80% rename from com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/mode/ScopeShapeMonitor.java rename to com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/ScopeShapeMonitor.java index c13ed59f..34fe066b 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/mode/ScopeShapeMonitor.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/ScopeShapeMonitor.java @@ -1,20 +1,20 @@ -package com.actionsoft.apps.coe.method.process.subprocess.mode; +package com.actionsoft.apps.coe.method.process.subprocess.graph.util; +import com.actionsoft.apps.coe.method.process.subprocess.constant.SubProcessConst; import com.actionsoft.apps.coe.method.process.subprocess.graph.component.AbstractDefinitionHandle; import com.actionsoft.apps.coe.method.process.subprocess.graph.util.SubProcessNodeDefineUtil; import com.actionsoft.exception.AWSException; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; /** * @author oYang - * @Description 构建虚线范围框监视器模型且关联其内部元素 方便最后调整范围框内的元素 + * @Description 构建总图中已存在的范围框监视器模型且关联其内部元素 + * 方便最后调整范围框内的元素位置 + * 内部元素只包含元素类型为 ElementType.INNER_NODE ElementType.INNER_LINKER * @createTime 2023年06月13日 14:46:00 */ public class ScopeShapeMonitor { @@ -34,43 +34,34 @@ public class ScopeShapeMonitor { */ public void buildScopeShapeMonitors(){ JSONObject elements = definitionHandle.getElements(); - Set scopeShapeKeySet = elements.keySet().stream().filter(key -> "scopeLimitation".equals(elements.getJSONObject(key).getString("name"))).collect(Collectors.toSet()); + // 获取总图中已存在的范围框图形ID集合 + List scopeShapeKeySet = definitionHandle.getScopeNodeIds(); if (scopeShapeKeySet.size() == 0) return; Map scopeShapeMonitorMap = new HashMap<>(); for (String scopeShapeKey : scopeShapeKeySet) { - JSONObject props = definitionHandle.getShapeByProps(scopeShapeKey); - double scopeX = props.getDoubleValue("x"), scopeY = props.getDoubleValue("y"), scopeW = props.getDoubleValue("w"), scopeH = props.getDoubleValue("h"); - // 判断当前元素是否在范围选择框的范围内 - Set inRangeEleKeySet = new HashSet<>(); - for (String key : elements.keySet()) { - if (scopeShapeKey.equals(key)) continue; // 范围框本身不算作其内部元素 - JSONObject shape = definitionHandle.getShapeByKey(key); - if ("linker".equals(shape.getString("name"))){ // 连线 - JSONObject from = shape.getJSONObject("from"); - JSONObject to = shape.getJSONObject("to"); - double fromX = from.getDoubleValue("x"), fromY = from.getDoubleValue("y"), toX = to.getDoubleValue("x"), toY = to.getDoubleValue("y"); - if ((scopeX < fromX && fromX < scopeX + scopeW && scopeY < fromY && fromY < scopeY + scopeH) || (scopeX < toX && toX < scopeX + scopeW && scopeY < toY && toY < scopeY + scopeH)){ - inRangeEleKeySet.add(key); - } - }else { - double x = definitionHandle.getShapeByProps(key).getDoubleValue("x"); - double y = definitionHandle.getShapeByProps(key).getDoubleValue("y"); - if (scopeX <= x && x < scopeX + scopeW && scopeY <= y && y < scopeY + scopeH){ - inRangeEleKeySet.add(key); - } - } - } + JSONObject scopeShape = definitionHandle.getShapeByKey(scopeShapeKey); + // 获取范围框内部元素 + JSONArray innerEleIds = scopeShape.getJSONArray(SubProcessConst.INNER_ELEMENTS); + Set inRangeEleKeySet = innerEleIds.stream().map(o -> o.toString()).collect(Collectors.toSet()); MonitorInfo monitorInfo = new MonitorInfo(scopeShapeKey, false, 0.0, false, 0.0, inRangeEleKeySet); scopeShapeMonitorMap.put(scopeShapeKey, monitorInfo); } this.scopeShapeMonitorMap = scopeShapeMonitorMap; } + /** + * 返回所有的范围框监视器数据模型 + * @return + */ public Map getScopeShapeMonitorMap() { return scopeShapeMonitorMap; } + /** + * 移除指定的范围框监视器数据模型 + * @param scopeShapeId + */ public void removeScopeShapeByKey(String scopeShapeId){ if (scopeShapeMonitorMap.containsKey(scopeShapeId)){ scopeShapeMonitorMap.remove(scopeShapeId); @@ -78,7 +69,7 @@ public class ScopeShapeMonitor { } /** - * 更新范围框的监视属性信息 + * 更新指定范围框内部元素在x方向上的位置信息 * @param scopeShapeId 当前范围框图形ID * @param isRightMove 是否右移 * @param rightMoveDistance 右移动距离 @@ -94,7 +85,7 @@ public class ScopeShapeMonitor { } /** - * 更新范围框的监视属性信息 + * 更新指定范围框内部元素在y方向上的位置信息 * @param scopeShapeId 当前范围框图形ID * @param isBottomMove 是否下移 * @param bottomMoveDistance 下移距离 @@ -194,7 +185,6 @@ public class ScopeShapeMonitor { } } - public class MonitorInfo { private String scopeShapeId; // 当前虚线范围框的图形ID diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/SubProcessNodeDefineUtil.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/SubProcessNodeDefineUtil.java index 06ed1bd0..449abe4a 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/SubProcessNodeDefineUtil.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/util/SubProcessNodeDefineUtil.java @@ -36,6 +36,11 @@ public class SubProcessNodeDefineUtil { if (childProcessBaseModel == null) throw new AWSException("当前子流程节点内部可能没有图形元素,可以去添加后展开"); subProcessNodeDefineStr = childProcessBaseModel.getDefinition(); + JSONObject defineObj = JSONObject.parseObject(subProcessNodeDefineStr); + JSONObject elements = defineObj.getJSONObject("elements"); + if (elements.size() == 0){ + throw new AWSException("当前子流程节点内部可能没有图形元素,可以去添加后展开"); + } } return subProcessNodeDefineStr; } diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/observers/node/NodeObserver.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/observers/node/NodeObserver.java index 8fc5721a..c0986894 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/observers/node/NodeObserver.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/observers/node/NodeObserver.java @@ -1,9 +1,8 @@ package com.actionsoft.apps.coe.method.process.subprocess.observers.node; import com.actionsoft.apps.coe.method.process.subprocess.graph.component.AbstractDefinitionHandle; -import com.actionsoft.apps.coe.method.process.subprocess.mode.ScopeShapeMonitor; +import com.actionsoft.apps.coe.method.process.subprocess.graph.util.ScopeShapeMonitor; import com.actionsoft.apps.coe.method.process.subprocess.observers.Observer; -import com.alibaba.fastjson.JSONObject; /** * @author oYang diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/observers/node/NodeSubject.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/observers/node/NodeSubject.java index 44df489d..c470ebb2 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/observers/node/NodeSubject.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/observers/node/NodeSubject.java @@ -2,7 +2,7 @@ package com.actionsoft.apps.coe.method.process.subprocess.observers.node; import com.actionsoft.apps.coe.method.process.subprocess.constant.SubProcessConst; import com.actionsoft.apps.coe.method.process.subprocess.graph.component.AbstractDefinitionHandle; -import com.actionsoft.apps.coe.method.process.subprocess.mode.ScopeShapeMonitor; +import com.actionsoft.apps.coe.method.process.subprocess.graph.util.ScopeShapeMonitor; import com.actionsoft.apps.coe.method.process.subprocess.observers.Observer; import com.actionsoft.apps.coe.method.process.subprocess.observers.Subject; import com.alibaba.fastjson.JSONObject; diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/uitl/Node.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/uitl/Node.java deleted file mode 100644 index 2a210048..00000000 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/uitl/Node.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.actionsoft.apps.coe.method.process.subprocess.uitl; - -/** - * @author oYang - * @create 2023-05-11 13:57 - */ -public class Node { - - public int id; // 节点id - public double x; // 节点x坐标 - public double y; // 节点y坐标 - public double displaceX; // x方向移动位移 - public double displaceY; // y方向移动位移 - - public Node(int id) { - this.id = id; - this.x = 0; - this.y = 0; - this.displaceX = 0; - this.displaceY = 0; - } -} diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/uitl/UtilTestGraph.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/uitl/UtilTestGraph.java deleted file mode 100644 index f2799d77..00000000 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/uitl/UtilTestGraph.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.actionsoft.apps.coe.method.process.subprocess.uitl; - -import java.util.ArrayList; -import java.util.Random; - -/** - * @author oYang - * @create 2023-05-11 13:53 - */ -public class UtilTestGraph { - - private final int width; // 布局区域宽度 - private final int height; // 布局区域高度 - private final double k; // 弹性系数 - private final double k2; // 斥力系数 - private final double damping; // 阻尼系数 - private final double maxDisplace; // 最大移动距离 - private final int maxIterations; // 最大迭代次数 - - // 存储每个节点的位置信息 - private ArrayList nodes; - - // 存储每个节点之间的连线信息(邻接矩阵) - private int[][] adjacencyMatrix; - - public UtilTestGraph(int width, int height, double k, double k2, double damping, double maxDisplace, int maxIterations, int nodeCount) { - this.width = width; - this.height = height; - this.k = k; - this.k2 = k2; - this.damping = damping; - this.maxDisplace = maxDisplace; - this.maxIterations = maxIterations; - - // 初始化节点数组 - this.nodes = new ArrayList<>(nodeCount); - for (int i = 0; i < nodeCount; i++) { - Node node = new Node(i); - node.x = Math.random() * this.width; - node.y = Math.random() * this.height; - nodes.add(node); - } - - // 初始化邻接矩阵 - this.adjacencyMatrix = new int[nodeCount][nodeCount]; - Random random = new Random(); - for (int i = 0; i < nodeCount; i++) { - for (int j = i + 1; j < nodeCount; j++) { - // 随机生成一个有连线的概率 - double p = random.nextDouble(); - if (p < 0.3) { - adjacencyMatrix[i][j] = 1; - adjacencyMatrix[j][i] = 1; - } - } - } - } - - // 执行布局操作 - public void executeLayout() { - for (int i = 0; i < maxIterations; i++) { - for (int j = 0; j < nodes.size(); j++) { - Node node = nodes.get(j); - node.displaceX = 0; - node.displaceY = 0; - for (int k = 0; k < nodes.size(); k++) { - if (j == k) continue; - Node other = nodes.get(k); - double dx = other.x - node.x; - double dy = other.y - node.y; - double distanceSquared = dx * dx + dy * dy; - if (distanceSquared == 0) { - dx = randomDisplacement(); - dy = randomDisplacement(); - distanceSquared = dx * dx + dy * dy; - } - double distance = Math.sqrt(distanceSquared); - double force = (k * k / distance) - (k2 * distance); - node.displaceX += (dx / distance) * force; - node.displaceY += (dy / distance) * force; - } - } - - // 移动节点的位置 - for (Node node : nodes) { - double xDisplace = node.displaceX * damping; - double yDisplace = node.displaceY * damping; - double displace = Math.sqrt(xDisplace * xDisplace + yDisplace * yDisplace); - if (displace > maxDisplace) { - xDisplace *= maxDisplace / displace; - yDisplace *= maxDisplace / displace; - } - node.x += xDisplace; - node.y += yDisplace; - } - } - } - - // 随机生成一个小的位移量 - private double randomDisplacement() { - return (Math.random() - 0.5) * 0.1; - } - - public static void main(String[] args) { - UtilTestGraph layout = new UtilTestGraph(800, 600, 5.0, 0.1, 0.9, 5.0, 100, 20); - layout.executeLayout(); - - // 输出节点的最终位置 - for (Node node : layout.nodes) { - System.out.println("Node " + node.id + ": (" + node.x + ", " + node.y + ")"); - } - } -} 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 c318ede8..7ea2ce4f 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 @@ -306,6 +306,8 @@ public class SubProcessWeb extends ActionWeb { // 流程属性中加入布局方向 graphRender.addDirectionToProcessProperties(direction); + graphRender.addLeadAndRearInfoToElements(); + } @@ -324,6 +326,7 @@ public class SubProcessWeb extends ActionWeb { ro.setData(define); return ro.toString(); } catch (AWSException e) { + e.printStackTrace(); return ResponseObject.newErrResponse(e.getMessage()).toString(); } } @@ -343,6 +346,7 @@ public class SubProcessWeb extends ActionWeb { ro.setData(define); return ro.toString(); } catch (AWSException e) { + e.printStackTrace(); return ResponseObject.newErrResponse(e.getMessage()).toString(); } }