1 // GNU Guix --- Functional package management for GNU
2 // Copyright © 2016 Ricardo Wurmus <rekado@elephly.net>
4 // This file is part of GNU Guix.
6 // GNU Guix is free software; you can redistribute it and/or modify it
7 // under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or (at
9 // your option) any later version.
11 // GNU Guix is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19 var outerRadius
= Math
.max(nodeArray
.length
* 15, 500) / 2,
20 innerRadius
= outerRadius
- Math
.min(nodeArray
.length
* 5, 200),
21 width
= outerRadius
* 2,
22 height
= outerRadius
* 2,
23 colors
= d3
.scale
.category20c(),
26 function neighborsOf (node
) {
27 return links
.filter(function (e
) {
28 return e
.source
=== node
;
35 zoomer
.attr("transform",
36 "translate(" + d3
.event
.translate
+ ")" +
37 "scale(" + d3
.event
.scale
+ ")");
40 function fade (opacity
, root
) {
41 return function (g
, i
) {
42 root
.selectAll("g path.chord")
43 .filter(function (d
) {
44 return d
.source
.index
!= i
&& d
.target
.index
!= i
;
47 .style("opacity", opacity
);
51 // Now that we have all nodes in an object we can replace each reference
52 // with the actual node object.
53 links
.forEach(function (link
) {
54 link
.target
= nodes
[link
.target
];
55 link
.source
= nodes
[link
.source
];
58 // Construct a square matrix for package dependencies
59 nodeArray
.forEach(function (d
, index
, arr
) {
63 row
= matrix
[source
] = [];
64 for (var i
= -1; ++i
< arr
.length
;) row
[i
] = 0;
66 neighborsOf(d
).forEach(function (d
) { row
[d
.index
]++; });
70 var chord
= d3
.layout
.chord()
72 .sortSubgroups(d3
.descending
)
73 .sortChords(d3
.descending
)
76 var arc
= d3
.svg
.arc()
77 .innerRadius(innerRadius
)
78 .outerRadius(innerRadius
+ 20);
80 var zoom
= d3
.behavior
.zoom()
81 .scaleExtent([0.1, 10])
84 var svg
= d3
.select("body").append("svg")
85 .attr("width", "100%")
86 .attr("height", "100%")
87 .attr('viewBox', '0 0 ' + Math
.min(width
, height
) + ' ' + Math
.min(width
, height
))
88 .attr('preserveAspectRatio', 'xMinYMin')
91 var zoomer
= svg
.append("g");
93 var container
= zoomer
.append("g")
94 .attr("transform", "translate(" + outerRadius
+ "," + outerRadius
+ ")");
96 // Group for arcs and labels
97 var g
= container
.selectAll(".group")
100 .attr("class", "group")
101 .on("mouseout", fade(1, container
))
102 .on("mouseover", fade(0.1, container
));
104 // Draw one segment per package
106 .style("fill", function (d
) { return colors(d
.index
); })
107 .style("stroke", function (d
) { return colors(d
.index
); })
110 // Add circular labels
112 .each(function (d
) { d
.angle
= (d
.startAngle
+ d
.endAngle
) / 2; })
114 .attr("transform", function (d
) {
115 return "rotate(" + (d
.angle
* 180 / Math
.PI
- 90) + ")"
116 + "translate(" + (innerRadius
+ 26) + ")"
117 + (d
.angle
> Math
.PI
? "rotate(180)" : "");
119 .style("text-anchor", function (d
) { return d
.angle
> Math
.PI
? "end" : null; })
120 .text(function (d
) { return nodeArray
[d
.index
].label
; });
122 // Draw chords from source to target; color by source.
123 container
.selectAll(".chord")
125 .enter().append("path")
126 .attr("class", "chord")
127 .style("stroke", function (d
) { return d3
.rgb(colors(d
.source
.index
)).darker(); })
128 .style("fill", function (d
) { return colors(d
.source
.index
); })
129 .attr("d", d3
.svg
.chord().radius(innerRadius
));