8261336: IGV: enhance default filters

Add filters to color and hide parts of the graph based on node categories or
estimated execution frequency, and simplify remaining filters.

Co-authored-by: Christian Hagedorn <chagedorn@openjdk.org>
Reviewed-by: vlivanov, chagedorn, thartmann
This commit is contained in:
Roberto Castañeda Lozano 2021-02-16 12:47:56 +00:00
parent 3f8819c666
commit 16bd7d381f
25 changed files with 332 additions and 84 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -30,6 +30,7 @@
#include "opto/parse.hpp"
#include "runtime/threadCritical.hpp"
#include "runtime/threadSMR.hpp"
#include "utilities/stringUtils.hpp"
#ifndef PRODUCT
@ -378,9 +379,39 @@ void IdealGraphPrinter::visit_node(Node *n, bool edges, VectorSet* temp_set) {
print_prop("block", C->cfg()->get_block(0)->_pre_order);
} else {
print_prop("block", block->_pre_order);
// Print estimated execution frequency, normalized within a [0,1] range.
buffer[0] = 0;
stringStream freq(buffer, sizeof(buffer) - 1);
// Higher precision has no practical effect in visualizations.
freq.print("%.8f", block->_freq / _max_freq);
assert(freq.size() < sizeof(buffer), "size in range");
// Enforce dots as decimal separators, as required by IGV.
StringUtils::replace_no_expand(buffer, ",", ".");
print_prop("frequency", buffer);
}
}
switch (t->category()) {
case Type::Category::Data:
print_prop("category", "data");
break;
case Type::Category::Memory:
print_prop("category", "memory");
break;
case Type::Category::Mixed:
print_prop("category", "mixed");
break;
case Type::Category::Control:
print_prop("category", "control");
break;
case Type::Category::Other:
print_prop("category", "other");
break;
case Type::Category::Undef:
print_prop("category", "undef");
break;
}
const jushort flags = node->flags();
if (flags & Node::Flag_is_Copy) {
print_prop("is_copy", "true");
@ -649,6 +680,16 @@ void IdealGraphPrinter::print(const char *name, Node *node) {
VectorSet temp_set;
head(NODES_ELEMENT);
if (C->cfg() != NULL) {
// Compute the maximum estimated frequency in the current graph.
_max_freq = 1.0e-6;
for (uint i = 0; i < C->cfg()->number_of_blocks(); i++) {
Block* block = C->cfg()->get_block(i);
if (block->_freq > _max_freq) {
_max_freq = block->_freq;
}
}
}
walk_nodes(node, false, &temp_set);
tail(NODES_ELEMENT);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -92,6 +92,7 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
PhaseChaitin* _chaitin;
bool _traverse_outs;
Compile *C;
double _max_freq;
void print_method(ciMethod *method, int bci, InlineTree *tree);
void print_inline_tree(InlineTree *tree);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -1113,6 +1113,73 @@ void Type::dump_stats() {
}
#endif
//------------------------------category---------------------------------------
#ifndef PRODUCT
Type::Category Type::category() const {
const TypeTuple* tuple;
switch (base()) {
case Type::Int:
case Type::Long:
case Type::Half:
case Type::NarrowOop:
case Type::NarrowKlass:
case Type::Array:
case Type::VectorA:
case Type::VectorS:
case Type::VectorD:
case Type::VectorX:
case Type::VectorY:
case Type::VectorZ:
case Type::AnyPtr:
case Type::RawPtr:
case Type::OopPtr:
case Type::InstPtr:
case Type::AryPtr:
case Type::MetadataPtr:
case Type::KlassPtr:
case Type::Function:
case Type::Return_Address:
case Type::FloatTop:
case Type::FloatCon:
case Type::FloatBot:
case Type::DoubleTop:
case Type::DoubleCon:
case Type::DoubleBot:
return Category::Data;
case Type::Memory:
return Category::Memory;
case Type::Control:
return Category::Control;
case Type::Top:
case Type::Abio:
case Type::Bottom:
return Category::Other;
case Type::Bad:
case Type::lastype:
return Category::Undef;
case Type::Tuple:
// Recursive case. Return CatMixed if the tuple contains types of
// different categories (e.g. CallStaticJavaNode's type), or the specific
// category if all types are of the same category (e.g. IfNode's type).
tuple = is_tuple();
if (tuple->cnt() == 0) {
return Category::Undef;
} else {
Category first = tuple->field_at(0)->category();
for (uint i = 1; i < tuple->cnt(); i++) {
if (tuple->field_at(i)->category() != first) {
return Category::Mixed;
}
}
return first;
}
default:
assert(false, "unmatched base type: all base types must be categorized");
}
return Category::Undef;
}
#endif
//------------------------------typerr-----------------------------------------
void Type::typerr( const Type *t ) const {
#ifndef PRODUCT

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -364,6 +364,17 @@ public:
}
virtual void dump2( Dict &d, uint depth, outputStream *st ) const;
static void dump_stats();
// Groups of types, for debugging and visualization only.
enum class Category {
Data,
Memory,
Mixed, // Tuples with types of different categories.
Control,
Other, // {Type::Top, Type::Abio, Type::Bottom}.
Undef // {Type::Bad, Type::lastype}, for completeness.
};
// Return the category of this type.
Category category() const;
static const char* str(const Type* t);
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -63,10 +63,8 @@ public class ConnectionFilter extends AbstractFilter {
for (Figure f : figures) {
for (OutputSlot os : f.getOutputSlots()) {
for (Connection c : os.getConnections()) {
if (figures.contains(c.getInputSlot().getFigure())) {
c.setStyle(rule.getLineStyle());
c.setColor(rule.getLineColor());
}
c.setStyle(rule.getLineStyle());
c.setColor(rule.getLineColor());
}
}
}

View File

@ -2,33 +2,5 @@
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
<filesystem>
<folder name="Filters">
<file name="Coloring" url="filters/color.filter">
<attr name="enabled" boolvalue="true"/>
</file>
<file name="Stamp Coloring" url="filters/stampColor.filter">
<attr name="enabled" boolvalue="false"/>
</file>
<file name="Probability Coloring" url="filters/probability.filter">
<attr name="enabled" boolvalue="false"/>
</file>
<file name="Call Graph Coloring" url="filters/callgraph.filter">
<attr name="enabled" boolvalue="false"/>
</file>
<file name="Reduce Edges" url="filters/reduceEdges.filter">
<attr name="enabled" boolvalue="true"/>
</file>
<file name="Remove State" url="filters/removeState.filter">
<attr name="enabled" boolvalue="false"/>
</file>
<file name="Remove Floating" url="filters/removeFloating.filter">
<attr name="enabled" boolvalue="false"/>
</file>
</folder>
</filesystem>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -46,7 +46,8 @@ public class Connection implements Source.Provider, Link {
NORMAL,
DASHED,
BOLD
BOLD,
INVISIBLE
}
private InputSlot inputSlot;
private OutputSlot outputSlot;

View File

@ -1,18 +1,25 @@
colorize("name", ".*", yellow);
colorize("name", "Catch.*", blue);
colorize("name", "Region|Loop|CountedLoop|Root", red);
colorize("name", "CProj|IfFalse|IfTrue|JProj|CatchProj", magenta);
colorize("name", "Con.*", orange);
colorize("name", "Parm|Proj", lightGray);
var mixedNodeColor = java.awt.Color.decode("#ffaabb");
var controlNodeColor = java.awt.Color.decode("#ee8866");
var otherNodeColor = java.awt.Color.decode("#eedd88");
var dataNodeColor = java.awt.Color.decode("#adcbea");
var memoryNodeColor = java.awt.Color.decode("#babb00");
// Nodes with bci
colorize("bci", "..*", magenta);
var mixedEdgeColor = java.awt.Color.decode("#ff7f99");
var controlEdgeColor = java.awt.Color.decode("#e75828");
var otherEdgeColor = java.awt.Color.decode("#dfc025");
var dataEdgeColor = java.awt.Color.decode("#3178c2");
var memoryEdgeColor = java.awt.Color.decode("#828200");
colorize("category", "data", dataNodeColor);
colorize("category", "memory", memoryNodeColor);
colorize("category", "mixed", mixedNodeColor);
colorize("category", "control", controlNodeColor);
colorize("category", "other", otherNodeColor);
// Line style
var f = new ColorFilter("Line Style filter");
f.addRule(new ColorFilter.ColorRule(new MatcherSelector(new Properties.StringPropertyMatcher("type", "int:")), null, Color.BLUE, null));
f.addRule(new ColorFilter.ColorRule(new MatcherSelector(new Properties.StringPropertyMatcher("type", "control")), null, Color.RED, null));
f.addRule(new ColorFilter.ColorRule(new MatcherSelector(new Properties.StringPropertyMatcher("type", "memory")), null, Color.GREEN, null));
f.addRule(new ColorFilter.ColorRule(new MatcherSelector(new Properties.StringPropertyMatcher("type", "tuple:")), null, Color.MAGENTA, null));
f.addRule(new ColorFilter.ColorRule(new MatcherSelector(new Properties.StringPropertyMatcher("type", "bottom")), null, Color.LIGHT_GRAY, null));
f.addRule(new ColorFilter.ColorRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "data")), null, dataEdgeColor, null));
f.addRule(new ColorFilter.ColorRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "memory")), null, memoryEdgeColor, null));
f.addRule(new ColorFilter.ColorRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "mixed")), null, mixedEdgeColor, null));
f.addRule(new ColorFilter.ColorRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "control")), null, controlEdgeColor, null));
f.addRule(new ColorFilter.ColorRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "other")), null, otherEdgeColor, null));
f.apply(graph);

