Skip to content

Commit 815aee3

Browse files
committed
bunlde forcegraph.js def in network.qmd for #59
1 parent 84c8841 commit 815aee3

File tree

3 files changed

+141
-4
lines changed

3 files changed

+141
-4
lines changed

DESCRIPTION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: repometrics
22
Title: Metrics for Your Code Repository
3-
Version: 0.1.3.048
3+
Version: 0.1.3.049
44
Authors@R:
55
person("Mark", "Padgham", , "mark.padgham@email.com", role = c("aut", "cre"),
66
comment = c(ORCID = "0000-0003-2172-5265"))

codemeta.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"codeRepository": "https://github.com/ropensci-review-tools/repometrics",
99
"issueTracker": "https://github.com/ropensci-review-tools/repometrics/issues",
1010
"license": "https://spdx.org/licenses/GPL-3.0",
11-
"version": "0.1.3.048",
11+
"version": "0.1.3.049",
1212
"programmingLanguage": {
1313
"@type": "ComputerLanguage",
1414
"name": "R",

inst/extdata/quarto/network.qmd

+139-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,145 @@ nodes_user <- data.frame (id = dat_pkg$rm$contribs_from_gh_api$login, group = 4L
3232
nodes_user$group [which (nodes_user$id %in% names (dat_users))] <- 3L
3333
```
3434

35-
```{ojs ForceGraph-import}
36-
import {ForceGraph} from "@d3/force-directed-graph-component"
35+
```{ojs ForceGraph-definition}
36+
// Copyright 2021-2024 Observable, Inc.
37+
// Released under the ISC license.
38+
// https://observablehq.com/@d3/force-directed-graph
39+
function ForceGraph({
40+
nodes, // an iterable of node objects (typically [{id}, …])
41+
links // an iterable of link objects (typically [{source, target}, …])
42+
}, {
43+
nodeId = d => d.id, // given d in nodes, returns a unique identifier (string)
44+
nodeGroup, // given d in nodes, returns an (ordinal) value for color
45+
nodeGroups, // an array of ordinal values representing the node groups
46+
nodeTitle, // given d in nodes, a title string
47+
nodeFill = "currentColor", // node stroke fill (if not using a group color encoding)
48+
nodeStroke = "#fff", // node stroke color
49+
nodeStrokeWidth = 1.5, // node stroke width, in pixels
50+
nodeStrokeOpacity = 1, // node stroke opacity
51+
nodeRadius = 5, // node radius, in pixels
52+
nodeStrength,
53+
linkSource = ({source}) => source, // given d in links, returns a node identifier string
54+
linkTarget = ({target}) => target, // given d in links, returns a node identifier string
55+
linkStroke = "#999", // link stroke color
56+
linkStrokeOpacity = 0.6, // link stroke opacity
57+
linkStrokeWidth = 1.5, // given d in links, returns a stroke width in pixels
58+
linkStrokeLinecap = "round", // link stroke linecap
59+
linkStrength,
60+
colors = d3.schemeTableau10, // an array of color strings, for the node groups
61+
width = 640, // outer width, in pixels
62+
height = 400, // outer height, in pixels
63+
invalidation // when this promise resolves, stop the simulation
64+
} = {}) {
65+
// Compute values.
66+
const N = d3.map(nodes, nodeId).map(intern);
67+
const R = typeof nodeRadius !== "function" ? null : d3.map(nodes, nodeRadius);
68+
const LS = d3.map(links, linkSource).map(intern);
69+
const LT = d3.map(links, linkTarget).map(intern);
70+
if (nodeTitle === undefined) nodeTitle = (_, i) => N[i];
71+
const T = nodeTitle == null ? null : d3.map(nodes, nodeTitle);
72+
const G = nodeGroup == null ? null : d3.map(nodes, nodeGroup).map(intern);
73+
const W = typeof linkStrokeWidth !== "function" ? null : d3.map(links, linkStrokeWidth);
74+
const L = typeof linkStroke !== "function" ? null : d3.map(links, linkStroke);
75+
76+
77+
// Replace the input nodes and links with mutable objects for the simulation.
78+
nodes = d3.map(nodes, (_, i) => ({id: N[i]}));
79+
links = d3.map(links, (_, i) => ({source: LS[i], target: LT[i]}));
80+
81+
// Compute default domains.
82+
if (G && nodeGroups === undefined) nodeGroups = d3.sort(G);
83+
84+
// Construct the scales.
85+
const color = nodeGroup == null ? null : d3.scaleOrdinal(nodeGroups, colors);
86+
87+
// Construct the forces.
88+
const forceNode = d3.forceManyBody();
89+
const forceLink = d3.forceLink(links).id(({index: i}) => N[i]);
90+
if (nodeStrength !== undefined) forceNode.strength(nodeStrength);
91+
if (linkStrength !== undefined) forceLink.strength(linkStrength);
92+
93+
const simulation = d3.forceSimulation(nodes)
94+
.force("link", forceLink)
95+
.force("charge", forceNode)
96+
.force("center", d3.forceCenter())
97+
.on("tick", ticked);
98+
99+
const svg = d3.create("svg")
100+
.attr("width", width)
101+
.attr("height", height)
102+
.attr("viewBox", [-width / 2, -height / 2, width, height])
103+
.attr("style", "max-width: 100%; height: auto; height: intrinsic;");
104+
105+
const link = svg.append("g")
106+
.attr("stroke", typeof linkStroke !== "function" ? linkStroke : null)
107+
.attr("stroke-opacity", linkStrokeOpacity)
108+
.attr("stroke-width", typeof linkStrokeWidth !== "function" ? linkStrokeWidth : null)
109+
.attr("stroke-linecap", linkStrokeLinecap)
110+
.selectAll("line")
111+
.data(links)
112+
.join("line");
113+
114+
const node = svg.append("g")
115+
.attr("fill", nodeFill)
116+
.attr("stroke", nodeStroke)
117+
.attr("stroke-opacity", nodeStrokeOpacity)
118+
.attr("stroke-width", nodeStrokeWidth)
119+
.selectAll("circle")
120+
.data(nodes)
121+
.join("circle")
122+
.attr("r", nodeRadius)
123+
.call(drag(simulation));
124+
125+
if (W) link.attr("stroke-width", ({index: i}) => W[i]);
126+
if (L) link.attr("stroke", ({index: i}) => L[i]);
127+
if (G) node.attr("fill", ({index: i}) => color(G[i]));
128+
if (R) node.attr("r", ({index: i}) => R[i]);
129+
if (T) node.append("title").text(({index: i}) => T[i]);
130+
if (invalidation != null) invalidation.then(() => simulation.stop());
131+
132+
function intern(value) {
133+
return value !== null && typeof value === "object" ? value.valueOf() : value;
134+
}
135+
136+
function ticked() {
137+
link
138+
.attr("x1", d => d.source.x)
139+
.attr("y1", d => d.source.y)
140+
.attr("x2", d => d.target.x)
141+
.attr("y2", d => d.target.y);
142+
143+
node
144+
.attr("cx", d => d.x)
145+
.attr("cy", d => d.y);
146+
}
147+
148+
function drag(simulation) {
149+
function dragstarted(event) {
150+
if (!event.active) simulation.alphaTarget(0.3).restart();
151+
event.subject.fx = event.subject.x;
152+
event.subject.fy = event.subject.y;
153+
}
154+
155+
function dragged(event) {
156+
event.subject.fx = event.x;
157+
event.subject.fy = event.y;
158+
}
159+
160+
function dragended(event) {
161+
if (!event.active) simulation.alphaTarget(0);
162+
event.subject.fx = null;
163+
event.subject.fy = null;
164+
}
165+
166+
return d3.drag()
167+
.on("start", dragstarted)
168+
.on("drag", dragged)
169+
.on("end", dragended);
170+
}
171+
172+
return Object.assign(svg.node(), {scales: {color}});
173+
}
37174
```
38175

39176
```{ojs import-network-data}

0 commit comments

Comments
 (0)