A simpler approach to the visualization of fuzzy graphs (see the previous example). This time, links only show the constrained maximum degree (in light gray) and the actual degree (in dark gray). Avoiding to display the unconstrained maximum degree (i.e., 1) reduce the complexity of the visualization, at the expense of losing a reference for evaluating a link's thickness.
Last active
February 15, 2019 06:29
-
-
Save nitaku/7891d5fe93150c9faeb7 to your computer and use it in GitHub Desktop.
Fuzzy graph II
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
graph = { | |
nodes: [ | |
{id: 'A', u: Math.random()}, | |
{id: 'B', u: Math.random()}, | |
{id: 'C', u: Math.random()}, | |
{id: 'D', u: Math.random()}, | |
{id: 'E', u: Math.random()}, | |
{id: 'F', u: Math.random()}, | |
{id: 'G', u: Math.random()}, | |
{id: 'H', u: Math.random()}, | |
{id: 'I', u: Math.random()}, | |
{id: 'J', u: Math.random()}, | |
{id: 'K', u: Math.random()}, | |
{id: 'L', u: Math.random()}, | |
{id: 'M', u: Math.random()}, | |
{id: 'N', u: Math.random()}, | |
{id: 'O', u: Math.random()} | |
], | |
links: [ | |
{id: 1, source: 'A', target: 'B'}, | |
{id: 2, source: 'B', target: 'C'}, | |
{id: 3, source: 'C', target: 'A'}, | |
{id: 4, source: 'B', target: 'D'}, | |
{id: 5, source: 'D', target: 'C'}, | |
{id: 6, source: 'D', target: 'E'}, | |
{id: 7, source: 'E', target: 'F'}, | |
{id: 8, source: 'F', target: 'G'}, | |
{id: 9, source: 'F', target: 'H'}, | |
{id: 10, source: 'G', target: 'H'}, | |
{id: 11, source: 'G', target: 'I'}, | |
{id: 12, source: 'H', target: 'I'}, | |
{id: 13, source: 'J', target: 'E'}, | |
{id: 14, source: 'J', target: 'L'}, | |
{id: 15, source: 'J', target: 'K'}, | |
{id: 16, source: 'K', target: 'L'}, | |
{id: 17, source: 'L', target: 'M'}, | |
{id: 18, source: 'M', target: 'K'}, | |
{id: 19, source: 'N', target: 'O'} | |
]} | |
### objectify the graph ### | |
### resolve node IDs (not optimized at all!) ### | |
for l in graph.links | |
for n in graph.nodes | |
if l.source is n.id | |
l.source = n | |
if l.target is n.id | |
l.target = n | |
# link's u cannot exceed the ones of connected nodes | |
l.u = Math.min(Math.random(), l.source.u, l.target.u) | |
radius = d3.scale.sqrt() | |
.domain([0,1]) | |
.range([0,18]) | |
thickness = d3.scale.linear() | |
.domain([0,1]) | |
.range([0,10]) | |
svg = d3.select('svg') | |
width = svg.node().getBoundingClientRect().width | |
height = svg.node().getBoundingClientRect().height | |
### create a crisp and a fuzzy layer ### | |
crisp = svg.append('g') | |
fuzzy = svg.append('g') | |
### create crisp nodes ### | |
nodes = crisp.selectAll('.node') | |
.data(graph.nodes, (d) -> d.id) | |
enter_crisp_nodes = nodes.enter().append('g') | |
.attr | |
class: 'crisp node' | |
enter_crisp_nodes.append('circle') | |
.attr | |
r: radius.range()[1] | |
enter_crisp_nodes.append('title') | |
.text((d) -> "(#{d.id} #{d3.format('%')(d.u)})") | |
### create fuzzy nodes and links ### | |
links = fuzzy.selectAll('.link') | |
.data(graph.links, (d) -> d.id) | |
enter_fuzzy_links = links | |
.enter().append('g') | |
.attr | |
class: 'fuzzy link' | |
enter_fuzzy_links.append('line') | |
.attr | |
class: 'max' | |
'stroke-width': (d) -> thickness(Math.min(d.source.u, d.target.u)) | |
enter_fuzzy_links.append('line') | |
.attr | |
class: 'value' | |
'stroke-width': (d) -> thickness(d.u) | |
enter_fuzzy_links.append('title') | |
.text((d) -> "(#{d.source.id})-[#{d3.format('%')(d.u)}]-(#{d.target.id})\nMax: #{d3.format('%')(Math.min(d.source.u,d.target.u))}") | |
nodes = fuzzy.selectAll('.node') | |
.data(graph.nodes, (d) -> d.id) | |
enter_fuzzy_nodes = nodes.enter().append('g') | |
.attr | |
class: 'fuzzy node' | |
enter_fuzzy_nodes.append('circle') | |
.attr | |
r: (d) -> radius(d.u) | |
### draw the label ### | |
enter_fuzzy_nodes.append('text') | |
.text((d) -> d.id) | |
.attr | |
dy: '0.8em' | |
x: (d) -> radius(d.u) | |
y: (d) -> radius(d.u)/2 | |
### cola layout ### | |
graph.nodes.forEach (v) -> | |
v.width = 2.5*radius(v.u) | |
v.height = 2.5*radius(v.u) | |
d3cola = cola.d3adaptor() | |
.size([width, height]) | |
.linkDistance(60) | |
.avoidOverlaps(true) | |
.nodes(graph.nodes) | |
.links(graph.links) | |
.on 'tick', () -> | |
### update nodes and links ### | |
svg.selectAll('.node') | |
.attr('transform', (d) -> "translate(#{d.x},#{d.y})") | |
svg.selectAll('.crisp.link > line') | |
.attr('x1', (d) -> d.source.x) | |
.attr('y1', (d) -> d.source.y) | |
.attr('x2', (d) -> d.target.x) | |
.attr('y2', (d) -> d.target.y) | |
svg.selectAll('.fuzzy.link > line') | |
.attr('x1', (d) -> d.source.x) | |
.attr('y1', (d) -> d.source.y) | |
.attr('x2', (d) -> d.target.x) | |
.attr('y2', (d) -> d.target.y) | |
enter_crisp_nodes | |
.call(d3cola.drag) | |
d3cola.start(30,30,30) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.crisp.node > circle { | |
fill: #DDD; | |
} | |
.fuzzy.node > circle { | |
fill: #595; | |
pointer-events: none; | |
} | |
.node > text { | |
font-family: sans-serif; | |
text-anchor: start; | |
pointer-events: none; | |
user-select: none; | |
-webkit-user-select: none; | |
text-shadow: -2px 0 white, 0 2px white, 2px 0 white, 0 -2px white, -1px -1px white, 1px -1px white, 1px 1px white, -1px 1px white | |
} | |
.fuzzy.link .max { | |
stroke: #DDD; | |
} | |
.fuzzy.link .value { | |
stroke: #888; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Fuzzy graph II</title> | |
<link rel="stylesheet" href="index.css"> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="http://marvl.infotech.monash.edu/webcola/cola.v3.min.js"></script> | |
</head> | |
<body> | |
<svg width="960px" height="500px"></svg> | |
<script src="index.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generated by CoffeeScript 1.4.0 | |
(function() { | |
var crisp, d3cola, enter_crisp_nodes, enter_fuzzy_links, enter_fuzzy_nodes, fuzzy, graph, height, l, links, n, nodes, radius, svg, thickness, width, _i, _j, _len, _len1, _ref, _ref1; | |
graph = { | |
nodes: [ | |
{ | |
id: 'A', | |
u: Math.random() | |
}, { | |
id: 'B', | |
u: Math.random() | |
}, { | |
id: 'C', | |
u: Math.random() | |
}, { | |
id: 'D', | |
u: Math.random() | |
}, { | |
id: 'E', | |
u: Math.random() | |
}, { | |
id: 'F', | |
u: Math.random() | |
}, { | |
id: 'G', | |
u: Math.random() | |
}, { | |
id: 'H', | |
u: Math.random() | |
}, { | |
id: 'I', | |
u: Math.random() | |
}, { | |
id: 'J', | |
u: Math.random() | |
}, { | |
id: 'K', | |
u: Math.random() | |
}, { | |
id: 'L', | |
u: Math.random() | |
}, { | |
id: 'M', | |
u: Math.random() | |
}, { | |
id: 'N', | |
u: Math.random() | |
}, { | |
id: 'O', | |
u: Math.random() | |
} | |
], | |
links: [ | |
{ | |
id: 1, | |
source: 'A', | |
target: 'B' | |
}, { | |
id: 2, | |
source: 'B', | |
target: 'C' | |
}, { | |
id: 3, | |
source: 'C', | |
target: 'A' | |
}, { | |
id: 4, | |
source: 'B', | |
target: 'D' | |
}, { | |
id: 5, | |
source: 'D', | |
target: 'C' | |
}, { | |
id: 6, | |
source: 'D', | |
target: 'E' | |
}, { | |
id: 7, | |
source: 'E', | |
target: 'F' | |
}, { | |
id: 8, | |
source: 'F', | |
target: 'G' | |
}, { | |
id: 9, | |
source: 'F', | |
target: 'H' | |
}, { | |
id: 10, | |
source: 'G', | |
target: 'H' | |
}, { | |
id: 11, | |
source: 'G', | |
target: 'I' | |
}, { | |
id: 12, | |
source: 'H', | |
target: 'I' | |
}, { | |
id: 13, | |
source: 'J', | |
target: 'E' | |
}, { | |
id: 14, | |
source: 'J', | |
target: 'L' | |
}, { | |
id: 15, | |
source: 'J', | |
target: 'K' | |
}, { | |
id: 16, | |
source: 'K', | |
target: 'L' | |
}, { | |
id: 17, | |
source: 'L', | |
target: 'M' | |
}, { | |
id: 18, | |
source: 'M', | |
target: 'K' | |
}, { | |
id: 19, | |
source: 'N', | |
target: 'O' | |
} | |
] | |
}; | |
/* objectify the graph | |
*/ | |
/* resolve node IDs (not optimized at all!) | |
*/ | |
_ref = graph.links; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
l = _ref[_i]; | |
_ref1 = graph.nodes; | |
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { | |
n = _ref1[_j]; | |
if (l.source === n.id) { | |
l.source = n; | |
} | |
if (l.target === n.id) { | |
l.target = n; | |
} | |
} | |
l.u = Math.min(Math.random(), l.source.u, l.target.u); | |
} | |
radius = d3.scale.sqrt().domain([0, 1]).range([0, 18]); | |
thickness = d3.scale.linear().domain([0, 1]).range([0, 10]); | |
svg = d3.select('svg'); | |
width = svg.node().getBoundingClientRect().width; | |
height = svg.node().getBoundingClientRect().height; | |
/* create a crisp and a fuzzy layer | |
*/ | |
crisp = svg.append('g'); | |
fuzzy = svg.append('g'); | |
/* create crisp nodes | |
*/ | |
nodes = crisp.selectAll('.node').data(graph.nodes, function(d) { | |
return d.id; | |
}); | |
enter_crisp_nodes = nodes.enter().append('g').attr({ | |
"class": 'crisp node' | |
}); | |
enter_crisp_nodes.append('circle').attr({ | |
r: radius.range()[1] | |
}); | |
enter_crisp_nodes.append('title').text(function(d) { | |
return "(" + d.id + " " + (d3.format('%')(d.u)) + ")"; | |
}); | |
/* create fuzzy nodes and links | |
*/ | |
links = fuzzy.selectAll('.link').data(graph.links, function(d) { | |
return d.id; | |
}); | |
enter_fuzzy_links = links.enter().append('g').attr({ | |
"class": 'fuzzy link' | |
}); | |
enter_fuzzy_links.append('line').attr({ | |
"class": 'max', | |
'stroke-width': function(d) { | |
return thickness(Math.min(d.source.u, d.target.u)); | |
} | |
}); | |
enter_fuzzy_links.append('line').attr({ | |
"class": 'value', | |
'stroke-width': function(d) { | |
return thickness(d.u); | |
} | |
}); | |
enter_fuzzy_links.append('title').text(function(d) { | |
return "(" + d.source.id + ")-[" + (d3.format('%')(d.u)) + "]-(" + d.target.id + ")\nMax: " + (d3.format('%')(Math.min(d.source.u, d.target.u))); | |
}); | |
nodes = fuzzy.selectAll('.node').data(graph.nodes, function(d) { | |
return d.id; | |
}); | |
enter_fuzzy_nodes = nodes.enter().append('g').attr({ | |
"class": 'fuzzy node' | |
}); | |
enter_fuzzy_nodes.append('circle').attr({ | |
r: function(d) { | |
return radius(d.u); | |
} | |
}); | |
/* draw the label | |
*/ | |
enter_fuzzy_nodes.append('text').text(function(d) { | |
return d.id; | |
}).attr({ | |
dy: '0.8em', | |
x: function(d) { | |
return radius(d.u); | |
}, | |
y: function(d) { | |
return radius(d.u) / 2; | |
} | |
}); | |
/* cola layout | |
*/ | |
graph.nodes.forEach(function(v) { | |
v.width = 2.5 * radius(v.u); | |
return v.height = 2.5 * radius(v.u); | |
}); | |
d3cola = cola.d3adaptor().size([width, height]).linkDistance(60).avoidOverlaps(true).nodes(graph.nodes).links(graph.links).on('tick', function() { | |
/* update nodes and links | |
*/ | |
svg.selectAll('.node').attr('transform', function(d) { | |
return "translate(" + d.x + "," + d.y + ")"; | |
}); | |
svg.selectAll('.crisp.link > line').attr('x1', function(d) { | |
return d.source.x; | |
}).attr('y1', function(d) { | |
return d.source.y; | |
}).attr('x2', function(d) { | |
return d.target.x; | |
}).attr('y2', function(d) { | |
return d.target.y; | |
}); | |
return svg.selectAll('.fuzzy.link > line').attr('x1', function(d) { | |
return d.source.x; | |
}).attr('y1', function(d) { | |
return d.source.y; | |
}).attr('x2', function(d) { | |
return d.target.x; | |
}).attr('y2', function(d) { | |
return d.target.y; | |
}); | |
}); | |
enter_crisp_nodes.call(d3cola.drag); | |
d3cola.start(30, 30, 30); | |
}).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment