GP-926 - Function Graph - Layouts - Created generic

layout provider to use jung layouts by name
This commit is contained in:
dragonmacher 2021-05-12 14:53:58 -04:00
parent 2da717d56a
commit c8e359ddec
28 changed files with 882 additions and 254 deletions

View file

@ -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()");

View file

@ -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() {

View file

@ -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

View file

@ -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);
}

View file

@ -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
}
}

View file

@ -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
/**

View file

@ -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
}

View file

@ -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() {

View file

@ -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;
}
}

View file

@ -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) {

View file

@ -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;
}