package application.layouts; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import com.mxgraph.layout.mxGraphLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxGeometry; import com.mxgraph.model.mxIGraphModel; import com.mxgraph.view.mxCellState; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxGraphView; public class DAGLayout extends mxGraphLayout { private mxIGraphModel graphModel; private mxGraphView view; private Map<mxCell, Set<mxCell>> dotEdges; private List<List<mxCell>> lines; private List<Integer> order; private Map<mxCell, List<mxCell>> hierarchicalRecursionOrder; private Set<Integer> usedLineIndex; private List<mxCell> cells; private Set<mxCell> roots; private Map<mxCell, mxCell> myRoot; private Set<mxCell> resources; private Set<mxCell> channels; private Set<mxCell> eventChannels; private Map<mxCell, List<Integer>> vertexToDist; private Map<mxCell, List<Integer>> cellToLineIndex; private Map<mxCell, Map<String, Double>> cellGeo; final double MOVE_X = 100; final double MOVE_Y = 50; final double WIDTH = 80; final double HEIGHT = 30; final double SHIFT = WIDTH / 4; final double SPACE = 10; final double MARGIN_WIDTH = 30; final double MARGIN_HEIGHT = 30; final double delta = Math.pow(10, -3); public DAGLayout(mxGraph graph) { super(graph); graphModel = graph.getModel(); view = graph.getView(); dotEdges = new HashMap<>(); lines = new ArrayList<>(); order = new ArrayList<>(); hierarchicalRecursionOrder = new HashMap<>(); usedLineIndex = new HashSet<>(); cells = new ArrayList<>(); roots = new HashSet<>(); myRoot = new HashMap<>(); resources = new HashSet<>(); channels = new HashSet<>(); eventChannels = new HashSet<>(); vertexToDist = new HashMap<>(); cellToLineIndex = new HashMap<>(); cellGeo = new HashMap<>(); } public void execute(Object parent) { graphModel.beginUpdate(); try { // 繝「繝�繝ォ縺九iresource, channel, eventChannel縺ォ蛻�縺代k for (int i = 0; i < graphModel.getChildCount(parent); i++) { mxCell cell = (mxCell) graphModel.getChildAt(parent, i); if (graphModel.isVertex(cell)) { mxCellState state = view.getState(cell); cells.add(cell); // cell縺罫esource縺ァ縺ゅk if ("ellipse".equals(state.getStyle().get("shape"))) { roots.add(cell); hierarchicalRecursionOrder.put(cell, new ArrayList<>()); familySearch(cell, cell, 0); } if ("rectangle".equals(state.getStyle().get("shape"))) { if ("true".equals(state.getStyle().get("dashed"))) { continue; } // cell縺憩ventChannel縺ァ縺ゅk縺句愛螳� boolean bEventChannel = true; for (int j = 0; j < cell.getEdgeCount(); j++) { mxCell edge = (mxCell) cell.getEdgeAt(j); if (edge.getTarget() == cell) { bEventChannel = false; } } // cell縺憩ventChannel縺ァ縺ゅk if (bEventChannel) { eventChannels.add(cell); // cell縺慶hannel縺ァ縺ゅk } else { channels.add(cell); } hierarchicalRecursionOrder.put(cell, new ArrayList<>()); } } } // 蜈ィ縺ヲ縺ョcell縺ョ蠎ァ讓吶�∝、ァ縺阪&縺ェ縺ゥ繧貞�晄悄蛹� for (mxCell c : cells) { c.getGeometry().setX(0); c.getGeometry().setY(0); if (resources.contains(c)) { view.getState(c).getStyle().put("verticalAlign", "top"); c.getGeometry().setWidth(WIDTH); c.getGeometry().setHeight(HEIGHT); } } // dotEdges繧貞�晄悄蛹� for (mxCell c : cells) { dotEdges.put(c, new HashSet<>()); } // line繧呈ァ狗ッ峨☆繧� for (mxCell ec : eventChannels) { List<mxCell> newline = new ArrayList<mxCell>(); lines.add(newline); constructionLine(ec, 0); } // cell縺ョ霍晞屬繧貞�崎ィ育ョ� reculcDist(); // cell縺悟ア槭☆繧詰ine縺ョindex繧呈アゅa繧� for (int i = 0; i < lines.size(); i++) { for (mxCell cell : lines.get(i)) { if (cellToLineIndex.get(cell) == null) { cellToLineIndex.put(cell, new ArrayList<>()); } cellToLineIndex.get(cell).add(i); } } // 荳ヲ縺ケ繧詰ine縺ョ鬆�逡ェ繧偵た繝シ繝医☆繧� sortLines(); Boolean isVersion1 = true; System.out.println(vertexToDist); // cell繧帝�咲スョ縺吶k if (isVersion1) { // channel縺ョ謇�螳壹�ョ菴咲スョ縺碁嚴螻、讒矩��蜀�驛ィ縺ォ縺ェ繧九→縺阪�∽ク九↓縺壹i縺励※驟咲スョ縺吶k繝ャ繧、繧「繧ヲ繝� layout1((mxCell) parent); } else { // channel繧帝嚴螻、讒矩��蜀�驛ィ縺ァ繧よ園螳壹�ョ菴咲スョ縺ォ驟咲スョ縺吶k繝ャ繧、繧「繧ヲ繝� layout2((mxCell) parent); } } finally { graphModel.endUpdate(); } } // 髫主ア、讒矩��繧貞�榊クー逧�縺ォ謗「邏「 public void familySearch(mxCell r, mxCell p, int d) { int childNum = graphModel.getChildCount(p); myRoot.put(p, r); resources.add(p); if (r != p) { hierarchicalRecursionOrder.get(r).add(p); } for (int i = 0; i < childNum; i++) { mxCell child = (mxCell) graphModel.getChildAt(p, i); cells.add(child); resources.add(child); familySearch(r, child, d + 1); } } // line繧呈ァ狗ッ峨☆繧� public void constructionLine(mxCell cur, int d) { // vertexToDist{key : cell, value : [霍晞屬縺ョ譛�蟆丞�、, 霍晞屬縺ョ譛�螟ァ蛟、]} 繧貞�晄悄蛹� if (vertexToDist.get(cur) == null) { vertexToDist.put(cur, Arrays.asList(d, d)); } else { // 閾ェ霄ォ縺悟性縺セ繧後kline縺ョ荳ュ縺ァ荳�逡ェ螟ァ縺阪>霍晞屬繧剃ソ晏ュ� List<Integer> 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縺ョ蟋狗せ縺ィ縺ェ繧議ell縺ィ縺励※蜿ら�ァ縺輔l縺溷屓謨ー 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縺悟ァ狗せ縺ィ縺ェ繧菊dge縺ョ邨らせ縺ィ縺ェ繧逆arget縺瑚�ェ霄ォ縲√b縺励¥縺ッ蟄伜惠縺励↑縺�蝣エ蜷医r髯、縺� if ((cur != target) && (target != null)) { // edge縺ョ遞ョ鬘槭′"dashed"縺ョ蝣エ蜷� if ("true".equals(state.getStyle().get("dashed"))) { state.getStyle().put("strokeColor", "#800080"); // edge縺ョ濶イ繧貞、画峩 dotEdges.get(cur).add(target); // dotEdges{key : 蟋狗せ縺ィ縺ェ繧議ell, value : 邨らせ縺ィ縺ェ繧議ell} } else { tagCount++; // 蟋狗せ縺ィ縺励※隍�謨ー蝗槫盾辣ァ縺輔l縺ヲ縺�繧句�エ蜷� if (tagCount > 1) { // cur縺セ縺ァ縺ョline縺ョ諠�蝣ア繧貞�肴ァ狗ッ峨@縺ヲ縲√◎縺薙°繧画眠縺励>line繧呈ァ狗ッ峨☆繧� List<mxCell> newline = new ArrayList<mxCell>(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); // 蟋狗せ縺ィ縺励※蛻昴a縺ヲ蜿ら�ァ縺輔l縺溷�エ蜷� } else { constructionLine(target, d + 1); } } } } } // cell縺ョ霍晞屬繧貞�崎ィ育ョ励☆繧� public void reculcDist() { // 霍晞屬縺ョ譖エ譁ー縺瑚。後o繧後↑縺上↑繧九∪縺ァ郢ー繧願ソ斐☆ while (true) { boolean isChanging = false; // 蜷御ク�縺ョline縺ォ蜷ォ縺セ繧後k閾ェ霄ォ縺ョ1縺、謇句燕縺ョ霍晞屬縺ィ閾ェ霄ォ縺ョ霍晞屬繧呈ッ碑シ�縺励※縲∵ュ」縺励>霍晞屬繧定ィ育ョ� for (List<mxCell> 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).get(0) < vertexToDist.get(pre).get(1) + 1) { List<Integer> 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; } } } } // 髫主ア、讒矩��繧定��諷ョ縺励◆cell縺ョ霍晞屬繧定ィ育ョ� for (mxCell r : roots) { for (mxCell c : hierarchicalRecursionOrder.get(r)) { mxCell p = (mxCell) graphModel.getParent(c); if (!vertexToDist.containsKey(c)) { vertexToDist.put(c, Arrays.asList(vertexToDist.get(p).get(0), vertexToDist.get(p).get(0))); } else { if (vertexToDist.get(p).get(0) > vertexToDist.get(c).get(0)) { List<Integer> 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<Integer> dists = vertexToDist.get(p); dists.set(1, vertexToDist.get(c).get(1)); isChanging = true; } } } } // 1蠎ヲ繧ょ、画峩縺檎┌縺�縺ィ繝ォ繝シ繝励r謚懊¢繧� if (!isChanging) { break; } } } // line繧偵た繝シ繝医☆繧�(縺溘□縺励�∝ョ滄圀縺ォ縺ッlines蜀�縺ョ隕∫エ�縺ョ鬆�逡ェ縺ッ蜈・繧梧崛縺医★縲∝他縺ウ蜃コ縺咎��逡ェ繧剃ソ晏ュ倥@縺殪rder繧剃ス懈��) public void sortLines() { for (int i = 0; i < lines.size(); i++) { List<mxCell> line = lines.get(i); // eventChannel縺九i縺ョ蜃コ蜉帛�医′root縺ァ縺ゅk蝣エ蜷� if (roots.contains(line.get(1))) { for (mxCell c : line) { addLine(c); // order縺ォline縺ョindex繧呈�シ邏阪☆繧� } } } } // order縺ォline縺ョindex繧呈�シ邏阪☆繧� public void addLine(mxCell c) { if (cellToLineIndex.get(c) != null) { for (int i : cellToLineIndex.get(c)) { if (!usedLineIndex.contains(i)) { order.add(i); } usedLineIndex.add(i); } } // root縺悟ア槭☆繧詰ine縺ョindex繧呈�シ邏阪@縺溷セ後�∝ュ仙ュォ縺悟ア槭☆繧詰ine縺ョindex繧帝��谺。譬シ邏阪☆繧� for (int i = 0; i < graphModel.getChildCount(c); i++) { mxCell child = (mxCell) graphModel.getChildAt(c, i); addLine(child); } } // cell繧帝�咲スョ縺吶k public void layout1(mxCell s) { /* cell縺ョ蠎ァ讓吶�ッ縲…ell縺ョparent縺ョ蟾ヲ荳翫�ョ蠎ァ讓吶r蝓コ貅悶↓x縺ィy繧偵←繧後□縺代★繧峨☆縺九〒螳夂セゥ縺輔l縺ヲ縺�繧� 繧医▲縺ヲ險育ョ励�ョ驛ス蜷井ク翫�(d=1 縺ョcell(繝ャ繧、繧「繧ヲ繝育判髱「)縺ョ蟾ヲ荳翫r蝓コ貅悶↓縺励◆蠎ァ讓吶rcellGeo縺ォ菫晏ュ倥@縺ヲ縺翫¥ */ Set<mxCell> movedSet = new HashSet<>(); // 驟咲スョ貂医∩縺ョcell繧堤ョ。逅� Map<Integer, List<mxCell>> movedMap = new TreeMap<>(); // 驟咲スョ貂医∩縺ョcell縺ョ霍晞屬縺斐→縺ョ驟咲スョ鬆�繧堤ョ。逅� Map<Integer, Double> distToMaxX = new HashMap<>(); // 霍晞屬縺斐→縺ョcell縺ョ蜿ウ遶ッ縺ョx蠎ァ讓吶�ョ譛�螟ァ蛟、繧堤ョ。逅� double maxY = 0; int maxD = 0; putCellGeo(s, 0, 0); // 霍晞屬縺ョ譛�螟ァ蛟、繧呈アゅa繧� 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 (mxCell cur : lines.get(i)) { // 譌「縺ォ驟咲スョ貂医∩縺ェ繧峨せ繧ュ繝�繝� if (movedSet.contains(cur)) { continue; } 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縺罫oot, channel, eventChannel縺ォ螻槭☆繧議ell縺ァ縺ゅk蝣エ蜷� 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; // 驟咲スョ莠亥ョ壹�ョy蠎ァ讓� double ny = y; double nx = x; // 莠亥ョ壹@縺ヲ縺�繧句コァ讓吶↓驟咲スョ縺吶k縺ィ莉悶�ョ繝ェ繧ス繝シ繧ケ蜀�驛ィ縺ォ驟咲スョ縺輔l縺ヲ縺励∪縺�蝣エ蜷医�∽ク九↓縺壹i縺� 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())) { ny = m.getGeometry().getY() + m.getGeometry().getHeight() + MOVE_Y; } } } // 莠亥ョ壹�ョ蠎ァ讓吶↓驟咲スョ縺励※縲∝推遞ョ蜃ヲ逅�繧定。後≧ 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()); // 霍晞屬縺慧縺ョ譛�螟ァ縺ョx蠎ァ讓吶′譖エ譁ー縺輔l繧句�エ蜷医�∵里縺ォ驟咲スョ貂医∩縺ョcell繧偵◎繧後↓蠢懊§縺ヲ蜿ウ縺ォ縺壹i縺� if (distToMaxX.get(d) < endX) { updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); } maxY = Math.max(maxY, endY); // 驟咲スョ貂医∩縺ョcell縺ョ荳ュ縺ァ縺ョ荳狗ォッ縺ョ蠎ァ讓吶�ョ譛�螟ァ // root縺ョ蟄仙ュォ繧偵∪縺ィ繧√※驟咲スョ縺吶k for (mxCell c : hierarchicalRecursionOrder.get(cur)) { d = vertexToDist.get(c).get(0); nx = x; // 驟咲スョ莠亥ョ壹�ョx蠎ァ讓� mxCell parent = (mxCell) graphModel.getParent(c); // 霍晞屬縺瑚ヲェ縺ィ遲峨@縺�蝣エ蜷医�∬ヲェ縺ョx蠎ァ讓吶h繧雁ー代@螟ァ縺阪¥縺吶k if (d == vertexToDist.get(parent).get(0)) { nx = cellGeo.get(parent).get("x") + MARGIN_WIDTH; } else { nx = distToMaxX.get(d - 1) + MOVE_X; } // 驥阪↑縺」縺ヲ縺励∪縺�蜈�蠑溘Μ繧ス繝シ繧ケ縺悟ュ伜惠縺吶k蝣エ蜷医�∽ク九↓縺壹i縺� 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); // 蟄仙ュォ縺瑚ヲェ縺ョ蜀�驛ィ縺ォ蜷ォ縺セ繧後↑縺�蝣エ蜷医�∬ヲェ縺ョ螟ァ縺阪&繧貞、画峩縺吶k // 霍晞屬縺慧縺ョ譛�螟ァ縺ョx蠎ァ讓吶′譖エ譁ー縺輔l繧句�エ蜷医�∵里縺ォ驟咲スョ貂医∩縺ョcell繧偵◎繧後↓蠢懊§縺ヲ蜿ウ縺ォ縺壹i縺� if (distToMaxX.get(d) < endX) { updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); } maxY = Math.max(maxY, endY); } } } } // 轤ケ邱壹�ョ霎コ縺碁㍾縺ェ縺」縺ヲ縺�繧句�エ蜷医�∝承縺ォ縺壹i縺� for (mxCell c : resources) { for (mxCell cc : dotEdges.get(c)) { for (Map.Entry<mxCell, Set<mxCell>> entry : dotEdges.entrySet()) { mxCell u = entry.getKey(); for (mxCell v : entry.getValue()) { // c-cc, u-v縺悟酔縺賄otEdge繧貞盾辣ァ縺励※縺�繧句�エ蜷� 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()); } } } } } // eventChannel繧貞�コ蜉帛�医�ョy蠎ァ讓吶↓謠�縺医k for (int i : order) { mxCell cur = lines.get(i).get(1); List<mxCell> 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)); } } 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 { 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; } } for (mxCell ec : ecs) { ec.getGeometry().setY(y); graphModel.setGeometry(ec, ec.getGeometry()); cellGeo.get(ec).replace("y", y); y += HEIGHT + SPACE; } } // eventChannel繧馳蠎ァ讓吶↓縺、縺�縺ヲ譏�鬆�縺ォ繧ス繝シ繝医☆繧� List<mxCell> sortedEcs = new ArrayList<>(); Set<mxCell> 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); } // 繧ス繝シ繝医�ョ鬆�逡ェ縺ォ蠢懊§縺ヲ縲�驥阪↑縺」縺ヲ縺�繧菊ventChannel繧剃ク九↓縺壹i縺� 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()); } } } // cell繧帝�咲スョ縺吶k public void layout2(mxCell s) { /* cell縺ョ蠎ァ讓吶�ッ縲…ell縺ョparent縺ョ蟾ヲ荳翫�ョ蠎ァ讓吶r蝓コ貅悶↓x縺ィy繧偵←繧後□縺代★繧峨☆縺九〒螳夂セゥ縺輔l縺ヲ縺�繧� 繧医▲縺ヲ險育ョ励�ョ驛ス蜷井ク翫�(d=1 縺ョcell(繝ャ繧、繧「繧ヲ繝育判髱「)縺ョ蟾ヲ荳翫r蝓コ貅悶↓縺励◆蠎ァ讓吶rcellGeo縺ォ菫晏ュ倥@縺ヲ縺翫¥ */ Set<mxCell> movedSet = new HashSet<>(); // 驟咲スョ貂医∩縺ョcell繧堤ョ。逅� Map<Integer, List<mxCell>> movedMap = new TreeMap<>(); // 驟咲スョ貂医∩縺ョcell縺ョ霍晞屬縺斐→縺ョ驟咲スョ鬆�繧堤ョ。逅� Map<Integer, Double> distToMaxX = new HashMap<>(); // 霍晞屬縺斐→縺ョcell縺ョ蜿ウ遶ッ縺ョx蠎ァ讓吶�ョ譛�螟ァ蛟、繧堤ョ。逅� double maxY = 0; int maxD = 0; putCellGeo(s, 0, 0); // 霍晞屬縺ョ譛�螟ァ蛟、繧呈アゅa繧� 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; } 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縺罫oots縺ィ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()); // 霍晞屬縺慧縺ョ譛�螟ァ縺ョx蠎ァ讓吶′譖エ譁ー縺輔l繧句�エ蜷医�∵里縺ォ驟咲スョ貂医∩縺ョcell繧偵◎繧後↓蠢懊§縺ヲ蜿ウ縺ォ縺壹i縺� if (distToMaxX.get(d) < endX) { updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); } maxY = Math.max(maxY, endY); // 驟咲スョ貂医∩縺ョcell縺ョ荳ュ縺ァ縺ョ荳狗ォッ縺ョ蠎ァ讓吶�ョ譛�螟ァ // root縺ョ蟄仙ュォ繧偵∪縺ィ繧√※驟咲スョ縺吶k for (mxCell c : hierarchicalRecursionOrder.get(cur)) { d = vertexToDist.get(c).get(0); nx = x; // 驟咲スョ莠亥ョ壹�ョx蠎ァ讓� mxCell parent = (mxCell) graphModel.getParent(c); // 霍晞屬縺瑚ヲェ縺ィ遲峨@縺�蝣エ蜷医�∬ヲェ縺ョx蠎ァ讓吶h繧雁ー代@螟ァ縺阪¥縺吶k if (d == vertexToDist.get(parent).get(0)) { nx = cellGeo.get(parent).get("x") + MARGIN_WIDTH; } else { nx = distToMaxX.get(d - 1) + MOVE_X; } // 驥阪↑縺」縺ヲ縺励∪縺�蜈�蠑溘Μ繧ス繝シ繧ケ縺悟ュ伜惠縺吶k蝣エ蜷医�∽ク九↓縺壹i縺� 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); // 蟄仙ュォ縺瑚ヲェ縺ョ蜀�驛ィ縺ォ蜷ォ縺セ繧後↑縺�蝣エ蜷医�∬ヲェ縺ョ螟ァ縺阪&繧貞、画峩縺吶k // 霍晞屬縺慧縺ョ譛�螟ァ縺ョx蠎ァ讓吶′譖エ譁ー縺輔l繧句�エ蜷医�∵里縺ォ驟咲スョ貂医∩縺ョcell繧偵◎繧後↓蠢懊§縺ヲ蜿ウ縺ォ縺壹i縺� if (distToMaxX.get(d) < endX) { updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); } maxY = Math.max(maxY, endY); // 驟咲スョ貂医∩縺ョcell縺ョ荳ュ縺ァ縺ョ荳狗ォッ縺ョ蠎ァ讓吶�ョ譛�螟ァ } } // cur縺慶hannel縺ォ螻槭☆繧九→縺阪�ョ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()); // 霍晞屬縺慧縺ョ譛�螟ァ縺ョx蠎ァ讓吶′譖エ譁ー縺輔l繧句�エ蜷医�∵里縺ォ驟咲スョ貂医∩縺ョcell繧偵◎繧後↓蠢懊§縺ヲ蜿ウ縺ォ縺壹i縺� if (distToMaxX.get(d) < endX) { updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); } maxY = Math.max(maxY, endY); // 驟咲スョ貂医∩縺ョcell縺ョ荳ュ縺ァ縺ョ荳狗ォッ縺ョ蠎ァ讓吶�ョ譛�螟ァ } } } // 轤ケ邱壹�ョ霎コ縺碁㍾縺ェ縺」縺ヲ縺�繧句�エ蜷医�∝承縺ォ縺壹i縺� for (mxCell c : resources) { for (mxCell cc : dotEdges.get(c)) { for (Map.Entry<mxCell, Set<mxCell>> 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()); } } } } } // eventChannel繧貞�コ蜉帛�医�ョy蠎ァ讓吶↓謠�縺医k for (int i : order) { mxCell cur = lines.get(i).get(1); List<mxCell> 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)); } } 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 { 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; } } for (mxCell ec : ecs) { ec.getGeometry().setY(y); graphModel.setGeometry(ec, ec.getGeometry()); cellGeo.get(ec).replace("y", y); y += HEIGHT + SPACE; } } // eventChannel繧馳蠎ァ讓吶↓縺、縺�縺ヲ譏�鬆�縺ォ繧ス繝シ繝医☆繧� List<mxCell> sortedEcs = new ArrayList<>(); Set<mxCell> 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); } // 繧ス繝シ繝医�ョ鬆�逡ェ縺ォ蠢懊§縺ヲ縲�驥阪↑縺」縺ヲ縺�繧菊ventChannel繧剃ク九↓縺壹i縺� 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()); } } } // cell縺ョ蠎ァ讓吶r逋サ骭イ 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); } // movedSet, movedMap縺ォ驟咲スョ貂医∩縺ョcell繧定ソス蜉� public void addMoved(Set<mxCell> ms, Map<Integer, List<mxCell>> mm, mxCell c, int d) { ms.add(c); mm.get(d).add(c); } // 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); double gradient2 = 0; double length2 = Math.pow(u2[0] - v2[0], 2) + Math.pow(u2[1] - v2[1], 2); boolean isVertical = false; if (u1[0] == v1[0]) { isVertical = true; } else { gradient1 = (u1[1] - v1[1]) / (u1[0] - v1[0]); } if (isVertical) { if (u2[0] != v2[0]) { return false; } else { gradient2 = (u2[1] - v2[1]) / (u2[0] - v2[0]); } } else { if (gradient1 - gradient2 > delta) { return false; } } double [][]a = {u1, v1}; double [][]b = {u2, v2}; double maxLength = 0; for (double []c1 : a) { for (double []c2 : b) { double gradient3 = 0; double length3 = Math.pow(c1[0] - c2[0], 2) + Math.pow(c1[1] - c2[1], 2); if(isVertical) { if (c1[0] == c2[0]) { maxLength = Math.max(maxLength, length3); } else { return false; } } else { if (c1[0] == c2[0]) { return false; } else { gradient3 = (c2[1] - c1[1]) / (c2[0] - c1[0]); if (gradient1 - gradient3 > delta) { return false; } maxLength = Math.max(maxLength, length3); } } } } if (length1 + length2 < maxLength) { return false; } else { return true; } } // 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))) { return true; } } return false; } // 豺ア縺輔′d縺ョdistToMaxX縺ォ譖エ譁ー縺悟�・繧九→縲∵キア縺輔′d繧医j螟ァ縺阪>驟咲スョ貂医∩縺ョcell繧貞承縺ォ縺壹i縺� public void updateDistToMaxX(Map<Integer, Double> distToMaxX, Set<mxCell> movedSet, Map<Integer, List<mxCell>> movedMap, int d, int md, double endX) { double preEndX = distToMaxX.get(d); distToMaxX.replace(d, endX); for (int i = d + 1; i <= md; i++) { if (distToMaxX.get(i) == -1) { distToMaxX.replace(i, endX); } } for (Map.Entry<Integer, List<mxCell>> entry : movedMap.entrySet()) { if (entry.getKey() <= d) { continue; } for (mxCell cell : entry.getValue()) { double newX = cellGeo.get(cell).get("x") + endX - preEndX; mxCell cp = (mxCell) cell.getParent(); relocateX(cell, newX, cellGeo.get(cp).get("x")); if (!(roots.contains(cell) || channels.contains(cell))) { mxCell ancestor = (mxCell) graphModel.getParent(cell); resize(ancestor, cellGeo.get(cell).get("x") + cell.getGeometry().getWidth(), cellGeo.get(cell).get("y") + cell.getGeometry().getHeight()); } if (distToMaxX.get(entry.getKey()) < cellGeo.get(cell).get("x") + cell.getGeometry().getWidth()) { distToMaxX.replace(entry.getKey(), cellGeo.get(cell).get("x") + cell.getGeometry().getWidth()); for (int i = entry.getKey() + 1; i <= md; i++) { if (distToMaxX.get(i) == -1) { distToMaxX.replace(i, cellGeo.get(cell).get("x") + cell.getGeometry().getWidth()); } } } } } } // 蟄舌r隕�縺�繧医≧縺ォ隕ェ縺ョ螟ァ縺阪&繧貞、画峩 public double resize(mxCell ancestor, double endX, double endY) { while (true) { boolean isChanging = false; if (ancestor.getGeometry().getWidth() < endX - cellGeo.get(ancestor).get("x") + MARGIN_WIDTH) { ancestor.getGeometry().setWidth(endX - cellGeo.get(ancestor).get("x") + MARGIN_WIDTH); isChanging = true; } if (ancestor.getGeometry().getHeight() < endY - cellGeo.get(ancestor).get("y") + MARGIN_HEIGHT) { ancestor.getGeometry().setHeight(endY - cellGeo.get(ancestor).get("y") + MARGIN_HEIGHT); isChanging = true; } if (isChanging) { graphModel.setGeometry(ancestor, ancestor.getGeometry()); } endX = cellGeo.get(ancestor).get("x") + ancestor.getGeometry().getWidth(); endY = cellGeo.get(ancestor).get("y") + ancestor.getGeometry().getHeight(); if (roots.contains(ancestor)) { break; } ancestor = (mxCell) ancestor.getParent(); } return endY; } // x蠎ァ讓吶r螟画峩 public void relocateX(mxCell c, double nx, double dx) { c.getGeometry().setX(nx - dx); graphModel.setGeometry(c, (mxGeometry) c.getGeometry().clone()); cellGeo.get(c).replace("x", nx); } }