mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GP-926 - Function Graph - Layouts - Created generic
layout provider to use jung layouts by name
This commit is contained in:
parent
2da717d56a
commit
c8e359ddec
28 changed files with 882 additions and 254 deletions
|
@ -88,7 +88,14 @@ public abstract class AbstractAnimatorJob implements GraphJob {
|
|||
void start() {
|
||||
trace("start() - " + getClass().getSimpleName());
|
||||
|
||||
animator = createAnimator();
|
||||
try {
|
||||
animator = createAnimator();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
Msg.error(this, "Unexepected exception creating animator", t);
|
||||
emergencyFinish();
|
||||
return;
|
||||
}
|
||||
|
||||
trace("\tcreated animator - " + animator);
|
||||
if (animator == null) {
|
||||
|
@ -143,6 +150,27 @@ public abstract class AbstractAnimatorJob implements GraphJob {
|
|||
}
|
||||
}
|
||||
|
||||
private void emergencyFinish() {
|
||||
trace("emergencyFinish()");
|
||||
if (isFinished) {
|
||||
trace("\talready finished");
|
||||
return; // already called
|
||||
}
|
||||
|
||||
isFinished = true;
|
||||
|
||||
// a null listener implies we were shortcut before we were started
|
||||
trace("\tmaybe notify finished...");
|
||||
if (finishedListener != null) {
|
||||
trace("\tlistener is not null--calling");
|
||||
finishedListener.jobFinished(this);
|
||||
}
|
||||
|
||||
if (busyListener != null) {
|
||||
busyListener.setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void stop() {
|
||||
trace("stop()");
|
||||
|
||||
|
|
|
@ -73,8 +73,12 @@ public class FitGraphToViewJob<V extends VisualVertex, E extends VisualEdge<V>>
|
|||
|
||||
@Override
|
||||
public void execute(GraphJobListener listener) {
|
||||
doExecute();
|
||||
listener.jobFinished(this);
|
||||
try {
|
||||
doExecute();
|
||||
}
|
||||
finally {
|
||||
listener.jobFinished(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void doExecute() {
|
||||
|
|
|
@ -40,7 +40,7 @@ public abstract class AbstractLayoutProvider<V extends VisualVertex,
|
|||
E extends VisualEdge<V>,
|
||||
G extends VisualGraph<V, E>>
|
||||
|
||||
implements LayoutProvider<V, E, G> {
|
||||
implements LayoutProviderExtensionPoint<V, E, G> {
|
||||
//@formatter:on
|
||||
|
||||
@Override
|
||||
|
|
|
@ -326,11 +326,12 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
|
|||
condenseVertices(rows, vertexLayoutLocations, transformer, centerX, centerY);
|
||||
}
|
||||
|
||||
Map<E, List<Point2D>> edgeLayoutArticulations = positionEdgeArticulationsInLayoutSpace(
|
||||
transformer, vertexLayoutLocations, edges, layoutLocations);
|
||||
Map<E, List<Point2D>> edgeLayoutArticulations =
|
||||
positionEdgeArticulationsInLayoutSpace(transformer, vertexLayoutLocations, edges,
|
||||
layoutLocations);
|
||||
|
||||
if (isCondensed) {
|
||||
// note: some layouts will not condense the edges, as they perform custom routing
|
||||
// note: some layouts will not condense the edges, as they perform custom routing
|
||||
List<Row<V>> rows = gridLocations.rows();
|
||||
condenseEdges(rows, edgeLayoutArticulations, centerX, centerY);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,14 @@ public class Column {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[x=" + x + ", width=" + width + ", padded width=" +
|
||||
getPaddedWidth(false) + "]";
|
||||
|
||||
//@formatter:off
|
||||
return getClass().getSimpleName() + "{\n" +
|
||||
"\tcolumn: " + index + ",\n" +
|
||||
"\tx: " + x + ",\n" +
|
||||
"\twidth: " + width + ",\n" +
|
||||
"\tpadded width: " + getPaddedWidth(false) + "\n" +
|
||||
"}";
|
||||
//@formatter:on
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import javax.swing.Icon;
|
|||
import ghidra.graph.VisualGraph;
|
||||
import ghidra.graph.viewer.VisualEdge;
|
||||
import ghidra.graph.viewer.VisualVertex;
|
||||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
@ -40,9 +39,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
//@formatter:off
|
||||
public interface LayoutProvider<V extends VisualVertex,
|
||||
E extends VisualEdge<V>,
|
||||
G extends VisualGraph<V, E>>
|
||||
|
||||
extends ExtensionPoint {
|
||||
G extends VisualGraph<V, E>> {
|
||||
//@formatter:on
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/* ###
|
||||
* 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.viewer.layout;
|
||||
|
||||
import ghidra.graph.VisualGraph;
|
||||
import ghidra.graph.viewer.VisualEdge;
|
||||
import ghidra.graph.viewer.VisualVertex;
|
||||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
|
||||
/**
|
||||
* A version of {@link LayoutProvider} that is discoverable at runtime. Layouts that do not wish
|
||||
* to be discoverable should implement {@link LayoutProvider} directly, not this interface.
|
||||
*
|
||||
* @param <V> the vertex type
|
||||
* @param <E> the edge type
|
||||
* @param <G> the graph type
|
||||
*/
|
||||
//@formatter:off
|
||||
public interface LayoutProviderExtensionPoint<V extends VisualVertex,
|
||||
E extends VisualEdge<V>,
|
||||
G extends VisualGraph<V, E>>
|
||||
|
||||
extends LayoutProvider<V, E, G>, ExtensionPoint {
|
||||
//@formatter:on
|
||||
|
||||
}
|
|
@ -30,6 +30,8 @@ import ghidra.graph.viewer.GraphViewerUtils;
|
|||
*
|
||||
* <p>This class maintains a collection of vertices on this row, organized by column index. You
|
||||
* can get the column of a vertex from {@link #getColumn(Object) getColumn(V)}.
|
||||
*
|
||||
* @param <V> the vertex type
|
||||
*/
|
||||
public class Row<V> {
|
||||
|
||||
|
@ -170,9 +172,16 @@ public class Row<V> {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[row=" + index + ", y=" + y + ", height=" + height +
|
||||
", padded height=" + getPaddedHeight(false) + ", column count=" + getColumnCount() +
|
||||
"]";
|
||||
|
||||
//@formatter:off
|
||||
return getClass().getSimpleName() + "{\n" +
|
||||
"\trow: " + index + ",\n" +
|
||||
"\ty: " + y + ",\n" +
|
||||
"\theight: " + height + ",\n" +
|
||||
"\tpadded height: " + getPaddedHeight(false) + ",\n" +
|
||||
"\tcolumn count: " + getColumnCount() + "\n" +
|
||||
"}";
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
|
|
|
@ -20,19 +20,15 @@ import java.awt.geom.GeneralPath;
|
|||
import java.awt.geom.Point2D;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import edu.uci.ics.jung.graph.Graph;
|
||||
import edu.uci.ics.jung.visualization.Layer;
|
||||
import edu.uci.ics.jung.visualization.RenderContext;
|
||||
import ghidra.graph.viewer.*;
|
||||
import ghidra.graph.viewer.edge.VisualEdgeRenderer;
|
||||
import ghidra.graph.viewer.shape.ArticulatedEdgeTransformer;
|
||||
|
||||
public class ArticulatedEdgeRenderer<V extends VisualVertex, E extends VisualEdge<V>>
|
||||
extends VisualEdgeRenderer<V, E> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Shape getEdgeShape(RenderContext<V, E> rc, Graph<V, E> graph, E e, float x1, float y1,
|
||||
float x2, float y2, boolean isLoop, Shape vertexShape) {
|
||||
|
@ -44,18 +40,14 @@ public class ArticulatedEdgeRenderer<V extends VisualVertex, E extends VisualEdg
|
|||
GeneralPath path = new GeneralPath();
|
||||
path.moveTo(x1, y1);
|
||||
|
||||
int offset = 0;
|
||||
Function<? super E, Shape> edgeShapeTransformer = rc.getEdgeShapeTransformer();
|
||||
if (edgeShapeTransformer instanceof ArticulatedEdgeTransformer) {
|
||||
offset = ((ArticulatedEdgeTransformer<V, E>) edgeShapeTransformer).getOverlapOffset(e);
|
||||
}
|
||||
// TODO investigate using the transformer directly
|
||||
// Function<? super E, Shape> edgeShapeTransformer = rc.getEdgeShapeTransformer();
|
||||
|
||||
List<Point2D> articulations = e.getArticulationPoints();
|
||||
offset = updateOffsetForLeftOrRightHandSizeEdge(rc, offset, x1, articulations);
|
||||
for (Point2D point : articulations) {
|
||||
double x = point.getX();
|
||||
double y = point.getY();
|
||||
Point2D offsetPoint = new Point2D.Double(x + offset, y + offset);
|
||||
Point2D offsetPoint = new Point2D.Double(x, y);
|
||||
point = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, offsetPoint);
|
||||
|
||||
x = point.getX();
|
||||
|
@ -69,26 +61,4 @@ public class ArticulatedEdgeRenderer<V extends VisualVertex, E extends VisualEdg
|
|||
|
||||
return path;
|
||||
}
|
||||
|
||||
private int updateOffsetForLeftOrRightHandSizeEdge(RenderContext<V, E> rc, int offset, float x,
|
||||
List<Point2D> articulations) {
|
||||
|
||||
int size = articulations.size();
|
||||
if (size == 0) {
|
||||
// no articulations or start to destination only, with no angles
|
||||
return offset;
|
||||
}
|
||||
|
||||
Point2D start = articulations.get(0);
|
||||
start = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, start);
|
||||
double delta = x - start.getX();
|
||||
if (delta == 0) {
|
||||
// don't move the edge when it is directly below the vertex (this prevents having
|
||||
// a slightly skewed/misaligned edge)
|
||||
return 0;
|
||||
}
|
||||
|
||||
boolean isLeft = delta > 0;
|
||||
return isLeft ? -offset : offset;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ import java.awt.Shape;
|
|||
import java.awt.geom.*;
|
||||
import java.util.List;
|
||||
|
||||
import edu.uci.ics.jung.visualization.decorators.ParallelEdgeShapeTransformer;
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import ghidra.graph.viewer.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
@ -30,25 +31,13 @@ import ghidra.util.SystemUtilities;
|
|||
* @param <E> the edge type
|
||||
*/
|
||||
public class ArticulatedEdgeTransformer<V extends VisualVertex, E extends VisualEdge<V>>
|
||||
extends ParallelEdgeShapeTransformer<V, E> {
|
||||
|
||||
protected static final int OVERLAPPING_EDGE_OFFSET = 10;
|
||||
implements Function<E, Shape> {
|
||||
|
||||
/**
|
||||
* Returns a value by which to offset edges that overlap. This is used to make the edges
|
||||
* easier to see.
|
||||
* Get the shape for this edge
|
||||
*
|
||||
* @param edge the edge
|
||||
* @return the offset value
|
||||
*/
|
||||
public int getOverlapOffset(E edge) {
|
||||
// not sure what the correct default behavior is
|
||||
return OVERLAPPING_EDGE_OFFSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shape for this edge, returning either the shared instance or, in
|
||||
* the case of self-loop edges, the Loop shared instance.
|
||||
* @param e the edge
|
||||
* @return the edge shape
|
||||
*/
|
||||
@Override
|
||||
public Shape apply(E e) {
|
||||
|
@ -76,7 +65,9 @@ public class ArticulatedEdgeTransformer<V extends VisualVertex, E extends Visual
|
|||
final double originX = p1.getX();
|
||||
final double originY = p1.getY();
|
||||
|
||||
int offset = getOverlapOffset(e);
|
||||
// TODO pretty sure this is not needed; keeping for a bit as a reminder of how it used to be
|
||||
// int offset = getOverlapOffset(e);
|
||||
int offset = 0;
|
||||
GeneralPath path = new GeneralPath();
|
||||
path.moveTo(0, 0);
|
||||
for (Point2D pt : articulations) {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package ghidra.graph.viewer.vertex;
|
||||
|
||||
import static ghidra.graph.viewer.GraphViewerUtils.PAINT_ZOOM_THRESHOLD;
|
||||
import static ghidra.graph.viewer.GraphViewerUtils.*;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
|
@ -61,8 +61,9 @@ public class VisualVertexRenderer<V extends VisualVertex, E extends VisualEdge<V
|
|||
public void paintVertex(RenderContext<V, E> rc, Layout<V, E> layout, V vertex) {
|
||||
|
||||
Graph<V, E> graph = layout.getGraph();
|
||||
if (!rc.getVertexIncludePredicate().apply(
|
||||
Context.<Graph<V, E>, V> getInstance(graph, vertex))) {
|
||||
if (!rc.getVertexIncludePredicate()
|
||||
.apply(
|
||||
Context.<Graph<V, E>, V> getInstance(graph, vertex))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue