GP-769 - Function Graph - added option to not used dimming for return flow edges

This commit is contained in:
dragonmacher 2021-03-12 15:26:35 -05:00
parent f2e702d1b2
commit e19d59cac7
5 changed files with 103 additions and 62 deletions

View File

@ -840,6 +840,22 @@
location when zooming from the middle-mouse. The default for this option is off, which
triggers zoom to work from the center of the graph, regardless of the mouse location.</P>
<P>The <B>View Settings</B> option describes how the graph will be zoomed when it is first
loaded. The values are:</P>
<UL>
<LI><B>Start Fully Zoomed Out</B> - always start fully zoomed out so that the entire
graph can be seen.</LI>
<LI><B>Start Fully Zoomed In/B> - always start fully zoomed in on the vertex containing
the current location.</LI>
<LI><B>Remember User Settings</B> - keep the zoom level where the user previously left
it.</LI>
</UL>
<BR>
<BR>
<P>There are various edge color and highlight color options available to change. The
highlight colors are those to be used when the flow animations take place.</P>
</BLOCKQUOTE>

View File

@ -49,6 +49,13 @@
notes on how edges are routed for this layout.)
</P>
</BLOCKQUOTE>
<BLOCKQUOTE>
<P>The <B>Use Dim Return Edges</B> option makes default code block return flow edges
lighter than conditional edges. This makes it easier for users to scan the
graph and ignore return flows.
</P>
</BLOCKQUOTE>
</BLOCKQUOTE>

View File

