Merge remote-tracking branch 'origin/apps_dev' into apps_dev

This commit is contained in:
Mr-wang 2023-06-30 17:37:40 +08:00
commit 28ffdedb99
19 changed files with 1066 additions and 385 deletions

View File

@ -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";
}

View File

@ -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<String> 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<String> 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<String> 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);
// }
// }
}
}

View File

@ -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;
}

View File

@ -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<JSONObject> 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<String> 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<String> 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<String> nodeIdList = new ArrayList<>();
List<JSONObject> 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<String> keys = SubProcessNodeDefineUtil.getInScopeLimitationRangeEles(shapeId, definitionHandle, subProcessNodeDefineHandle);
JSONArray innerEleKeySet = scopeLimitationShape.getJSONArray(SubProcessConst.INNER_ELEMENTS);
Set<String> 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<String> nodeIds;
private List<JSONObject> linkerList;
private List<JSONObject> nodeList; // 当前画布中所有子流程节点以及范围框节点 不包含内部节点与连线
public NodeCloseAdjMatrix(List<String> nodeIds, List<JSONObject> linkerList) {
super(nodeIds.size());
this.nodeIds = nodeIds;
this.linkerList = linkerList;
public NodeCloseAdjMatrix(List<JSONObject> nodeList) {
super(nodeList.size());
this.nodeList = nodeList;
this.nodeIds = nodeList.stream().map(o -> o.getString("id")).collect(Collectors.toList());
}
public List<String> getNodeIds() {
return nodeIds;
}
public List<JSONObject> 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));
}
}
}
}

View File

@ -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<JSONObject> 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<String> nodeIdList = new ArrayList<>();
List<JSONObject> 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<String> 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<String> 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<String> nodeIds;
private List<JSONObject> linkerList;
private List<JSONObject> nodeList; // 当前画布中所有子流程节点以及范围框节点 不包含内部节点与连线
public NodeExpandAdjMatrix(List<String> nodeIds, List<JSONObject> linkerList) {
super(nodeIds.size());
this.nodeIds = nodeIds;
this.linkerList = linkerList;
public NodeExpandAdjMatrix(List<JSONObject> nodeList) {
super(nodeList.size());
this.nodeList = nodeList;
this.nodeIds = nodeList.stream().map(o -> o.getString("id")).collect(Collectors.toList());
}
public List<String> getNodeIds() {
return nodeIds;
}
public List<JSONObject> 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));
}
}
}
}

View File

@ -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<String> 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);
}
}

View File

@ -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<String> getScopeNodeIds() throws AWSException;
}

View File

@ -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<String> getScopeNodeIds() throws AWSException {
lock.lock();
try {
JSONObject elements = getElements();
List<String> scopeShapeIds = elements.keySet().stream().filter(key -> elements.getJSONObject(key).getString("elementType").equals(ElementType.SCOPE_NODE.name())).collect(Collectors.toList());
return scopeShapeIds;
} finally {
lock.unlock();
}
}
}

View File

@ -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<String> getScopeNodeIds() throws AWSException {
JSONObject elements = getElements();
List<String> scopeShapeIds = elements.keySet().stream().filter(key -> elements.getJSONObject(key).getString("elementType").equals(ElementType.SCOPE_NODE.name())).collect(Collectors.toList());
return scopeShapeIds;
}
}

View File

@ -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 {
}

View File

@ -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};
}
}
}

View File

@ -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<String> scopeShapeKeySet = elements.keySet().stream().filter(key -> "scopeLimitation".equals(elements.getJSONObject(key).getString("name"))).collect(Collectors.toSet());
// 获取总图中已存在的范围框图形ID集合
List<String> scopeShapeKeySet = definitionHandle.getScopeNodeIds();
if (scopeShapeKeySet.size() == 0) return;
Map<String, MonitorInfo> 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<String> 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<String> 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<String, MonitorInfo> 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

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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<Node> 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 + ")");
}
}
}

View File

@ -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();
}
}