From d3217c96b875930d9b1cbb6fd77586901b9ff000 Mon Sep 17 00:00:00 2001 From: ghidravore Date: Mon, 9 Nov 2020 12:27:45 -0500 Subject: [PATCH] added post process function for vertex overlap in tree layouts that are used with graphs that are not trees. --- .../LayoutTransitionManager.java | 9 ++- .../visualization/PostProcessRunnable.java | 80 +++++++++++++++++++ .../src/main/resources/jungrapht.properties | 2 +- 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/PostProcessRunnable.java diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/LayoutTransitionManager.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/LayoutTransitionManager.java index 8f89c5d3f6..ccaf19f064 100644 --- a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/LayoutTransitionManager.java +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/LayoutTransitionManager.java @@ -106,6 +106,8 @@ class LayoutTransitionManager { } if (layoutAlgorithm instanceof TreeLayout) { ((TreeLayout) layoutAlgorithm).setRootPredicate(rootPredicate); + layoutAlgorithm.setAfter(new PostProcessRunnable<>( + visualizationServer.getVisualizationModel().getLayoutModel())); } // remove any previously added layout paintables removePaintable(radialLayoutRings); @@ -127,7 +129,9 @@ class LayoutTransitionManager { if (layoutAlgorithm instanceof EdgeSorting) { ((EdgeSorting) layoutAlgorithm).setEdgeComparator(edgeComparator); } - LayoutAlgorithmTransition.apply(visualizationServer, layoutAlgorithm); + LayoutAlgorithmTransition.apply(visualizationServer, + layoutAlgorithm, + new PostProcessRunnable<>(visualizationServer.getVisualizationModel().getLayoutModel())); } private void removePaintable(VisualizationServer.Paintable paintable) { @@ -146,6 +150,9 @@ class LayoutTransitionManager { .setRootPredicate(rootPredicate); ((TreeLayout) initialLayoutAlgorithm) .setVertexBoundsFunction(vertexBoundsFunction); + initialLayoutAlgorithm.setAfter(new PostProcessRunnable<>( + visualizationServer.getVisualizationModel().getLayoutModel())); + } if (initialLayoutAlgorithm instanceof EdgeSorting) { ((EdgeSorting) initialLayoutAlgorithm) diff --git a/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/PostProcessRunnable.java b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/PostProcessRunnable.java new file mode 100644 index 0000000000..da03727fc4 --- /dev/null +++ b/Ghidra/Features/GraphServices/src/main/java/ghidra/graph/visualization/PostProcessRunnable.java @@ -0,0 +1,80 @@ +/* ### + * IP: GHIDRA + * + * 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 ghidra.graph.visualization; + +import java.util.*; + +import org.jgrapht.Graph; +import org.jungrapht.visualization.layout.model.LayoutModel; + +/** + * to post-process tree layouts to move vertices that overlap a vertical edge that + * is not incident on the vertex. + * This can be removed after jungrapht-layout-1.1 + * @param vertex type + * @param edge type + */ +public class PostProcessRunnable implements Runnable { + + LayoutModel layoutModel; + + public PostProcessRunnable(LayoutModel layoutModel) { + this.layoutModel = layoutModel; + } + + @Override + public void run() { + moveVerticesThatOverlapVerticalEdges(layoutModel); + } + + protected int moveVerticesThatOverlapVerticalEdges(LayoutModel layoutModel) { + int offset = 100; + int moved = 0; + Graph graph = layoutModel.getGraph(); + Map> verticalEdgeMap = new LinkedHashMap<>(); + graph.edgeSet() + .stream() + .filter(e -> layoutModel.apply(graph.getEdgeSource(e)).x == layoutModel + .apply(graph.getEdgeTarget(e)).x) + .forEach(e -> verticalEdgeMap + .computeIfAbsent(layoutModel.apply(graph.getEdgeSource(e)).x, + k -> new HashSet<>()) + .add(e)); + + for (V v : graph.vertexSet()) { + double x = layoutModel.apply(v).x; + for (E edge : verticalEdgeMap.getOrDefault(x, Collections.emptySet())) { + V source = graph.getEdgeSource(edge); + V target = graph.getEdgeTarget(edge); + if (!v.equals(source) && !v.equals(target)) { + double lowy = layoutModel.apply(source).y; + double hiy = layoutModel.apply(target).y; + if (lowy > hiy) { + double temp = lowy; + lowy = hiy; + hiy = temp; + } + double vy = layoutModel.apply(v).y; + if (lowy <= vy && vy <= hiy) { + layoutModel.set(v, layoutModel.apply(v).add(offset, 0)); + moved++; + } + } + } + } + return moved; + } +} diff --git a/Ghidra/Features/GraphServices/src/main/resources/jungrapht.properties b/Ghidra/Features/GraphServices/src/main/resources/jungrapht.properties index 843e9f086b..d05dea0cc3 100644 --- a/Ghidra/Features/GraphServices/src/main/resources/jungrapht.properties +++ b/Ghidra/Features/GraphServices/src/main/resources/jungrapht.properties @@ -61,7 +61,7 @@ jungrapht.circle.reduceEdgeCrossingMaxEdges=200 jungrapht.initialDimensionVertexDensity=0.3f jungrapht.minScale=0.001 -jungrapht.maxScale=1.0 +jungrapht.maxScale=4.0 # not using spatial data structures for vertices at this time. May remove after jungrapht 1.1 jungrapht.vertexSpatialSupport=NONE