@ -42,7 +42,7 @@ public class FunctionGraphOptions extends VisualGraphOptions {
private static final String EDGE_COLOR_CONDITIONAL_JUMP_KEY = "Edge Color - Conditional Jump ";
//@formatter:off
private static final String NAVIGATION_HISTORY_KEY = "Navigation History";
private static final String NAVIGATION_HISTORY_KEY = "Navigation History";
private static final String NAVIGATION_HISTORY_DESCRIPTION =
"Determines how the navigation history will be updated when using the Function Graph. " +
"The basic options are:" +
@ -185,8 +185,8 @@ public class FunctionGraphOptions extends VisualGraphOptions {
options.registerOption(SCROLL_WHEEL_PANS_KEY, getScrollWheelPans(), help,
SCROLL_WHEEL_PANS_DESCRIPTION);
options.registerOption(GRAPH_BACKGROUND_COLOR_KEY, DEFAULT_GRAPH_BACKGROUND_COLOR,
help, GRAPH_BACKGROUND_COLOR_DESCRPTION);
options.registerOption(GRAPH_BACKGROUND_COLOR_KEY, DEFAULT_GRAPH_BACKGROUND_COLOR, help,
GRAPH_BACKGROUND_COLOR_DESCRPTION);
options.registerOption(DEFAULT_VERTEX_BACKGROUND_COLOR_KEY, DEFAULT_VERTEX_BACKGROUND_COLOR,
help, DEFAULT_VERTEX_BACKGROUND_COLOR_DESCRPTION);

View File

@ -31,7 +31,12 @@ public class DNLayoutOptions implements FGLayoutOptions {
"edges should be routed around any intersecting vertex. When toggled off, edges will " +
"pass through any intersecting vertices.";
private static final String DIM_RETURN_EDGES_KEY = "Use Dim Return Edges";
private static final String DIM_RETURN_EDGES_DESCRIPTION =
"Signals to lighten the default return edges.";
private boolean useEdgeRoutingAroundVertices;
private boolean useDimmedReturnEdges = true;
@Override
public void registerOptions(Options options) {
@ -40,21 +45,32 @@ public class DNLayoutOptions implements FGLayoutOptions {
options.registerOption(USE_EDGE_ROUTING_AROUND_VERTICES_KEY, useEdgeRoutingAroundVertices,
help, USE_EDGE_ROUTING_AROUND_VERTICES_DESCRIPTION);
options.registerOption(DIM_RETURN_EDGES_KEY, useDimmedReturnEdges, help,
DIM_RETURN_EDGES_DESCRIPTION);
}
@Override
public void loadOptions(Options options) {
useEdgeRoutingAroundVertices =
options.getBoolean(USE_EDGE_ROUTING_AROUND_VERTICES_KEY, useEdgeRoutingAroundVertices);
useDimmedReturnEdges = options.getBoolean(DIM_RETURN_EDGES_KEY, useDimmedReturnEdges);
}
public boolean useEdgeRoutingAroundVertices() {
return useEdgeRoutingAroundVertices;
}
public boolean useDimmedReturnEdges() {
return useDimmedReturnEdges;
}
@Override
public boolean optionChangeRequiresRelayout(String optionName) {
// format: 'Nested Code Layout.Route Edges....'
return optionName.endsWith(USE_EDGE_ROUTING_AROUND_VERTICES_KEY);
return optionName.endsWith(USE_EDGE_ROUTING_AROUND_VERTICES_KEY) ||
optionName.endsWith(DIM_RETURN_EDGES_KEY);
}
}

View File

@ -52,19 +52,19 @@ import ghidra.util.task.TaskMonitor;
/**
* A layout that uses the decompiler to show code nesting based upon conditional logic.
*
*
* <p>Edges returning to the default code flow are painted lighter to de-emphasize them. This
* could be made into an option.
*
* <p>Edge routing herein defaults to 'simple routing'; 'complex routing' is a user option.
*
* <p>Edge routing herein defaults to 'simple routing'; 'complex routing' is a user option.
* Simple routing will reduce edge noise as much as possible by combining/overlapping edges that
* flow towards the bottom of the function (returning code flow). Also, edges may fall behind
* vertices for some functions. Complex routing allows the user to visually follow the flow
* of an individual edge. Complex routing will prevent edges from overlapping and will route
* edges around vertices. Simple routing is better when the layout of the vertices is
* important to the user; complex routing is better when edges/relationships are more
* edges around vertices. Simple routing is better when the layout of the vertices is
* important to the user; complex routing is better when edges/relationships are more
* important to the user.
*
*
* TODO ideas:
* -paint fallthrough differently for all, or just for those returning to the baseline
*/
@ -113,7 +113,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
@Override
protected double getCondenseFactor() {
// our layout needs more spacing because we have custom edge routing that we want to
// our layout needs more spacing because we have custom edge routing that we want to
// stand out
return .3;
}
@ -201,7 +201,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
continue;
}
//
//
// Special case: fallthrough--don't label this...not sure how to tell fallthrough. For
// now assume that any column below or backwards is fallthrough. However,
// do label fallthrough if it is the only edge.
@ -231,10 +231,10 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
Map<FGEdge, List<Point2D>> newEdgeArticulations = new HashMap<>();
// Condensing Note: we have guilty knowledge that our parent class my condense the
// Condensing Note: we have guilty knowledge that our parent class my condense the
// vertices and edges towards the center of the graph after we calculate positions.
// To prevent the edges from moving to far behind the vertices, we will compensate a
// bit for that effect using this offset value. The getEdgeOffset() method below is
// bit for that effect using this offset value. The getEdgeOffset() method below is
// updated for the condense factor.
int edgeOffset = isCondensedLayout()
? (int) (VERTEX_TO_EDGE_ARTICULATION_PADDING * (1 - getCondenseFactor()))
@ -242,7 +242,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
Vertex2dFactory vertex2dFactory =
new Vertex2dFactory(transformer, vertexLayoutLocations, layoutToGridMap, edgeOffset);
//
//
// Route our edges!
//
for (FGEdge e : edges) {
@ -275,31 +275,31 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
List<Point2D> articulations = new ArrayList<>();
//
// Basic routing:
// Basic routing:
// -leave the bottom of the start vertex
// -first bend at some constant offset
// -move to right or left, to above the end vertex
// -second bend above the end vertex at previous constant offset
//
// Edges start/end on the vertex center. If we offset them to avoid
//
// Edges start/end on the vertex center. If we offset them to avoid
// overlapping, then they produce angles when only using two articulations.
// Thus, we create articulations that are behind the vertices to remove
// the angles. This points will not be seen.
//
//
//
// Complex routing:
// -this mode will route edges around vertices
//
//
// One goal for complex edge routing is to prevent overlapping (simple edge routing
// prefers overlapping to reduce lines). To prevent overlapping we will use different
// offset x and y values, depending upon the start and end vertex row and column
// offset x and y values, depending upon the start and end vertex row and column
// locations. Specifically, for a given edge direction there will be a bias:
// -Edge to the right - leave from the right; arrive to the left
// -Edge to the left - leave from the left; arrive to the right
// -Edge straight down - go straight down
//
// For each of the above offsets, there will be an amplifier based upon row/column
// distance from start to end vertex. This has the effect that larger vertex
// distance from start to end vertex. This has the effect that larger vertex
// distances will have a larger offset/spacing.
//
@ -335,10 +335,10 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
Vertex2dFactory vertex2dFactory, List<Point2D> articulations) {
//
// For routing to the right and back up we will leave the start vertex from the right side
// For routing to the right and back up we will leave the start vertex from the right side
// and enter the end vertex on the right side. As the vertices get further apart, we will
// space them further in towards the center.
//
// space them further in towards the center.
//
int delta = start.rowIndex - end.rowIndex;
int multiplier = EDGE_ENDPOINT_DISTANCE_MULTIPLIER;
@ -347,10 +347,10 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
}
int distanceSpacing = delta * multiplier;
// Condensing Note: we have guilty knowledge that our parent class my condense the
// Condensing Note: we have guilty knowledge that our parent class my condense the
// vertices and edges towards the center of the graph after we calculate positions.
// To prevent the edges from moving to far behind the vertices, we will compensate a
// bit for that effect using this offset value. The getEdgeOffset() method is
// bit for that effect using this offset value. The getEdgeOffset() method is
// updated for the condense factor.
int exaggerationFactor = 1;
if (isCondensedLayout()) {
@ -369,7 +369,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
y1 = Math.min(y1, startCenterY);
articulations.add(new Point2D.Double(x1, y1)); // point is hidden behind the vertex
// Use the spacing to move the y value towards the top of the vertex. Just like with
// Use the spacing to move the y value towards the top of the vertex. Just like with
// the x value, restrict the y to the range between the edge and the center.
double startRightX = start.getRight();
double x2 = startRightX + VERTEX_BORDER_THICKNESS; // start at the end
@ -434,10 +434,10 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
//
// For routing to the left we will leave the start vertex from just left of center and
// enter the end vertex on the top, towards the right. As the vertices get further apart,
// we will space them further in towards the center of the end vertex. This will keep
// enter the end vertex on the top, towards the right. As the vertices get further apart,
// we will space them further in towards the center of the end vertex. This will keep
// edges with close endpoints from intersecting edges with distant endpoints.
//
//
int delta = end.rowIndex - start.rowIndex;
int multiplier = EDGE_ENDPOINT_DISTANCE_MULTIPLIER;
@ -499,7 +499,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
// enter the end vertex on the left side. As the vertices get further apart, we will
// space them further in towards the center. This will keep edges with close endpoints
// from intersecting edges with distant endpoints.
//
//
int delta = end.rowIndex - start.rowIndex;
if (delta < 0) {
@ -529,7 +529,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
double y1 = start.getY();
articulations.add(new Point2D.Double(x1, y1)); // point is hidden behind the vertex
// Use the spacing to move the y value towards the top of the vertex. Just like with
// Use the spacing to move the y value towards the top of the vertex. Just like with
// the x value, restrict the y to the range between the edge and the center.
double x2 = x1;
double y2 = end.getTop() + VERTEX_BORDER_THICKNESS;
@ -605,10 +605,10 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
int padding = VERTEX_TO_EDGE_AVOIDANCE_PADDING;
int distanceSpacing = padding + delta; // adding the delta makes overlap less likely
// Condensing Note: we have guilty knowledge that our parent class my condense the
// Condensing Note: we have guilty knowledge that our parent class my condense the
// vertices and edges towards the center of the graph after we calculate positions.
// To prevent the edges from moving to far behind the vertices, we will compensate a
// bit for that effect using this offset value. The getEdgeOffset() method is
// bit for that effect using this offset value. The getEdgeOffset() method is
// updated for the condense factor.
int vertexToEdgeOffset = otherVertex.getEdgeOffset();
int exaggerationFactor = 1;
@ -629,20 +629,20 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
// no need to check the 'y' value, as the end vertex is above/below this one
if (vertexClipper.isClippingX(otherVertex, edgeX)) {
/*
/*
Must route around this vertex - new points:
-p1 - just above the intersection point
-p2 - just past the left edge
-p3 - just past the bottom of the vertex
-p4 - back at the original x value
|
.___|
| .-----.
| | |
| '-----'
'---.
|
|
*/
// p1 - same x; y just above vertex
@ -650,19 +650,19 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
double y = vertexClipper.getTopOffset(otherVertex, vertexToEdgeOffset);
articulations.add(new Point2D.Double(x, y));
// Maybe merge points if they are too close together. Visually, many lines
// moving around intersecting vertices looks busy. When the intersecting
// Maybe merge points if they are too close together. Visually, many lines
// moving around intersecting vertices looks busy. When the intersecting
// vertices are close together, we remove some of the articulations in order to
// smooth out the edges.
if (articulations.size() > 2) {
/*
/*
The last articulation is the one added before this method was called, which
lies just below the intersecting vertex. The articulation before that is
the one that is the one that is sending the x value straight into the
lies just below the intersecting vertex. The articulation before that is
the one that is the one that is sending the x value straight into the
intersecting vertex. Delete that point as well so that the entire edge is
shifted to the outside of the intersecting vertex. This will get repeated
for each vertex that is intersecting.
for each vertex that is intersecting.
*/
Point2D previousArticulation = articulations.get(articulations.size() - 2);
int closenessHeight = 50;
@ -720,7 +720,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
Point2D first = new Point2D.Double(x, y1);
articulations.add(first);
// loop second point - same y coord as destination;
// loop second point - same y coord as destination;
// x is the col after the outermost dominated vertex
Point2D endVertexPoint = end.center;
@ -733,6 +733,10 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
private void lighten(FGEdge e) {
if (!getLayoutOptions().useDimmedReturnEdges()) {
return;
}
// assumption: edges that move to the left in this layout are return flows that happen
// after the code block has been executed. We dim those a bit so that they
// produce less clutter.
@ -840,8 +844,11 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
BlockCopy copy = (BlockCopy) child;
StringBuilder buffy = new StringBuilder();
buffy.append(printDepth(depth, depth + 1)).append(' ').append(ID).append(
" plain - ").append(copy.getRef());
buffy.append(printDepth(depth, depth + 1))
.append(' ')
.append(ID)
.append(" plain - ")
.append(copy.getRef());
debug(buffy.toString());
}
@ -958,7 +965,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
//==================================================================================================
// Inner Classes
//==================================================================================================
//==================================================================================================
/**
* Encapsulates knowledge of edge direction (up/down, left/right) and uses that knowledge
@ -1060,7 +1067,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
}
/**
* A class that represents 2D information about the contained vertex, such as location,
* A class that represents 2D information about the contained vertex, such as location,
* bounds, row and column of the layout grid.
*/
private class Vertex2d {
@ -1207,8 +1214,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
int row = startRow;
for (int i = 0; i < allChildren.size(); i++) {
DecompilerBlock block = allChildren.get(i);
for (DecompilerBlock block : allChildren) {
if (block instanceof DecompilerBlockGraph) {
row = ((DecompilerBlockGraph) block).setRows(row);
}
@ -1229,8 +1235,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
String getChildrenString(int depth) {
StringBuilder buffy = new StringBuilder();
int childCount = 0;
for (int i = 0; i < allChildren.size(); i++) {
DecompilerBlock block = allChildren.get(i);
for (DecompilerBlock block : allChildren) {
if (block instanceof DecompilerBlockGraph) {
String blockName = block.getName();
@ -1315,8 +1320,8 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
@Override
DecompilerBlock getBlock(FGVertex vertex) {
//
// Note: we currently allow grouping in this layout. When we search for a vertex,
// we have to check each vertex inside of the given group *and* each vertex
// Note: we currently allow grouping in this layout. When we search for a vertex,
// we have to check each vertex inside of the given group *and* each vertex
// inside of the vertex that belongs to this decompiler block.
//
if (vertex instanceof GroupedFunctionGraphVertex) {
@ -1447,9 +1452,8 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
// The 'list' structure for children's nesting:
// -all nodes are at the same level
//
for (int i = 0; i < allChildren.size(); i++) {
for (DecompilerBlock block : allChildren) {
int column = col;
DecompilerBlock block = allChildren.get(i);
block.setCol(column);
}
@ -1477,8 +1481,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
// -each successive condition is another level nested
//
int column = col;
for (int i = 0; i < allChildren.size(); i++) {
DecompilerBlock block = allChildren.get(i);
for (DecompilerBlock block : allChildren) {
block.setCol(column);
column++;
}
@ -1515,11 +1518,10 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
//
// The 'do' structure for children's nesting:
// -all blocks nested
// -all blocks nested
//
int column = col + 1;
for (int i = 0; i < allChildren.size(); i++) {
DecompilerBlock block = allChildren.get(i);
for (DecompilerBlock block : allChildren) {
block.setCol(column);
}