View File

@ -0,0 +1,23 @@
// Color nodes by estimated execution frequency. Applies only when control-flow
// graph information is available (from "Global Code Motion" on).
// These colors are generated by running:
// $ python3 extract-colors.py --steps 10 --colormap coolwarm
var step0Color = java.awt.Color.decode("#3b4cc0");
var step1Color = java.awt.Color.decode("#5977e3");
var step2Color = java.awt.Color.decode("#7b9ff9");
var step3Color = java.awt.Color.decode("#9ebeff");
var step4Color = java.awt.Color.decode("#c0d4f5");
var step5Color = java.awt.Color.decode("#dddcdc");
var step6Color = java.awt.Color.decode("#f2cbb7");
var step7Color = java.awt.Color.decode("#f7ac8e");
var step8Color = java.awt.Color.decode("#ee8468");
var step9Color = java.awt.Color.decode("#d65244");
var step10Color = java.awt.Color.decode("#b40426");
var colors = [step0Color, step1Color, step2Color, step3Color, step4Color, step5Color, step6Color, step7Color, step8Color, step9Color, step10Color]
var fractions = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
// The max value is set to 1.01 instead of 1.0 to workaround a (numerical?)
// issue where nodes with frequencies close (but not equal to) 1.0 are not colored.
colorizeGradientCustom("frequency", 0.0, 1.01, "logarithmic", colors, fractions, 1024);

