diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java index 08739af..29c0dda 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java @@ -377,6 +377,18 @@ Map channelsOut = new HashMap<>(); Map resources = new HashMap<>(); + // create resource vertices + for (ResourceNode resNode: dataFlowGraph.getRootResourceNodes()) { + int w = 80; + int h = 30; + ResourcePath res = resNode.getOutSideResource(); + Object resource = graph.insertVertex(parent, null, + res.getResourceName(), 20, 20, w, h, + "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex + resources.put(System.identityHashCode(res), resource); + getChildResource(resource, resNode, resources, w, h); + } + // create channel vertices for (ChannelNode c: dataFlowGraph.getRootChannelNodes()) { DataTransferChannel channel = (DataTransferChannel) c.getChannel(); @@ -446,18 +458,6 @@ } } } - - // create resource vertices - for (ResourceNode resNode: dataFlowGraph.getRootResourceNodes()) { - int w = 80; - int h = 30; - ResourcePath res = resNode.getOutSideResource(); - Object resource = graph.insertVertex(parent, null, - res.getResourceName(), 20, 20, w, h, - "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex - resources.put(System.identityHashCode(res), resource); - getChildResource(resource, resNode, resources, w, h); - } // add input, output and reference edges for (Edge edge: dataFlowGraph.getEdges()) { @@ -501,11 +501,6 @@ public void getChildResource(Object resource, ResourceNode resNode, Map resources, int w, int h) { for (ResourceNode c: resNode.getChildren()) { -// w = w - 100; -// if(w <= 0) { -// w = 100; -// } -// h = (2 * w) / 3; ResourcePath chRes = c.getOutSideResource(); Object chResource = graph.insertVertex(resource, null, chRes.getName(), 0, 0, w, h, diff --git a/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java b/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java index ddb1428..7ced584 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java +++ b/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java @@ -1,6 +1,7 @@ package application.layouts; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -30,7 +31,7 @@ private Set resources; private Set channels; private Set eventChannels; - private Map vertexToDist; + private Map> vertexToDist; private Map> cellToLineIndex; private Map> cellGeo; final double MOVE_X = 100; @@ -67,7 +68,7 @@ public void execute(Object parent) { graphModel.beginUpdate(); try { - // ���f������resource, channel, eventChannel�ɕ����� + // モデルからresource, channel, eventChannelに分ける for (int i = 0; i < graphModel.getChildCount(parent); i++) { mxCell cell = (mxCell) graphModel.getChildAt(parent, i); @@ -75,7 +76,7 @@ mxCellState state = view.getState(cell); cells.add(cell); - // cell��resource�ł��� + // cellがresourceである if ("ellipse".equals(state.getStyle().get("shape"))) { roots.add(cell); hierarchicalRecursionOrder.put(cell, new ArrayList<>()); @@ -87,31 +88,28 @@ continue; } - // cell��eventChannel�ł��� - if (cell.getEdgeCount() == 1) { - eventChannels.add(cell); - hierarchicalRecursionOrder.put(cell, new ArrayList<>()); - } else { - // cell��channel�ł��� - boolean bEventChannel = true; - for (int j = 0; j < cell.getEdgeCount(); j++) { - mxCell edge = (mxCell) cell.getEdgeAt(j); - if (edge.getTarget() == cell) { - bEventChannel = false; - } + // cellがeventChannelであるか判定 + boolean bEventChannel = true; + for (int j = 0; j < cell.getEdgeCount(); j++) { + mxCell edge = (mxCell) cell.getEdgeAt(j); + if (edge.getTarget() == cell) { + bEventChannel = false; } - if (bEventChannel) { - eventChannels.add(cell); - } else { - channels.add(cell); - } - hierarchicalRecursionOrder.put(cell, new ArrayList<>()); } + + // cellがeventChannelである + if (bEventChannel) { + eventChannels.add(cell); + // cellがchannelである + } else { + channels.add(cell); + } + hierarchicalRecursionOrder.put(cell, new ArrayList<>()); } } } - // �S�Ă�cell�̍��W�A�傫���Ȃǂ������� + // 全てのcellの座標、大きさなどを初期化 for (mxCell c : cells) { c.getGeometry().setX(0); c.getGeometry().setY(0); @@ -123,22 +121,22 @@ } } - // dotEdges�������� + // dotEdgesを初期化 for (mxCell c : cells) { dotEdges.put(c, new HashSet<>()); } - // line���\�z���� + // lineを構築する for (mxCell ec : eventChannels) { List newline = new ArrayList(); lines.add(newline); constructionLine(ec, 0); } - // cell�̐[�����Čv�Z + // cellの距離を再計算 reculcDist(); - // cell��������line��index�����߂� + // cellが属するlineのindexを求める for (int i = 0; i < lines.size(); i++) { for (mxCell cell : lines.get(i)) { if (cellToLineIndex.get(cell) == null) { @@ -148,18 +146,26 @@ } } - // ���ׂ�line�̏��Ԃ��\�[�g���� + // 並べるlineの順番をソートする sortLines(); - // cell��z�u���� - layout((mxCell) parent); + Boolean isVersion1 = false; + System.out.println(vertexToDist); + // cellを配置する + if (isVersion1) { + // channelの所定の位置が階層構造内部になるとき、下にずらして配置するレイアウト + layout1((mxCell) parent); + } else { + // channelを階層構造内部でも所定の位置に配置するレイアウト + layout2((mxCell) parent); + } } finally { graphModel.endUpdate(); } } - // �K�w�\�����ċA�I�ɒT�� + // 階層構造を再帰的に探索 public void familySearch(mxCell r, mxCell p, int d) { int childNum = graphModel.getChildCount(p); @@ -177,42 +183,44 @@ } } - // line���\�z���� + // lineを構築する public void constructionLine(mxCell cur, int d) { - // vertexToDist{key : cell, value : �[��} �������� + // vertexToDist{key : cell, value : [距離の最小値, 距離の最大値]} を初期化 if (vertexToDist.get(cur) == null) { - vertexToDist.put(cur, d); + vertexToDist.put(cur, Arrays.asList(d, d)); } else { - // ���g���܂܂��line�̒��ň�ԑ傫���[����ۑ� - vertexToDist.put(cur, Math.max(vertexToDist.get(cur), d)); + // 自身が含まれるlineの中で一番大きい距離を保存 + List dists = vertexToDist.get(cur); + dists.set(0, Math.max(dists.get(0), d)); + dists.set(1, dists.get(0)); } lines.get(lines.size() - 1).add(cur); - int tagCount = 0; // edge�̎n�_�ƂȂ�cell�Ƃ��ĎQ�Ƃ��ꂽ�� + int tagCount = 0; // edgeの始点となるcellとして参照された回数 for (int i = 0; i < cur.getEdgeCount(); i++) { mxCell edge = (mxCell) cur.getEdgeAt(i); mxCellState state = view.getState(edge); mxCell target = (mxCell) edge.getTarget(); - // cur���n�_�ƂȂ�edge�̏I�_�ƂȂ�target�����g�A�������͑��݂��Ȃ��ꍇ������ + // curが始点となるedgeの終点となるtargetが自身、もしくは存在しない場合を除く if ((cur != target) && (target != null)) { - // edge�̎�ނ�"dashed"�̏ꍇ + // edgeの種類が"dashed"の場合 if ("true".equals(state.getStyle().get("dashed"))) { - state.getStyle().put("strokeColor", "#800080"); // edge�̐F��ύX - dotEdges.get(cur).add(target); // dotEdges{key : �n�_�ƂȂ�cell, value : �I�_�ƂȂ�cell} + state.getStyle().put("strokeColor", "#800080"); // edgeの色を変更 + dotEdges.get(cur).add(target); // dotEdges{key : 始点となるcell, value : 終点となるcell} } else { tagCount++; - // �n�_�Ƃ��ĕ�����Q�Ƃ���Ă���ꍇ + // 始点として複数回参照されている場合 if (tagCount > 1) { - // cur�܂ł�line�̏����č\�z���āA��������V����line���\�z���� + // curまでのlineの情報を再構築して、そこから新しいlineを構築する List newline = new ArrayList(lines.get(lines.size() - 1)); while (newline.get(newline.size() - 1).getId() != cur.getId()) { newline.remove(newline.size() - 1); } lines.add(newline); constructionLine(target, d + 1); - // �n�_�Ƃ��ď��߂ĎQ�Ƃ��ꂽ�ꍇ + // 始点として初めて参照された場合 } else { constructionLine(target, d + 1); } @@ -221,63 +229,73 @@ } } - // cell�̐[�����Čv�Z���� - public void reculcDist() { - boolean isChanging = false; - - // �[���̍X�V���s���Ȃ��Ȃ�܂ŌJ��Ԃ� + // cellの距離を再計算する + public void reculcDist() { + // 距離の更新が行われなくなるまで繰り返す while (true) { - // �����line�Ɋ܂܂�鎩�g��1�Ž�O�̐[���Ǝ��g�̐[�����r���āA�������[�����v�Z + boolean isChanging = false; + + // 同一のlineに含まれる自身の1つ手前の距離と自身の距離を比較して、正しい距離を計算 for (List line : lines) { for (int i = 1; i < line.size(); i++) { mxCell cur = line.get(i); mxCell pre = line.get(i - 1); if (!(resources.contains(cur) && resources.contains(pre))) { - if (vertexToDist.get(cur) < vertexToDist.get(pre) + 1) { - vertexToDist.replace(cur, vertexToDist.get(pre) + 1); + if (vertexToDist.get(cur).get(0) < vertexToDist.get(pre).get(1) + 1) { + List dists = vertexToDist.get(cur); + dists.set(0, Math.max(dists.get(0), vertexToDist.get(pre).get(1) + 1)); + dists.set(1, Math.max(dists.get(0), dists.get(1))); isChanging = true; } } } } - // �K�w�\�����l������cell�̐[�����v�Z + // 階層構造を考慮したcellの距離を計算 for (mxCell r : roots) { for (mxCell c : hierarchicalRecursionOrder.get(r)) { mxCell p = (mxCell) graphModel.getParent(c); if (!vertexToDist.containsKey(c)) { - vertexToDist.put(c, vertexToDist.get(p)); + vertexToDist.put(c, Arrays.asList(vertexToDist.get(p).get(0), vertexToDist.get(p).get(0))); } else { - if (vertexToDist.get(p) > vertexToDist.get(c)) { - vertexToDist.replace(c, vertexToDist.get(p)); + if (vertexToDist.get(p).get(0) > vertexToDist.get(c).get(0)) { + List dists = vertexToDist.get(c); + dists.set(0, vertexToDist.get(p).get(0)); + dists.set(1, Math.max(dists.get(0), dists.get(1))); + isChanging = true; + } + + if (vertexToDist.get(p).get(1) < vertexToDist.get(c).get(1)) { + List dists = vertexToDist.get(p); + dists.set(1, vertexToDist.get(c).get(1)); isChanging = true; } } } } - // 1�x���ύX�������ƃ��[�v�𔲂��� + // 1度も変更が無いとループを抜ける if (!isChanging) { break; } } } - // line���\�[�g����(�������A���ۂɂ�lines���̗v�f�̏��Ԃ͓���ւ����A�Ăяo�����Ԃ�ۑ�����order���쐬) + // lineをソートする(ただし、実際にはlines内の要素の順番は入れ替えず、呼び出す順番を保存したorderを作成) public void sortLines() { for (int i = 0; i < lines.size(); i++) { List line = lines.get(i); - // eventChannel����̏o�͐悪root�ł���ꍇ + // eventChannelからの出力先がrootである場合 if (roots.contains(line.get(1))) { for (mxCell c : line) { - addLine(c); // order��line��index���i�[���� + addLine(c); // orderにlineのindexを格納する } } } } - // order��line��index���i�[���� + // orderにlineのindexを格納する public void addLine(mxCell c) { if (cellToLineIndex.get(c) != null) { for (int i : cellToLineIndex.get(c)) { @@ -288,33 +306,32 @@ } } - // root��������line��index���i�[������A�q����������line��index�������i�[���� + // rootが属するlineのindexを格納した後、子孫が属するlineのindexを順次格納する for (int i = 0; i < graphModel.getChildCount(c); i++) { mxCell child = (mxCell) graphModel.getChildAt(c, i); addLine(child); } } - // cell��z�u���� - public void layout(mxCell s) { - /* cell�̍��W�́Acell��parent�̍���̍��W�����x��y���ǂꂾ�����炷���Œ�`����Ă��� - ����Čv�Z�̓s����Aid=1 ��cell(���C�A�E�g���)�̍������ɂ������W��ۑ����Ă��� */ + // cellを配置する + public void layout1(mxCell s) { + /* cellの座標は、cellのparentの左上の座標を基準にxとyをどれだけずらすかで定義されている + よって計算の都合上、id=1 のcell(レイアウト画面)の左上を基準にした座標をcellGeoに保存しておく */ - Set movedSet = new HashSet<>(); - List movedList = new ArrayList<>(); - Map> movedMap = new TreeMap<>(); - Map distToMaxX = new HashMap<>(); + Set movedSet = new HashSet<>(); // 配置済みのcellを管理 + Map> movedMap = new TreeMap<>(); // 配置済みのcellの距離ごとの配置順を管理 + Map distToMaxX = new HashMap<>(); // 距離ごとのcellの右端のx座標の最大値を管理 double maxY = 0; int maxD = 0; putCellGeo(s, 0, 0); - // �[���̍ő�l�����߂� + // 距離の最大値を求める for (mxCell c : cells) { - maxD = Math.max(maxD, vertexToDist.get(c)); + maxD = Math.max(maxD, vertexToDist.get(c).get(1)); } - // ������ + // 初期化 distToMaxX.put(-1, 0.0); for (int d = 0; d <= maxD; d++) { movedMap.put(d, new ArrayList<>()); @@ -322,28 +339,28 @@ } for (int i : order) { - double centerY = -1; // �z�u�\���y���W(����) + double centerY = -1; // 配置予定のy座標(中央) for (mxCell cur : lines.get(i)) { - // ���ɔz�u�ς݂Ȃ�X�L�b�v + // 既に配置済みならスキップ if (movedSet.contains(cur)) { continue; } - int d = vertexToDist.get(cur); // cur�̐[�� - double x = distToMaxX.get(d - 1) + MOVE_X; // �z�u�\���x���W - double endX = 0; // �z�u����cell�̉E�[��x���W - double endY = 0; // �z�u����cell�̉��[��y���W + int d = vertexToDist.get(cur).get(0); // curの距離 + double x = distToMaxX.get(d - 1) + MOVE_X; // 配置予定のx座標 + double endX = 0; // 配置したcellの右端のx座標 + double endY = 0; // 配置したcellの下端のy座標 - // cur��root, channel, eventChannel�ɑ�����cell�ł���ꍇ + // curがroot, channel, eventChannelに属するcellである場合 if (roots.contains(cur) || channels.contains(cur) || eventChannels.contains(cur)) { if (centerY == -1) { centerY = maxY + MOVE_Y + cur.getGeometry().getHeight() / 2; } - double y = centerY - cur.getGeometry().getHeight() / 2; // �z�u�\���y���W + double y = centerY - cur.getGeometry().getHeight() / 2; // 配置予定のy座標 double ny = y; double nx = x; - // �\�肵�Ă�����W�ɔz�u����Ƒ��̃��\�[�X�����ɔz�u����Ă��܂��ꍇ�A���ɂ��炷 + // 予定している座標に配置すると他のリソース内部に配置されてしまう場合、下にずらす for (mxCell m : movedSet) { if (roots.contains(m) || channels.contains(m)) { if (cellOverlap(m.getGeometry().getX(), m.getGeometry().getY(), m.getGeometry().getWidth(), m.getGeometry().getHeight(), x, y, cur.getGeometry().getWidth(), cur.getGeometry().getHeight())) { @@ -352,35 +369,35 @@ } } - // �\��̍��W�ɔz�u���āA�e�폈�����s�� + // 予定の座標に配置して、各種処理を行う cur.getGeometry().setX(nx); cur.getGeometry().setY(ny); endX = nx + cur.getGeometry().getWidth(); endY = ny + cur.getGeometry().getHeight(); putCellGeo(cur, nx, ny); - addMoved(movedSet, movedList, movedMap, cur, d); + addMoved(movedSet, movedMap, cur, d); graphModel.setGeometry(cur, (mxGeometry) cur.getGeometry().clone()); - // �[����d�̍ő��x���W���X�V�����ꍇ�A���ɔz�u�ς݂�cell������ɉ����ĉE�ɂ��炷 + // 距離がdの最大のx座標が更新される場合、既に配置済みのcellをそれに応じて右にずらす if (distToMaxX.get(d) < endX) { updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); } - maxY = Math.max(maxY, endY); // �z�u�ς݂�cell�̒��ł̉��[�̍��W�̍ő� + maxY = Math.max(maxY, endY); // 配置済みのcellの中での下端の座標の最大 - // root�̎q�����܂Ƃ߂Ĕz�u���� + // rootの子孫をまとめて配置する for (mxCell c : hierarchicalRecursionOrder.get(cur)) { - d = vertexToDist.get(c); - nx = x; // �z�u�\���x���W + d = vertexToDist.get(c).get(0); + nx = x; // 配置予定のx座標 mxCell parent = (mxCell) graphModel.getParent(c); - // �[�����e�Ɠ������ꍇ�A�e��x���W��菭���傫������ - if (d == vertexToDist.get(parent)) { + // 距離が親と等しい場合、親のx座標より少し大きくする + if (d == vertexToDist.get(parent).get(0)) { nx = cellGeo.get(parent).get("x") + MARGIN_WIDTH; } else { nx = distToMaxX.get(d - 1) + MOVE_X; } - // �z�u�ς݂Ő[�����������Z�킪�v��ꍇ�A���ɂ��炷 + // 重なってしまう兄弟リソースが存在する場合、下にずらす double brotherMaxY = MOVE_Y; for (int k = 0; k < graphModel.getChildCount(parent); k++) { mxCell child = (mxCell) graphModel.getChildAt(parent, k); @@ -388,24 +405,24 @@ continue; } - if (movedSet.contains(child) && (d == vertexToDist.get(child))) { + if (movedSet.contains(child) && (vertexToDist.get(child).get(0) <= d && d <= vertexToDist.get(child).get(1))) { brotherMaxY = Math.max(brotherMaxY, child.getGeometry().getY() + child.getGeometry().getHeight() + SPACE); } } - // �\��̍��W�ɔz�u���āA�e�폈�����s�� + // 予定の座標に配置して、各種処理を行う c.getGeometry().setX(nx - cellGeo.get(parent).get("x")); c.getGeometry().setY(brotherMaxY); graphModel.setGeometry(c, (mxGeometry) c.getGeometry().clone()); - addMoved(movedSet, movedList, movedMap, c, d); + addMoved(movedSet, movedMap, c, d); putCellGeo(c, nx, cellGeo.get(parent).get("y") + brotherMaxY); endX = cellGeo.get(c).get("x") + c.getGeometry().getWidth(); endY = cellGeo.get(c).get("y") + c.getGeometry().getHeight(); - endY = resize(parent, endX, endY); // �q�����e�̓����Ɋ܂܂�Ȃ��ꍇ�A�e�̑傫����ύX���� + endY = resize(parent, endX, endY); // 子孫が親の内部に含まれない場合、親の大きさを変更する - // �[����d�̍ő��x���W���X�V�����ꍇ�A���ɔz�u�ς݂�cell������ɉ����ĉE�ɂ��炷 - if (distToMaxX.get(vertexToDist.get(c)) < endX) { + // 距離がdの最大のx座標が更新される場合、既に配置済みのcellをそれに応じて右にずらす + if (distToMaxX.get(d) < endX) { updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); } maxY = Math.max(maxY, endY); @@ -414,12 +431,17 @@ } } - // �_���̕ӂ��d�Ȃ��Ă���ꍇ�A�E�ɂ��炷 + // 点線の辺が重なっている場合、右にずらす for (mxCell c : resources) { for (mxCell cc : dotEdges.get(c)) { for (Map.Entry> entry : dotEdges.entrySet()) { mxCell u = entry.getKey(); for (mxCell v : entry.getValue()) { + // c-cc, u-vが同じdotEdgeを参照している場合 + if ((c == u && cc == v) || (c == v && cc == u)) { + continue; + } + double []posC = {cellGeo.get(c).get("x") + c.getGeometry().getWidth() / 2, cellGeo.get(c).get("y") + c.getGeometry().getHeight() / 2}; double []posCC = {cellGeo.get(cc).get("x") + cc.getGeometry().getWidth() / 2, cellGeo.get(cc).get("y") + cc.getGeometry().getHeight() / 2}; double []posU = {cellGeo.get(u).get("x") + u.getGeometry().getWidth() / 2, cellGeo.get(u).get("y") + u.getGeometry().getHeight() / 2}; @@ -438,7 +460,7 @@ } } - // eventChannel���o�͐��y���W�ɑ����� + // eventChannelを出力先のy座標に揃える for (int i : order) { mxCell cur = lines.get(i).get(1); List ecs = new ArrayList<>(); @@ -471,87 +493,279 @@ } } - // ����resource�ɕ�����eventChannel���o�͂��Ă���ꍇ�A�d�Ȃ��Ă��܂��̂ʼn��ɂ��炷 - Map> ecToLineIndex = new HashMap<>(); - for (mxCell ec : eventChannels) { - ecToLineIndex.put(ec, new ArrayList<>()); - } - for (int i = 0; i < lines.size(); i++) { - mxCell ec = lines.get(i).get(0); - ecToLineIndex.get(ec).add(i); + // eventChannelをy座標について昇順にソートする + List sortedEcs = new ArrayList<>(); + Set usedEcs = new HashSet<>(); + for (int i = 0; i < eventChannels.size(); i++) { + mxCell minEc = movedMap.get(0).get(0); + double ecMinY = Double.MAX_VALUE; + for (mxCell ec : movedMap.get(0)) { + if (usedEcs.contains(ec)) { + continue; + } + if (cellGeo.get(ec).get("y") < ecMinY) { + minEc = ec; + ecMinY = cellGeo.get(ec).get("y"); + } + } + sortedEcs.add(minEc); + usedEcs.add(minEc); } - Set usedEventChannels = new HashSet<>(); - List sortedEventChannels = new ArrayList<>(); - List eventChannelOut = new ArrayList<>(); - for (int i : order) { - mxCell cur = lines.get(i).get(1); - mxCell ec = lines.get(i).get(0); - if (!roots.contains(cur) || usedEventChannels.contains(ec)) { - continue; + // ソートの順番に応じて、重なっているeventChannelを下にずらす + for (int i = 1; i < sortedEcs.size(); i++) { + mxCell cur = sortedEcs.get(i); + mxCell pre = sortedEcs.get(i - 1); + + if (cellGeo.get(cur).get("y") < cellGeo.get(pre).get("y") + pre.getGeometry().getHeight() + SPACE) { + double dy = cellGeo.get(pre).get("y") + pre.getGeometry().getHeight() + SPACE - cellGeo.get(cur).get("y"); + cur.getGeometry().setY(cur.getGeometry().getY() + dy); + cellGeo.get(cur).replace("y", cellGeo.get(cur).get("y") + dy); + graphModel.setGeometry(cur, cur.getGeometry()); } - - sortedEventChannels.add(ec); - eventChannelOut.add(cur); - usedEventChannels.add(ec); - - for (mxCell c : hierarchicalRecursionOrder.get(cur)) { - if (!cellToLineIndex.containsKey(c)) { + } + } + + // cellを配置する + public void layout2(mxCell s) { + /* cellの座標は、cellのparentの左上の座標を基準にxとyをどれだけずらすかで定義されている + よって計算の都合上、id=1 のcell(レイアウト画面)の左上を基準にした座標をcellGeoに保存しておく */ + + Set movedSet = new HashSet<>(); // 配置済みのcellを管理 + Map> movedMap = new TreeMap<>(); // 配置済みのcellの距離ごとの配置順を管理 + Map distToMaxX = new HashMap<>(); // 距離ごとのcellの右端のx座標の最大値を管理 + double maxY = 0; + int maxD = 0; + + putCellGeo(s, 0, 0); + + // 距離の最大値を求める + for (mxCell c : cells) { + maxD = Math.max(maxD, vertexToDist.get(c).get(1)); + } + + // 初期化 + distToMaxX.put(-1, 0.0); + for (int d = 0; d <= maxD; d++) { + movedMap.put(d, new ArrayList<>()); + distToMaxX.put(d, -1.0); + } + + for (int i : order) { + double centerY = -1; // 配置予定のy座標(中央) + for (int j = 0; j < lines.get(i).size(); j++) { + mxCell cur = lines.get(i).get(j); + + // 既に配置済みならスキップ + if (movedSet.contains(cur)) { continue; } - for (int j : cellToLineIndex.get(c)) { - if (c == lines.get(j).get(1)) { - if (!usedEventChannels.contains(lines.get(j).get(0))) { - sortedEventChannels.add(lines.get(j).get(0)); - eventChannelOut.add(cur); - usedEventChannels.add(lines.get(j).get(0)); + int d = vertexToDist.get(cur).get(0); // curの距離 + double x = distToMaxX.get(d - 1) + MOVE_X; // 配置予定のx座標 + double endX = 0; // 配置したcellの右端のx座標 + double endY = 0; // 配置したcellの下端のy座標 + + // curがrootsとeventChannelに属するときのcellの配置 + if (roots.contains(cur) || eventChannels.contains(cur)) { + if (centerY == -1) { + centerY = maxY + MOVE_Y + cur.getGeometry().getHeight() / 2; + } + double y = centerY - cur.getGeometry().getHeight() / 2; // 配置予定のy座標 + double ny = y; + double nx = x; + + // 予定の座標に配置して、各種処理を行う + cur.getGeometry().setX(nx); + cur.getGeometry().setY(ny); + endX = nx + cur.getGeometry().getWidth(); + endY = ny + cur.getGeometry().getHeight(); + putCellGeo(cur, nx, ny); + addMoved(movedSet, movedMap, cur, d); + graphModel.setGeometry(cur, (mxGeometry) cur.getGeometry().clone()); + + // 距離がdの最大のx座標が更新される場合、既に配置済みのcellをそれに応じて右にずらす + if (distToMaxX.get(d) < endX) { + updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); + } + maxY = Math.max(maxY, endY); // 配置済みのcellの中での下端の座標の最大 + + // rootの子孫をまとめて配置する + for (mxCell c : hierarchicalRecursionOrder.get(cur)) { + d = vertexToDist.get(c).get(0); + nx = x; // 配置予定のx座標 + mxCell parent = (mxCell) graphModel.getParent(c); + + // 距離が親と等しい場合、親のx座標より少し大きくする + if (d == vertexToDist.get(parent).get(0)) { + nx = cellGeo.get(parent).get("x") + MARGIN_WIDTH; + } else { + nx = distToMaxX.get(d - 1) + MOVE_X; + } + + // 重なってしまう兄弟リソースが存在する場合、下にずらす + double brotherMaxY = MOVE_Y; + for (int k = 0; k < graphModel.getChildCount(parent); k++) { + mxCell child = (mxCell) graphModel.getChildAt(parent, k); + if (child == c) { + continue; + } + + if (movedSet.contains(child) && (vertexToDist.get(child).get(0) <= d && d <= vertexToDist.get(child).get(1))) { + brotherMaxY = Math.max(brotherMaxY, child.getGeometry().getY() + child.getGeometry().getHeight() + SPACE); + } + } + + // 予定の座標に配置して、各種処理を行う + c.getGeometry().setX(nx - cellGeo.get(parent).get("x")); + c.getGeometry().setY(brotherMaxY); + graphModel.setGeometry(c, (mxGeometry) c.getGeometry().clone()); + addMoved(movedSet, movedMap, c, d); + putCellGeo(c, nx, cellGeo.get(parent).get("y") + brotherMaxY); + + endX = cellGeo.get(c).get("x") + c.getGeometry().getWidth(); + endY = cellGeo.get(c).get("y") + c.getGeometry().getHeight(); + endY = resize(parent, endX, endY); // 子孫が親の内部に含まれない場合、親の大きさを変更する + + // 距離がdの最大のx座標が更新される場合、既に配置済みのcellをそれに応じて右にずらす + if (distToMaxX.get(d) < endX) { + updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); + } + maxY = Math.max(maxY, endY); // 配置済みのcellの中での下端の座標の最大 + } + } + + // curがchannelに属するときのcellの配置 + if (channels.contains(cur)) { + mxCell pre = lines.get(i).get(j - 1); + centerY = cellGeo.get(pre).get("y") + pre.getGeometry().getHeight() / 2; + double y = centerY - cur.getGeometry().getHeight() / 2; + + cur.getGeometry().setX(x); + cur.getGeometry().setY(y); + endX = x + cur.getGeometry().getWidth(); + endY = y + cur.getGeometry().getHeight(); + putCellGeo(cur, x, y); + addMoved(movedSet, movedMap, cur, d); + graphModel.setGeometry(cur, (mxGeometry) cur.getGeometry().clone()); + + // 距離がdの最大のx座標が更新される場合、既に配置済みのcellをそれに応じて右にずらす + if (distToMaxX.get(d) < endX) { + updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); + } + maxY = Math.max(maxY, endY); // 配置済みのcellの中での下端の座標の最大 + } + } + } + + // 点線の辺が重なっている場合、右にずらす + for (mxCell c : resources) { + for (mxCell cc : dotEdges.get(c)) { + for (Map.Entry> entry : dotEdges.entrySet()) { + mxCell u = entry.getKey(); + for (mxCell v : entry.getValue()) { + if ((c == u && cc == v) || (c == v && cc == u)) { + continue; + } + + double []posC = {cellGeo.get(c).get("x") + c.getGeometry().getWidth() / 2, cellGeo.get(c).get("y") + c.getGeometry().getHeight() / 2}; + double []posCC = {cellGeo.get(cc).get("x") + cc.getGeometry().getWidth() / 2, cellGeo.get(cc).get("y") + cc.getGeometry().getHeight() / 2}; + double []posU = {cellGeo.get(u).get("x") + u.getGeometry().getWidth() / 2, cellGeo.get(u).get("y") + u.getGeometry().getHeight() / 2}; + double []posV = {cellGeo.get(v).get("x") + v.getGeometry().getWidth() / 2, cellGeo.get(v).get("y") + v.getGeometry().getHeight() / 2}; + + if (isStraightLine(posC, posCC, posU, posV)) { + c.getGeometry().setX(c.getGeometry().getX() + SHIFT); + cellGeo.get(c).replace("x", cellGeo.get(c).get("x")); + cc.getGeometry().setX(cc.getGeometry().getX() + SHIFT); + cellGeo.get(cc).replace("x", cellGeo.get(cc).get("x") + SHIFT); + graphModel.setGeometry(c, c.getGeometry()); + graphModel.setGeometry(cc, cc.getGeometry()); } } } } } - for (int i = 1; i < sortedEventChannels.size(); i++) { - mxCell pre = sortedEventChannels.get(i - 1); - mxCell preOut = eventChannelOut.get(i - 1); - mxCell cur = sortedEventChannels.get(i); - mxCell curOut = eventChannelOut.get(i); - double dy = 0; + // eventChannelを出力先のy座標に揃える + for (int i : order) { + mxCell cur = lines.get(i).get(1); + List ecs = new ArrayList<>(); + for (int j : cellToLineIndex.get(cur)) { + if (!ecs.contains(lines.get(j).get(0)) && cur == lines.get(j).get(1)) { + ecs.add(lines.get(j).get(0)); + } + } - if (myRoot.get(preOut) == myRoot.get(curOut)) { - dy = SPACE; + double centerY = cellGeo.get(cur).get("y") + cur.getGeometry().getHeight() / 2; + double y = 0; + if (ecs.size() % 2 != 0) { + y = centerY - ecs.size()*HEIGHT / 2 - ((ecs.size() - 1) / 2)*SPACE; } else { - dy = MOVE_Y; + y = centerY - ecs.size()*HEIGHT / 2 - (ecs.size() - 1)*SPACE / 2; + } + + for (int j : order) { + mxCell other = lines.get(j).get(0); + if (ecs.contains(other)) { + continue; + } } - if (cellGeo.get(cur).get("y") < cellGeo.get(pre).get("y") + pre.getGeometry().getHeight() + dy) { - cur.getGeometry().setY(cellGeo.get(pre).get("y") + pre.getGeometry().getHeight() + dy); - cellGeo.get(cur).replace("y", cellGeo.get(pre).get("y") + pre.getGeometry().getHeight() + dy); + for (mxCell ec : ecs) { + ec.getGeometry().setY(y); + graphModel.setGeometry(ec, ec.getGeometry()); + cellGeo.get(ec).replace("y", y); + y += HEIGHT + SPACE; } + } + + // eventChannelをy座標について昇順にソートする + List sortedEcs = new ArrayList<>(); + Set usedEcs = new HashSet<>(); + for (int i = 0; i < eventChannels.size(); i++) { + mxCell minEc = movedMap.get(0).get(0); + double ecMinY = Double.MAX_VALUE; + for (mxCell ec : movedMap.get(0)) { + if (usedEcs.contains(ec)) { + continue; + } + if (cellGeo.get(ec).get("y") < ecMinY) { + minEc = ec; + ecMinY = cellGeo.get(ec).get("y"); + } + } + sortedEcs.add(minEc); + usedEcs.add(minEc); + } + + // ソートの順番に応じて、重なっているeventChannelを下にずらす + for (int i = 1; i < sortedEcs.size(); i++) { + mxCell cur = sortedEcs.get(i); + mxCell pre = sortedEcs.get(i - 1); - if (cellGeo.get(cur).get("y") + cur.getGeometry().getHeight() / 2 == cellGeo.get(curOut).get("y") + curOut.getGeometry().getHeight() / 2 - && vertexToDist.get(curOut) != 1) { - cur.getGeometry().setY(cellGeo.get(cur).get("y") + cur.getGeometry().getHeight() + dy); - cellGeo.get(cur).replace("y", cellGeo.get(cur).get("y") + cur.getGeometry().getHeight() + dy); + if (cellGeo.get(cur).get("y") < cellGeo.get(pre).get("y") + pre.getGeometry().getHeight() + SPACE) { + double dy = cellGeo.get(pre).get("y") + pre.getGeometry().getHeight() + SPACE - cellGeo.get(cur).get("y"); + cur.getGeometry().setY(cur.getGeometry().getY() + dy); + cellGeo.get(cur).replace("y", cellGeo.get(cur).get("y") + dy); + graphModel.setGeometry(cur, cur.getGeometry()); } - graphModel.setGeometry(cur, cur.getGeometry()); } } + // cellの座標を登録 public void putCellGeo(mxCell c, double x, double y) { cellGeo.put(c, new HashMap<>()); cellGeo.get(c).put("x", x); cellGeo.get(c).put("y", y); } - public void addMoved(Set ms, List ml, Map> mm, mxCell c, int d) { + // movedSet, movedMapに配置済みのcellを追加 + public void addMoved(Set ms, Map> mm, mxCell c, int d) { ms.add(c); - ml.add(c); mm.get(d).add(c); } - // 2�‚̕ӂ��d�Ȃ��Ă��邩���� + // 2つの辺が重なっているか判定 public boolean isStraightLine(double []u1, double []v1, double []u2, double []v2) { double gradient1 = 0; double length1 = Math.pow(u1[0] - v1[0], 2) + Math.pow(u1[1] - v1[1], 2); @@ -612,7 +826,7 @@ } } - // 2�‚�resource���d�Ȃ��Ă��邩���� + // 2つのcellが重なっているか判定 public boolean cellOverlap(double ax, double ay, double aw, double ah, double bx, double by, double bw, double bh) { if (((ax <= bx) && (bx <= ax + aw)) || ((bx <= ax) && (ax <= bx + bw))) { if (((ay <= by) && (by <= ay + ah)) || ((by <= ay) && (ay <= by + bh))) { @@ -622,7 +836,7 @@ return false; } - // �[�����Ƃ̍ő��x���W���ύX���ꂽ�Ƃ��̏��� + // 深さがdのdistToMaxXに更新が入ると、深さがdより大きい配置済みのcellを右にずらす public void updateDistToMaxX(Map distToMaxX, Set movedSet, Map> movedMap, int d, int md, double endX) { double preEndX = distToMaxX.get(d); distToMaxX.replace(d, endX); @@ -658,7 +872,7 @@ } } - // �e�̑傫����ύX + // 子を覆うように親の大きさを変更 public double resize(mxCell ancestor, double endX, double endY) { while (true) { boolean isChanging = false; @@ -687,7 +901,7 @@ return endY; } - // x���W��ύX + // x座標を変更 public void relocateX(mxCell c, double nx, double dx) { c.getGeometry().setX(nx - dx); graphModel.setGeometry(c, (mxGeometry) c.getGeometry().clone());