feat(diagram): 流程图竖排改造(横→竖)——布局单源坐标转置+名单2行换行+边几何/箭头/标签翻转,golden 用例改写并新增 6 出口用例
build / build-push (push) Successful in 2m44s

This commit is contained in:
chenxing
2026-10-09 19:42:50 +08:00
parent 56a71f813f
commit 7c92e953ec
3 changed files with 227 additions and 103 deletions
@@ -15,8 +15,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* 布局单源 golden 用例(2026-10-03 三视图统一):PNG 与 DiagramVo.positions 共同消费这份几何。
* 数值断言 ±1 容差(int 除法);关系断言(列距/居中/归零)与常量解耦,防调参误报。
* 布局单源 golden 用例(2026-10-03 三视图统一;2026-10-09 竖排改写):PNG 与 DiagramVo.positions
* 共同消费这份几何。数值断言 ±1 容差(int 除法);关系断言(行距/居中/归零)与常量解耦,防调参误报。
*/
class DiagramLayoutServiceTest {
@@ -41,12 +41,16 @@ class DiagramLayoutServiceTest {
return service.layout(nodes, new ArrayList<>(Arrays.asList(edges)));
}
private int cx(int[] b) {
return b[0] + b[2] / 2;
}
private int cy(int[] b) {
return b[1] + b[3] / 2;
}
@Test
void linear_xPitchByColumn_andZeroMin() {
void linear_rowPitchByLevel_centeredAndZeroMin() {
List<FlowModels.NodeVo> nodes = new ArrayList<>(Arrays.asList(
node("start", FlowDefModel.TYPE_START),
node("a", FlowDefModel.TYPE_APPROVAL),
@@ -56,8 +60,16 @@ class DiagramLayoutServiceTest {
for (FlowModels.NodeVo n : nodes) {
assertNotNull(box.get(n.getId()), "缺坐标:" + n.getId());
}
// 同宽节点(审批→审批)x 差 = 列距 SLOT_W+GAP_X(±1 容差)
assertEquals(DiagramLayoutService.SLOT_W + DiagramLayoutService.GAP_X, box.get("b")[0] - box.get("a")[0], 1);
// 纯串行:相邻层 y 差 = 行距 SLOT_H+GAP_Y(±1 容差;顶差仅同盒高成立,跨盒型断中心差),
// 主链各层单列块水平居中 → 中心 x 相等
assertEquals(DiagramLayoutService.SLOT_H + DiagramLayoutService.GAP_Y, box.get("b")[1] - box.get("a")[1], 1);
assertEquals(DiagramLayoutService.SLOT_H + DiagramLayoutService.GAP_Y,
cy(box.get("a")) - cy(box.get("start")), 1);
assertEquals(DiagramLayoutService.SLOT_H + DiagramLayoutService.GAP_Y,
cy(box.get("end")) - cy(box.get("b")), 1);
assertEquals(cx(box.get("start")), cx(box.get("a")), 1);
assertEquals(cx(box.get("a")), cx(box.get("b")), 1);
assertEquals(cx(box.get("b")), cx(box.get("end")), 1);
// 归零:全部坐标非负且最小 x/y 为 0
int minX = Integer.MAX_VALUE;
int minY = Integer.MAX_VALUE;
@@ -71,7 +83,7 @@ class DiagramLayoutServiceTest {
}
@Test
void conditionBranches_sameColumnBlockVerticallyCentered() {
void conditionBranches_sameRowBlockHorizontallyCentered() {
List<FlowModels.NodeVo> nodes = new ArrayList<>(Arrays.asList(
node("start", FlowDefModel.TYPE_START),
node("c", FlowDefModel.TYPE_CONDITION),
@@ -80,10 +92,13 @@ class DiagramLayoutServiceTest {
node("end", FlowDefModel.TYPE_END)));
Map<String, int[]> box = layout(nodes,
edge("start", "c"), edge("c", "a"), edge("c", "b"), edge("a", "end"), edge("b", "end"));
// 两分支同列不同行,列块垂直居中:两盒中心均值 ≈ 内容区中线(单行列的中心同值)
assertEquals(box.get("a")[0], box.get("b")[0]);
assertTrue(box.get("a")[1] != box.get("b")[1]);
assertEquals(cy(box.get("c")), (cy(box.get("a")) + cy(box.get("b"))) / 2, 1);
// 同层分支:顶 y 对齐、各占一列等间距(列距 SLOT_W+GAP_X)
assertEquals(box.get("a")[1], box.get("b")[1]);
assertTrue(box.get("a")[0] != box.get("b")[0]);
assertEquals(DiagramLayoutService.SLOT_W + DiagramLayoutService.GAP_X,
Math.abs(box.get("b")[0] - box.get("a")[0]), 1);
// 整层块水平居中:两分支中心均值 ≈ 单节点层的中心(条件菱形与内容区中线同轴)
assertEquals(cx(box.get("c")), (cx(box.get("a")) + cx(box.get("b"))) / 2, 1);
}
@Test
@@ -98,13 +113,50 @@ class DiagramLayoutServiceTest {
Map<String, int[]> box = layout(nodes,
edge("start", "s"), edge("s", "a"), edge("s", "b"),
edge("a", "j"), edge("b", "j"), edge("j", "end"));
// split 与 join 同一水平中线,且即两分支的对称轴(centerParallelGroups 生效)
assertEquals(cy(box.get("s")), cy(box.get("j")), 1);
assertEquals(cy(box.get("s")), (cy(box.get("a")) + cy(box.get("b"))) / 2, 1);
// 单并行组:分支头同行水平对齐,以 SPLIT 竖直中线左右对称展开;JOIN 水平中点吸附回 SPLIT 中线
assertEquals(box.get("a")[1], box.get("b")[1]);
assertEquals(cx(box.get("s")), (cx(box.get("a")) + cx(box.get("b"))) / 2, 1);
assertEquals(cx(box.get("s")), cx(box.get("j")), 1);
}
@Test
void nestedParallel_groupsCentered_noNegativeY() {
void wideBranches_sixOutlets_spreadHorizontallyWithoutOverlap() {
List<FlowModels.NodeVo> nodes = new ArrayList<>(Arrays.asList(
node("start", FlowDefModel.TYPE_START),
node("c", FlowDefModel.TYPE_CONDITION),
node("a1", FlowDefModel.TYPE_APPROVAL),
node("a2", FlowDefModel.TYPE_APPROVAL),
node("a3", FlowDefModel.TYPE_APPROVAL),
node("a4", FlowDefModel.TYPE_APPROVAL),
node("a5", FlowDefModel.TYPE_APPROVAL),
node("a6", FlowDefModel.TYPE_APPROVAL),
node("end", FlowDefModel.TYPE_END)));
Map<String, int[]> box = layout(nodes,
edge("start", "c"),
edge("c", "a1"), edge("c", "a2"), edge("c", "a3"),
edge("c", "a4"), edge("c", "a5"), edge("c", "a6"),
edge("a1", "end"), edge("a2", "end"), edge("a3", "end"),
edge("a4", "end"), edge("a5", "end"), edge("a6", "end"));
// 6 出口同层:顶 y 对齐、水平等间距展开、两两无重叠、整层块仍居中(首末中心均值=菱形中心)
List<int[]> branches = new ArrayList<>();
for (int i = 1; i <= 6; i++) {
branches.add(box.get("a" + i));
}
branches.sort((x, y) -> Integer.compare(x[0], y[0]));
for (int[] b : branches) {
assertEquals(branches.get(0)[1], b[1]);
}
for (int i = 1; i < branches.size(); i++) {
assertEquals(DiagramLayoutService.SLOT_W + DiagramLayoutService.GAP_X,
branches.get(i)[0] - branches.get(i - 1)[0], 1);
assertTrue(branches.get(i)[0] >= branches.get(i - 1)[0] + branches.get(i - 1)[2],
"分支重叠:" + (branches.get(i)[0] - branches.get(i - 1)[0]));
}
assertEquals(cx(box.get("c")), (cx(branches.get(0)) + cx(branches.get(5))) / 2, 1);
}
@Test
void nestedParallel_groupsCentered_noNegativeX() {
List<FlowModels.NodeVo> nodes = new ArrayList<>(Arrays.asList(
node("start", FlowDefModel.TYPE_START),
node("os", FlowDefModel.TYPE_PARALLEL_SPLIT),
@@ -122,12 +174,12 @@ class DiagramLayoutServiceTest {
edge("p", "ij"), edge("q", "ij"),
edge("ij", "oj"), edge("c", "oj"),
edge("oj", "end"));
// 内层组:is 与 {p,q} 居中;外层组:os/oj 同中线即分支对称轴
assertEquals(cy(box.get("is")), (cy(box.get("p")) + cy(box.get("q"))) / 2, 1);
assertEquals(cy(box.get("os")), cy(box.get("oj")), 1);
// 全图无 y<0(centerParallelGroups 末段抬回)
// 内层组:is 与 {p,q} 居中;外层组:os/oj 同中线即分支对称轴(内层先处理不被外层破坏)
assertEquals(cx(box.get("is")), (cx(box.get("p")) + cx(box.get("q"))) / 2, 1);
assertEquals(cx(box.get("os")), cx(box.get("oj")), 1);
// 全图无 x<0(centerParallelGroups 末段推回)
for (int[] b : box.values()) {
assertTrue(b[1] >= 0, "出现负 y:" + b[1]);
assertTrue(b[0] >= 0, "出现负 x:" + b[0]);
}
}