View File

@ -0,0 +1,41 @@
#!/usr/bin/python3
#
# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
import matplotlib.cm
import argparse
import sys
parser = argparse.ArgumentParser()
parser.add_argument('--steps', type=int, default=10)
parser.add_argument('--colormap', default='coolwarm')
args = parser.parse_args()
cmap = matplotlib.cm.get_cmap(args.colormap)
n = args.steps
for step in range(n + 1):
point = step / float(n)
rgb = tuple([int(round(c * 255)) for c in cmap(point)[0:3]])
hex = '#%02x%02x%02x' % rgb
print("var step" + str(step) + "Color" + " = java.awt.Color.decode(\"" + \
hex + "\");")

View File

@ -0,0 +1,3 @@
var f = new RemoveFilter("Hide control subgraph");
f.addRule(new RemoveFilter.RemoveRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "control"))));
f.apply(graph);

View File

@ -0,0 +1,5 @@
var f = new ConnectionFilter("Hide control edges");
f.addRule(new ConnectionFilter.ConnectionStyleRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "control")),
white,
Connection.ConnectionStyle.INVISIBLE));
f.apply(graph);

View File

@ -0,0 +1,3 @@
var f = new RemoveFilter("Hide data subgraph");
f.addRule(new RemoveFilter.RemoveRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "data"))));
f.apply(graph);

View File

@ -0,0 +1,5 @@
var f = new ConnectionFilter("Hide data edges");
f.addRule(new ConnectionFilter.ConnectionStyleRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "data")),
white,
Connection.ConnectionStyle.INVISIBLE));
f.apply(graph);

View File

@ -0,0 +1,3 @@
var f = new RemoveFilter("Hide memory subgraph");
f.addRule(new RemoveFilter.RemoveRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "memory"))));
f.apply(graph);

View File

