From e646deabc1beb21dc596597121cf17a4d03fce39 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Mon, 12 Aug 2019 08:03:56 -0400 Subject: [PATCH] GT-3020 - Function Graph - fixed edge visual state that was being overwritten after mutation operations --- .../core/functiongraph/graph/FGEdge.java | 34 +++++++++ .../core/functiongraph/graph/FGEdgeImpl.java | 15 +++- .../job/AbstractGroupingFunctionGraphJob.java | 20 ++++-- .../util/job/MergeVertexFunctionGraphJob.java | 20 +++--- .../util/job/SplitVertexFunctionGraphJob.java | 30 +++++++- .../FunctionGraphGroupVertices1Test.java | 35 ++++++++- .../graph/layout/TestFGLayoutProvider.java | 4 +- ...ava => DNLArticulatedEdgeTransformer.java} | 2 +- .../graph/layout/DecompilerNestedLayout.java | 6 +- .../graph/graphs/DefaultVisualGraph.java | 8 +-- .../ghidra/graph/viewer/GraphViewerUtils.java | 5 +- .../java/ghidra/graph/viewer/VisualEdge.java | 10 +-- .../graph/viewer/edge/VisualEdgeRenderer.java | 72 ++++++++++--------- .../edge/VisualGraphPathHighlighter.java | 26 +++++-- .../renderer/ArticulatedEdgeRenderer.java | 4 ++ .../shape/ArticulatedEdgeTransformer.java | 44 ++++++++---- 16 files changed, 242 insertions(+), 93 deletions(-) rename Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/jung/renderer/{DecompilerDominanceArticulatedEdgeTransformer.java => DNLArticulatedEdgeTransformer.java} (89%) diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/FGEdge.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/FGEdge.java index 459740c9c0..03a1bf3225 100644 --- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/FGEdge.java +++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/FGEdge.java @@ -19,6 +19,16 @@ import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex; import ghidra.graph.viewer.VisualEdge; import ghidra.program.model.symbol.FlowType; +/** + * This version of the {@link VisualEdge} adds a few methods. + * + *

The {@link #setDefaultAlpha(double)} method was added here instead of the base interface, as it + * was not needed any higher at the time of writing. It can be pulled-up, but there is most + * likely a better pattern for specifying visual attributes of an edge. If we find we need more + * methods like this, then that is a good time for a refactor to change how we manipulate + * rending attributes from various parts of the API (e.g., from the layouts and from animation + * jobs). + */ public interface FGEdge extends VisualEdge { public FlowType getFlowType(); @@ -27,6 +37,30 @@ public interface FGEdge extends VisualEdge { public void setLabel(String label); + /** + * Set this edge's base alpha, which determines how much of the edge is visible/see through. + * 0 is completely transparent. + * + *

This differs from {@link #setAlpha(double)} in that the latter is used for + * temporary display effects. This method is used to set the alpha value for the edge when + * it is not part of a temporary display effect. + * + * @param alpha the alpha value + */ + public void setDefaultAlpha(double alpha); + + /** + * Set this edge's base alpha, which determines how much of the edge is visible/see through. + * 0 is completely transparent. + * + *

This differs from {@link #getAlpha()} in that the latter is used for + * temporary display effects. This method is used to set the alpha value for the edge when + * it is not part of a temporary display effect. + * + * @return the alpha value + */ + public double getDefaultAlpha(); + @SuppressWarnings("unchecked") // Suppressing warning on the return type; we know our class is the right type @Override diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/FGEdgeImpl.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/FGEdgeImpl.java index 491239ae5b..1024265903 100644 --- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/FGEdgeImpl.java +++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/FGEdgeImpl.java @@ -38,7 +38,8 @@ public class FGEdgeImpl implements FGEdge { private boolean inFocusedPath = false; private boolean selected = false; private double emphasis = 0D; - private double alpha = 1D; + private double defaultAlpha = 1D; + private double alpha = defaultAlpha; private String edgeLabel = null; public FGEdgeImpl(FGVertex startVertex, FGVertex destinationVertex, FlowType flowType, @@ -100,6 +101,17 @@ public class FGEdgeImpl implements FGEdge { return alpha; } + @Override + public void setDefaultAlpha(double alpha) { + this.defaultAlpha = alpha; + this.alpha = alpha; + } + + @Override + public double getDefaultAlpha() { + return defaultAlpha; + } + @Override public List getArticulationPoints() { return layoutArticulationPoints; @@ -146,6 +158,7 @@ public class FGEdgeImpl implements FGEdge { newEdge.layoutArticulationPoints = newPoints; newEdge.alpha = alpha; + newEdge.defaultAlpha = defaultAlpha; newEdge.inHoveredPath = inHoveredPath; newEdge.inFocusedPath = inFocusedPath; newEdge.selected = selected; diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/util/job/AbstractGroupingFunctionGraphJob.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/util/job/AbstractGroupingFunctionGraphJob.java index 16535bb732..43aba57889 100644 --- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/util/job/AbstractGroupingFunctionGraphJob.java +++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/util/job/AbstractGroupingFunctionGraphJob.java @@ -68,7 +68,7 @@ public abstract class AbstractGroupingFunctionGraphJob extends AbstractFunctionG */ AbstractGroupingFunctionGraphJob(FGController controller, GroupedFunctionGraphVertex groupVertex, Set newVertices, - Set verticesToRemove, boolean relayloutOverride, boolean useAnimation) { + Set verticesToRemove, boolean relayoutOverride, boolean useAnimation) { super(controller, useAnimation); @@ -87,7 +87,7 @@ public abstract class AbstractGroupingFunctionGraphJob extends AbstractFunctionG FunctionGraphOptions options = controller.getFunctionGraphOptions(); RelayoutOption relayoutOption = options.getRelayoutOption(); this.relayout = relayoutOption == VERTEX_GROUPING_CHANGES || relayoutOption == ALWAYS || - relayloutOverride; + relayoutOverride; } @Override @@ -158,7 +158,7 @@ public abstract class AbstractGroupingFunctionGraphJob extends AbstractFunctionG return positions; } - /** + /* * Subclasses must return locations for vertices. This method will be called when no * relayout will be performed. * @@ -191,15 +191,21 @@ public abstract class AbstractGroupingFunctionGraphJob extends AbstractFunctionG @Override protected void updateOpacity(double percentComplete) { + double oldComponentsAlpha = 1.0 - percentComplete; Collection vertices = getVerticesToBeRemoved(); for (FGVertex vertex : vertices) { + vertex.setAlpha(oldComponentsAlpha); Collection edges = getEdges(vertex); for (FGEdge edge : edges) { - edge.setAlpha(oldComponentsAlpha); + + // don't go past the alpha when removing + double defaultAlpha = edge.getDefaultAlpha(); + double alpha = Math.min(oldComponentsAlpha, defaultAlpha); + edge.setAlpha(alpha); } } @@ -210,7 +216,11 @@ public abstract class AbstractGroupingFunctionGraphJob extends AbstractFunctionG Collection edges = getEdges(vertex); for (FGEdge edge : edges) { - edge.setAlpha(newComponentsAlpha); + + // don't go past the alpha when adding + double defaultAlpha = edge.getDefaultAlpha(); + double alpha = Math.min(newComponentsAlpha, defaultAlpha); + edge.setAlpha(alpha); } } } diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/util/job/MergeVertexFunctionGraphJob.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/util/job/MergeVertexFunctionGraphJob.java index a434da90d1..357430b21f 100644 --- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/util/job/MergeVertexFunctionGraphJob.java +++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/util/job/MergeVertexFunctionGraphJob.java @@ -19,6 +19,7 @@ import java.awt.Rectangle; import java.awt.geom.Point2D; import java.util.*; +import org.apache.commons.collections4.IterableUtils; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.interpolation.PropertySetter; @@ -197,22 +198,25 @@ public class MergeVertexFunctionGraphJob extends AbstractAnimatorJob { parentVertex.setAlpha(oldComponentsAlpha); childVertex.setAlpha(oldComponentsAlpha); - Collection edges = getEdges(parentVertex); + Iterable edges = + IterableUtils.chainedIterable(getEdges(parentVertex), getEdges(childVertex)); for (FGEdge edge : edges) { - edge.setAlpha(oldComponentsAlpha); - } - edges = getEdges(childVertex); - for (FGEdge edge : edges) { - edge.setAlpha(oldComponentsAlpha); + // don't go past the alpha when removing + double defaultAlpha = edge.getDefaultAlpha(); + double alpha = Math.min(oldComponentsAlpha, defaultAlpha); + edge.setAlpha(alpha); } double newComponentsAlpha = percentComplete; mergedVertex.setAlpha(newComponentsAlpha); - edges = getEdges(mergedVertex); for (FGEdge edge : edges) { - edge.setAlpha(newComponentsAlpha); + + // don't go past the alpha when adding + double defaultAlpha = edge.getDefaultAlpha(); + double alpha = Math.min(newComponentsAlpha, defaultAlpha); + edge.setAlpha(alpha); } } diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/util/job/SplitVertexFunctionGraphJob.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/util/job/SplitVertexFunctionGraphJob.java index a0d8d25cda..6880a0ea07 100644 --- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/util/job/SplitVertexFunctionGraphJob.java +++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/util/job/SplitVertexFunctionGraphJob.java @@ -19,6 +19,7 @@ import java.awt.Rectangle; import java.awt.geom.Point2D; import java.util.*; +import org.apache.commons.collections4.IterableUtils; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.interpolation.PropertySetter; @@ -114,9 +115,21 @@ public class SplitVertexFunctionGraphJob extends AbstractAnimatorJob { controller.synchronizeProgramLocationAfterEdit(); + restoreEdgeDisplayAttributes(); + viewer.repaint(); } + private void restoreEdgeDisplayAttributes() { + + Iterable edges = + IterableUtils.chainedIterable(getEdges(parentVertex), getEdges(childVertex)); + for (FGEdge edge : edges) { + double alpha = edge.getDefaultAlpha(); + edge.setAlpha(alpha); + } + } + public void setPercentComplete(double percentComplete) { trace("setPercentComplete() callback: " + percentComplete); updateNewVertexPositions(percentComplete); @@ -214,7 +227,11 @@ public class SplitVertexFunctionGraphJob extends AbstractAnimatorJob { Collection edges = getEdges(toSplitVertex); for (FGEdge edge : edges) { - edge.setAlpha(oldComponentsAlpha); + + // don't go past the alpha when removing + double defaultAlpha = edge.getDefaultAlpha(); + double alpha = Math.min(oldComponentsAlpha, defaultAlpha); + edge.setAlpha(alpha); } double newComponentsAlpha = percentComplete; @@ -223,12 +240,19 @@ public class SplitVertexFunctionGraphJob extends AbstractAnimatorJob { edges = getEdges(parentVertex); for (FGEdge edge : edges) { - edge.setAlpha(newComponentsAlpha); + + // don't go past the alpha when adding + double defaultAlpha = edge.getDefaultAlpha(); + double alpha = Math.min(newComponentsAlpha, defaultAlpha); + edge.setAlpha(alpha); } edges = getEdges(childVertex); for (FGEdge edge : edges) { - edge.setAlpha(newComponentsAlpha); + // don't go past the alpha when adding + double defaultAlpha = edge.getDefaultAlpha(); + double alpha = Math.min(newComponentsAlpha, defaultAlpha); + edge.setAlpha(alpha); } } diff --git a/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/FunctionGraphGroupVertices1Test.java b/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/FunctionGraphGroupVertices1Test.java index c9c6dfa189..105d854aac 100644 --- a/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/FunctionGraphGroupVertices1Test.java +++ b/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/FunctionGraphGroupVertices1Test.java @@ -21,6 +21,7 @@ import java.awt.Color; import java.awt.geom.Point2D; import java.util.*; +import org.apache.commons.collections4.IterableUtils; import org.junit.*; import docking.ActionContext; @@ -37,6 +38,7 @@ import ghidra.framework.plugintool.util.PluginException; import ghidra.graph.viewer.options.RelayoutOption; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; +import util.CollectionUtils; public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest { @@ -809,14 +811,41 @@ public class FunctionGraphGroupVertices1Test extends AbstractFunctionGraphTest { verifyDefaultColor(v2); } + @Test + public void testEdgeDefaultAlphaPersistsAfterGrouping() { + + graphFunction("01002cf5"); + + FGVertex v1 = vertex("01002cf5"); + FGVertex v2 = vertex("01002d0f"); + + FunctionGraph graph = getFunctionGraph(); + Iterable edges = graph.getEdges(v1, v2); + assertEquals(1, IterableUtils.size(edges)); + FGEdge edge = CollectionUtils.any(edges); + + Double alpha = edge.getAlpha(); + assertTrue(alpha < 1.0); // this is the default flow + + GroupedFunctionGraphVertex group = group("A", v1, v2); + ungroup(group); + + edges = graph.getEdges(v1, v2); + assertEquals(1, IterableUtils.size(edges)); + edge = CollectionUtils.any(edges); + + Double alphAfterGroup = edge.getAlpha(); + assertEquals(alpha, alphAfterGroup); + } + @Test public void testSymbolAddedWhenGrouped_SymbolOutsideOfGroupNode() { // TODO } - //================================================================================================== - // Private Methods - //================================================================================================== +//================================================================================================== +// Private Methods +//================================================================================================== // @formatter:off @Override diff --git a/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/graph/layout/TestFGLayoutProvider.java b/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/graph/layout/TestFGLayoutProvider.java index af3235cc0d..3f8aea4d1c 100644 --- a/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/graph/layout/TestFGLayoutProvider.java +++ b/Ghidra/Features/FunctionGraph/src/test/java/ghidra/app/plugin/core/functiongraph/graph/layout/TestFGLayoutProvider.java @@ -313,7 +313,7 @@ public class TestFGLayoutProvider extends FGLayoutProvider { } else if (startCol.index > endCol.index) { // flow return - e.setAlpha(.25); + e.setDefaultAlpha(.25); Shape shape = transformer.apply(startVertex); Rectangle bounds = shape.getBounds(); @@ -338,7 +338,7 @@ public class TestFGLayoutProvider extends FGLayoutProvider { else { // same column--nothing to route // straight line, which is the default - e.setAlpha(.25); + e.setDefaultAlpha(.25); } newEdgeArticulations.put(e, articulations); } diff --git a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/jung/renderer/DecompilerDominanceArticulatedEdgeTransformer.java b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/jung/renderer/DNLArticulatedEdgeTransformer.java similarity index 89% rename from Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/jung/renderer/DecompilerDominanceArticulatedEdgeTransformer.java rename to Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/jung/renderer/DNLArticulatedEdgeTransformer.java index 832fed273a..78f780469b 100644 --- a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/jung/renderer/DecompilerDominanceArticulatedEdgeTransformer.java +++ b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/jung/renderer/DNLArticulatedEdgeTransformer.java @@ -17,7 +17,7 @@ package ghidra.app.plugin.core.functiongraph.graph.jung.renderer; import ghidra.app.plugin.core.functiongraph.graph.FGEdge; -public class DecompilerDominanceArticulatedEdgeTransformer extends FGArticulatedEdgeTransformer { +public class DNLArticulatedEdgeTransformer extends FGArticulatedEdgeTransformer { @Override public int getOverlapOffset(FGEdge edge) { diff --git a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java index 39062c64c8..e2f8247885 100644 --- a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java +++ b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java @@ -34,7 +34,7 @@ import ghidra.app.decompiler.DecompInterface; import ghidra.app.decompiler.DecompileOptions; import ghidra.app.plugin.core.functiongraph.graph.FGEdge; import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph; -import ghidra.app.plugin.core.functiongraph.graph.jung.renderer.DecompilerDominanceArticulatedEdgeTransformer; +import ghidra.app.plugin.core.functiongraph.graph.jung.renderer.DNLArticulatedEdgeTransformer; import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex; import ghidra.app.plugin.core.functiongraph.graph.vertex.GroupedFunctionGraphVertex; import ghidra.graph.VisualGraph; @@ -103,7 +103,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout { @Override public Function getEdgeShapeTransformer() { - return new DecompilerDominanceArticulatedEdgeTransformer(); + return new DNLArticulatedEdgeTransformer(); } @Override @@ -715,7 +715,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout { // 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. - e.setAlpha(.25); + e.setDefaultAlpha(.25); } private Column getOutermostCol(LayoutLocationMap layoutLocations, diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/graphs/DefaultVisualGraph.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/graphs/DefaultVisualGraph.java index 533dbfba6d..513c6369e8 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/graphs/DefaultVisualGraph.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/graphs/DefaultVisualGraph.java @@ -15,8 +15,6 @@ */ package ghidra.graph.graphs; -import static com.google.common.collect.Iterables.concat; -import static com.google.common.collect.Iterables.filter; import static util.CollectionUtils.nonNull; import java.awt.Point; @@ -206,9 +204,11 @@ public abstract class DefaultVisualGraph outs = nonNull(getOutEdges(start)); Collection ins = nonNull(getInEdges(end)); + Set unique = new HashSet<>(); + unique.addAll(outs); + unique.addAll(ins); - Iterable concatenated = concat(outs, ins); - Iterable filtered = filter(concatenated, e -> { + Iterable filtered = IterableUtils.filteredIterable(unique, e -> { return e.getStart().equals(start) && e.getEnd().equals(end); }); return filtered; diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphViewerUtils.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphViewerUtils.java index 0db047ff6d..957753e2ae 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphViewerUtils.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/GraphViewerUtils.java @@ -699,9 +699,10 @@ public class GraphViewerUtils { return createHollowEgdeLoopInGraphSpace(vertexShape, startX, startY); } - // translate the edge to the starting vertex + // translate the edge from 0,0 to the starting vertex point AffineTransform xform = AffineTransform.getTranslateInstance(startX, startY); Shape edgeShape = renderContext.getEdgeShapeTransformer().apply(e); + double deltaX = endX - startX; double deltaY = endY - startY; @@ -713,7 +714,7 @@ public class GraphViewerUtils { double dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY); xform.scale(dist, 1.0f); - // apply the transformations + // apply the transformations; converting the given shape from model space into graph space return xform.createTransformedShape(edgeShape); } diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/VisualEdge.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/VisualEdge.java index acccbdfd5a..9f381622b2 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/VisualEdge.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/VisualEdge.java @@ -150,10 +150,10 @@ public interface VisualEdge extends GEdge { public void setAlpha(double alpha); /** - * Get the alpha, which determines how much of the edge is visible/see through. 0 is - * completely transparent. This attribute allows transitional for animations. - * - * @return the alpha value - */ + * Get the alpha, which determines how much of the edge is visible/see through. 0 is + * completely transparent. This attribute allows transitional for animations. + * + * @return the alpha value + */ public double getAlpha(); } diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/edge/VisualEdgeRenderer.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/edge/VisualEdgeRenderer.java index 9120363695..95f7ca3595 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/edge/VisualEdgeRenderer.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/edge/VisualEdgeRenderer.java @@ -168,7 +168,7 @@ public abstract class VisualEdgeRenderer sources = GraphAlgorithms.getSources(graph); + if (sources.isEmpty()) { + Msg.debug(this, "No sources found for graph; cannot calculate dominance: " + + graph.getClass().getSimpleName()); + return null; + } + try { // note: calling the constructor performs the work return new ChkDominanceAlgorithm<>(graph, timeoutMonitor); @@ -275,12 +282,12 @@ public class VisualGraphPathHighlighter the vertex type + * @param the edge type */ public class ArticulatedEdgeTransformer> extends ParallelEdgeShapeTransformer { @@ -75,38 +77,50 @@ public class ArticulatedEdgeTransformer reverse = new ArrayList<>(articulations); - Collections.reverse(reverse); - for (Point2D pt : reverse) { - generalPath.lineTo((float) (pt.getX() - originX) + offset, - (float) (pt.getY() - originY) + offset); - } AffineTransform transform = new AffineTransform(); - final double deltaY = p2.getY() - originY; final double deltaX = p2.getX() - originX; if (deltaX == 0 && deltaY == 0) { // this implies the source and destination node are at the same location, which // is possible if the user drags it there or during animations - return transform.createTransformedShape(generalPath); + return transform.createTransformedShape(path); } double theta = StrictMath.atan2(deltaY, deltaX); transform.rotate(theta); double scale = StrictMath.sqrt(deltaY * deltaY + deltaX * deltaX); transform.scale(scale, 1.0f); + + // + // TODO + // The current design and use of this transformer is a bit odd. We currently have code + // to create the edge shape here and in the ArticulatedEdgeRenderer. Ideally, this + // class would be the only one that creates the edge shape. Then, any clients of the + // edge transformer would have to take the shape and then transform it to the desired + // space (the view or graph space). The transformations could be done using the + // GraphViewerUtils. + // + try { + // TODO it is not clear why this is using an inverse transform; why not just create + // the transform that we want? AffineTransform inverse = transform.createInverse(); - Shape transformedShape = inverse.createTransformedShape(generalPath); + Shape transformedShape = inverse.createTransformedShape(path); return transformedShape; } catch (NoninvertibleTransformException e1) {