/* * Copyright 2012 Odysseus Software GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.odysseus.ithaka.digraph.layout.sugiyama; import java.util.HashMap; import java.util.Map; import de.odysseus.ithaka.digraph.Digraph; import de.odysseus.ithaka.digraph.DigraphFactory; import de.odysseus.ithaka.digraph.Digraphs; import de.odysseus.ithaka.digraph.DoubledDigraph; import de.odysseus.ithaka.digraph.DoubledDigraphAdapter; import de.odysseus.ithaka.digraph.MapDigraph; import de.odysseus.ithaka.digraph.layout.DigraphLayoutDimensionProvider; /** * Create layout graph and assign layer numbers */ public class SugiyamaStep1<V,E> { private DigraphFactory<Digraph<SugiyamaNode<V>,SugiyamaArc<V,E>>> factory = new DigraphFactory<Digraph<SugiyamaNode<V>,SugiyamaArc<V,E>>>() { @Override public Digraph<SugiyamaNode<V>,SugiyamaArc<V,E>> create() { return new MapDigraph<SugiyamaNode<V>, SugiyamaArc<V,E>>(SugiyamaNode.CMP_ID); } }; public DoubledDigraph<SugiyamaNode<V>,SugiyamaArc<V,E>> createLayoutGraph(Digraph<V,E> graph, DigraphLayoutDimensionProvider<V> dimensions, Digraph<V,?> feedback, int horizontalSpacing) { DoubledDigraph<SugiyamaNode<V>,SugiyamaArc<V,E>> result = DoubledDigraphAdapter.getAdapterFactory(factory).create(); Map<V,SugiyamaNode<V>> map = new HashMap<V, SugiyamaNode<V>>(); // create nodes for (V vertex : graph.vertices()) { SugiyamaNode<V> node = new SugiyamaNode<V>(vertex, dimensions.getDimension(vertex), horizontalSpacing); map.put(vertex, node); result.add(node); } // create arcs for (V source : graph.vertices()) { for (V target : graph.targets(source)) { SugiyamaNode<V> s = map.get(source); SugiyamaNode<V> t = map.get(target); E e = graph.get(source, target); if (feedback.contains(source, target)) { if (graph.contains(target, source)) { result.put(t, s, new SugiyamaArc<V,E>(t, s, e, graph.get(target, source))); } else { result.put(t, s, new SugiyamaArc<V,E>(t, s, true, e)); } } else if (!graph.contains(target, source)) { result.put(s, t, new SugiyamaArc<V,E>(s, t, false, e)); } } } assert Digraphs.isAcyclic(result); computeNodeLayers(result); return result; } protected void computeNodeLayers(DoubledDigraph<SugiyamaNode<V>,SugiyamaArc<V,E>> graph) { int maxLayer = minLayers(graph); shiftDown(graph, 0, maxLayer); } private void shiftDown(DoubledDigraph<SugiyamaNode<V>,SugiyamaArc<V,E>> graph, int minLayer, int maxLayer) { boolean changed; do { changed = false; for (SugiyamaNode<V> node : graph.vertices()) { int minTargetLayer = maxLayer + 1; for (SugiyamaNode<V> target : graph.targets(node)) { minTargetLayer = Math.min(minTargetLayer, target.getLayer()); } assert node.getLayer() < minTargetLayer; if (minTargetLayer <= maxLayer && minTargetLayer > node.getLayer() + 1) { changed = true; node.setLayer(minTargetLayer - 1); } } } while (changed); } @SuppressWarnings("unused") private void shiftUp(DoubledDigraph<SugiyamaNode<V>,SugiyamaArc<V,E>> graph, int minLayer, int maxLayer) { boolean changed; do { changed = false; for (SugiyamaNode<V> node : graph.vertices()) { int maxSourceLayer = minLayer - 1; for (SugiyamaNode<V> source : graph.sources(node)) { maxSourceLayer = Math.max(maxSourceLayer, source.getLayer()); } assert maxSourceLayer < node.getLayer(); if (maxSourceLayer >= minLayer && maxSourceLayer < node.getLayer() - 1) { changed = true; node.setLayer(maxSourceLayer + 1); } } } while (changed); } @SuppressWarnings("unused") private void shiftMiddle(DoubledDigraph<SugiyamaNode<V>,SugiyamaArc<V,E>> graph, int minLayer, int maxLayer) { boolean changed; do { changed = false; for (SugiyamaNode<V> node : graph.vertices()) { int minTargetLayer = maxLayer + 1; for (SugiyamaNode<V> target : graph.targets(node)) { minTargetLayer = Math.min(minTargetLayer, target.getLayer()); } int maxSourceLayer = minLayer - 1; for (SugiyamaNode<V> source : graph.sources(node)) { maxSourceLayer = Math.max(maxSourceLayer, source.getLayer()); } assert maxSourceLayer < node.getLayer() && node.getLayer() < minTargetLayer; if (minTargetLayer <= maxLayer && maxSourceLayer >= minLayer) { // middle int nodeLayer = (maxSourceLayer + minTargetLayer) / 2; if (node.getLayer() != nodeLayer) { changed = true; node.setLayer(nodeLayer); } } else if (minTargetLayer <= maxLayer && minTargetLayer > node.getLayer() + 1) { // down changed = true; node.setLayer(minTargetLayer - 1); } else if (maxSourceLayer >= minLayer && maxSourceLayer < node.getLayer() - 1) { // up changed = true; node.setLayer(maxSourceLayer + 1); } } } while (changed); } /** * Compute minimum layers for nodes * @param graph * @return the maximum layer number */ private int minLayers(Digraph<SugiyamaNode<V>,SugiyamaArc<V,E>> graph) { int maxLayer = 0; for (SugiyamaNode<V> node : graph.vertices()) { if (graph.reverse().getOutDegree(node) == 0) { maxLayer = Math.max(maxLayer, minLayers(graph, node)); } } return maxLayer; } /** * Compute minimum layers for nodes reachable from specified source * @return the maximum layer number */ private int minLayers(Digraph<SugiyamaNode<V>,SugiyamaArc<V,E>> graph, SugiyamaNode<V> source) { int maxLayer = source.getLayer(); for (SugiyamaNode<V> target : graph.targets(source)) { if (target.getLayer() <= source.getLayer()) { target.setLayer(source.getLayer() + 1); maxLayer = Math.max(maxLayer, minLayers(graph, target)); } } return maxLayer; } }