@ -0,0 +1,5 @@
var f = new ConnectionFilter("Hide memory edges");
f.addRule(new ConnectionFilter.ConnectionStyleRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "memory")),
white,
Connection.ConnectionStyle.INVISIBLE));
f.apply(graph);

View File

@ -0,0 +1,3 @@
var f = new RemoveFilter("Hide mixed subgraph");
f.addRule(new RemoveFilter.RemoveRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "mixed"))));
f.apply(graph);

View File

@ -0,0 +1,5 @@
var f = new ConnectionFilter("Hide mixed edges");
f.addRule(new ConnectionFilter.ConnectionStyleRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "mixed")),
white,
Connection.ConnectionStyle.INVISIBLE));
f.apply(graph);

View File

@ -0,0 +1,3 @@
var f = new RemoveFilter("Hide other subgraph");
f.addRule(new RemoveFilter.RemoveRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "other"))));
f.apply(graph);

View File

@ -0,0 +1,5 @@
var f = new ConnectionFilter("Hide other edges");
f.addRule(new ConnectionFilter.ConnectionStyleRule(new MatcherSelector(new Properties.StringPropertyMatcher("category", "other")),
white,
Connection.ConnectionStyle.INVISIBLE));
f.apply(graph);

View File

@ -1,24 +1,27 @@
// Remove all nodes except control, mixed, and nodes of 'bottom' type that are
// successors of control nodes (typically 'Halt', 'Return', etc.).
var f = new RemoveFilter("Show only control flow");
f.addRule(
new RemoveFilter.RemoveRule(
new InvertSelector(
new OrSelector(
new OrSelector(
new MatcherSelector(
new Properties.RegexpPropertyMatcher("category", "control|mixed")
),
new AndSelector(
new SuccessorSelector(
new MatcherSelector(
new Properties.StringPropertyMatcher("type", "control")
new Properties.RegexpPropertyMatcher("type", "control")
)
),
new MatcherSelector(
new Properties.StringPropertyMatcher("type", "control")
new MatcherSelector(
new Properties.RegexpPropertyMatcher("type", "bottom")
)
),
new MatcherSelector(
new Properties.StringPropertyMatcher("name", "Start")
)
)
), false
),
false
)
);
f.addRule(new RemoveFilter.RemoveRule(new MatcherSelector(new Properties.RegexpPropertyMatcher("name", "Phi|Store."))));
f.apply(graph);

View File

@ -1,8 +0,0 @@
remove("dump_spec", "FramePtr|ReturnAdr|I_O");
removeInputs("name", "Root");
var f = new RemoveSelfLoopsFilter("Remove Self-Loops");
f.apply(graph);
removeInputs("name", "SafePoint|CallStaticJava|CallDynamicJava|CallJava|CallLeaf|CallRuntime|AbstractLock|CallLeafNoFP|Call|CallStaticJavaDirect", 5);
removeInputs("name", "Unlock|Lock", 7);
removeInputs("name", "Allocate", 7);
removeInputs("name", "AllocateArray", 9);

View File

@ -1,6 +1,19 @@
// Hide secondary edges.
remove("dump_spec", "FramePtr|ReturnAdr|I_O");
removeInputs("name", "Root");
var f = new RemoveSelfLoopsFilter("Remove Self-Loops");
f.apply(graph);
removeInputs("name", "SafePoint|CallStaticJava|CallDynamicJava|CallJava|CallLeaf|CallRuntime|AbstractLock|CallLeafNoFP|Call|CallStaticJavaDirect", 5);
removeInputs("name", "Unlock|Lock", 7);
removeInputs("name", "Allocate", 7);
removeInputs("name", "AllocateArray", 9);
// Combine projection nodes.
var f = new CombineFilter("Combine Filter");
f.addRule(new CombineFilter.CombineRule(new Properties.RegexpPropertyMatcher("name", ".*"), new Properties.RegexpPropertyMatcher("name", "Proj|IfFalse|IfTrue|JProj|MachProj|JumpProj|CatchProj")));
f.addRule(new CombineFilter.CombineRule(new Properties.RegexpPropertyMatcher("name", "Cmp.*"), new Properties.RegexpPropertyMatcher("name", "Bool")));
f.apply(graph);
// Inline (split) constant nodes.
split("name", "BoxLock");
split("name", "(Con.*)|(loadCon.*)", "[dump_spec]");

View File

@ -2,28 +2,60 @@
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
<filesystem>
<folder name="Filters">
<file name="C2 Basic Coloring" url="filters/color.filter">
<attr name="enabled" boolvalue="false"/>
<file name="Color by category" url="filters/color.filter">
<attr name="enabled" boolvalue="true"/>
</file>
<file name="C2 Matcher Flags Coloring" url="filters/matchingFlags.filter">
<file name="Color by execution frequency" url="filters/colorFrequency.filter">
<attr name="enabled" boolvalue="false"/>
<attr name="after" stringvalue="C2 Basic Coloring"/>
<attr name="after" stringvalue="Color by category"/>
</file>
<file name="C2 Register Coloring" url="filters/register.filter">
<file name="Simplify graph" url="filters/structural.filter">
<attr name="enabled" boolvalue="false"/>
<attr name="after" stringvalue="C2 Matcher Flags Coloring"/>
<attr name="after" stringvalue="Color by execution frequency"/>
</file>
<file name="C2 Only Control Flow" url="filters/onlyControlFlow.filter">
<file name="Hide data subgraph" url="filters/hideData.filter">
<attr name="enabled" boolvalue="false"/>
<attr name="after" stringvalue="C2 Register Coloring"/>
<attr name="after" stringvalue="Simplify graph"/>
</file>
<file name="C2 Remove Filter" url="filters/remove.filter">
<file name="Hide memory subgraph" url="filters/hideMemory.filter">
<attr name="enabled" boolvalue="false"/>
<attr name="after" stringvalue="C2 Only Control Flow"/>
<attr name="after" stringvalue="Hide data subgraph"/>
</file>
<file name="C2 Structural" url="filters/structural.filter">
<file name="Hide control subgraph" url="filters/hideControl.filter">
<attr name="enabled" boolvalue="false"/>
<attr name="after" stringvalue="C2 Remove Filter"/>
<attr name="after" stringvalue="Hide memory subgraph"/>
</file>
</folder>
<file name="Hide mixed subgraph" url="filters/hideMixed.filter">
<attr name="enabled" boolvalue="false"/>
<attr name="after" stringvalue="Hide control subgraph"/>
</file>
<file name="Hide other subgraph" url="filters/hideOther.filter">
<attr name="enabled" boolvalue="false"/>
<attr name="after" stringvalue="Hide mixed subgraph"/>
</file>
<file name="Show control flow only" url="filters/onlyControlFlow.filter">
<attr name="enabled" boolvalue="false"/>
<attr name="after" stringvalue="Hide other subgraph"/>
</file>
<file name="Hide data edges" url="filters/hideDataEdges.filter">
<attr name="enabled" boolvalue="false"/>
<attr name="after" stringvalue="Show control flow only"/>
</file>
<file name="Hide memory edges" url="filters/hideMemoryEdges.filter">
<attr name="enabled" boolvalue="false"/>
<attr name="after" stringvalue="Hide data edges"/>
</file>
<file name="Hide control edges" url="filters/hideControlEdges.filter">
<attr name="enabled" boolvalue="false"/>
<attr name="after" stringvalue="Hide memory edges"/>
</file>
<file name="Hide mixed edges" url="filters/hideMixedEdges.filter">
<attr name="enabled" boolvalue="false"/>
<attr name="after" stringvalue="Hide control edges"/>
</file>
<file name="Hide other edges" url="filters/hideOtherEdges.filter">
<attr name="enabled" boolvalue="false"/>
<attr name="after" stringvalue="Hide mixed edges"/>
</file>
</folder>
</filesystem>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -779,6 +779,7 @@ public class DiagramScene extends ObjectScene implements DiagramViewer {
boolean isBold = false;
boolean isDashed = true;
boolean isVisible = true;
for (Connection c : connectionList) {
@ -789,6 +790,10 @@ public class DiagramScene extends ObjectScene implements DiagramViewer {
if (c.getStyle() != Connection.ConnectionStyle.DASHED) {
isDashed = false;
}
if (c.getStyle() == Connection.ConnectionStyle.INVISIBLE) {
isVisible = false;
}
}
LineWidget newPredecessor = predecessor;
@ -804,6 +809,7 @@ public class DiagramScene extends ObjectScene implements DiagramViewer {
curAnimator = null;
}
LineWidget w = new LineWidget(this, s, connectionList, p1, p2, predecessor, curAnimator, isBold, isDashed);
w.setVisible(isVisible);
lineCache.add(curPair);
newPredecessor = w;