You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

254 lines
6.4 KiB
JavaScript

8 months ago
// Create some global variables
var width, height, audioEl;
8 months ago
var links = [],
nodes = [],
groups = [];
let zoomLvl = 1;
let lastK = 0;
function parseExifData(files) {
// loop trough all files in JSON and add each to the list of nodes
files.forEach((file) => {
file.id = file.SourceFile;
nodes.push(file);
// also include any mentioned directories in the list of groups and the list of nodes.
var paths = file.Directory.split("/");
// the directory is split up and looped trough, so for every subdirectory, a node is created
paths.forEach((p, index) => {
var path = "";
for (var i = 0; i < index + 1; i++) {
path += (i === 0 ? "" : "/") + paths[i];
}
// we check if the directory is not already stored
if (!groups.includes(path)) {
groups.push(path);
nodes.push({
id: path,
FileType: "directory",
});
}
});
});
// Now that the groups are created, they need to be linked together by adding them to the array of links.
groups.forEach((group) => {
var path = group.split("/");
// link a folder to its parent when it exists
if (path.length > 1) {
var currentName = "/" + path[path.length - 1];
var parentName = group.replace(currentName, "");
var parent = groups.find((group) => {
return group === parentName;
});
8 months ago
// if there is a parent, create a link between the parent and the group.
8 months ago
if (parent) {
links.push({
source: group,
target: parentName,
});
}
}
// now, find any files of the folder and
var children = files.filter((cFile) => {
return cFile.Directory === group;
});
// for each child, add a link between the directory and the file.
children.forEach((sFile) => {
links.push({
source: group,
target: sFile.SourceFile,
});
});
});
}
function createGraph() {
8 months ago
// Create a scale to set the radius based on a file size (0 to 3 GB)
8 months ago
var rScale = d3.scaleLinear().domain([0, 30000000]).range([2, 15]);
const simulation = d3
.forceSimulation(nodes)
.force("center", d3.forceCenter(width / 2, height / 2))
.force(
"collision",
d3
.forceCollide()
// .radius((d) => rScale(d.size) + 1)
.iterations(3)
)
.force(
"link",
d3
.forceLink(links)
.id((d) => d.id)
.strength((d) => 0.2)
// .distance((d) => (d.source.type === "directory" ? 80 : 20))
)
.force("charge", d3.forceManyBody().strength(-1));
// .alphaTarget(0.3); // stay hot
const svg = d3
.create("svg") //
.attr("width", width)
.attr("height", height);
const g = svg.append("g");
const labels = g
.append("g")
.selectAll("text")
.data(nodes)
.join("text")
8 months ago
.attr("font-size", (d) => (d.type === "directory" ? 12 : 3))
8 months ago
.text((d) => d.id)
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y);
const link = g
.append("g") //
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(links)
.join("line")
.join("text")
.text("aaa");
const globalNode = g.append("g");
const node = globalNode
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", (d) => rScale(d.size))
.attr("r", 3)
.attr("stroke", "black")
.attr("fill", (d) => {
return d.FileType === "directory" ? "orange" : "white";
})
8 months ago
// .attr("fill","rgba(255,255,255,.5)")
8 months ago
.attr("label", (d) => d.id)
.attr("path", (d) => d.id)
.attr("group", (d) => d.group);
// Add a drag behavior.
8 months ago
node.call(
d3
.drag() //
.on("start", onDragStart)
.on("drag", onDrag)
.on("end", onDragEnd)
);
// On tick, animate
8 months ago
simulation.on("tick", () => {
link
.attr("x1", (d) => d.source.x)
.attr("y1", (d) => d.source.y)
.attr("x2", (d) => d.target.x)
.attr("y2", (d) => d.target.y);
node.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
labels.attr("x", (d) => d.x).attr("y", (d) => d.y);
});
8 months ago
// add zoom behavior
8 months ago
svg.call(
d3
.zoom()
.extent([
[0, 0],
[width, height],
])
.scaleExtent([1, 100])
.on("zoom", zoomed)
);
8 months ago
// add some mouse behaviors
8 months ago
svg
.selectAll("circle")
.on("mouseenter", function (e) {
8 months ago
if (d3.select(this).attr("fill") !== "black") {
d3.select(this).attr("fill", "grey");
8 months ago
}
})
.on("click", function () {
8 months ago
d3.selectAll("circle").attr("fill", "rgba(255,255,255,.5)");
d3.select(this).attr("fill", "black");
8 months ago
var path = this.getAttribute("path");
8 months ago
// do things based on the file type
8 months ago
if (path.match(/\.(?:wav|mp3|flac)$/i)) {
playAudio(path);
}
})
.on("mouseleave", function () {
8 months ago
if (d3.select(this).attr("fill") !== "black") {
d3.select(this).attr("fill", "rgba(255,255,255,.5)");
8 months ago
}
});
8 months ago
container.append(svg.node());
8 months ago
// Reheat the simulation when drag starts, and fix the subject position.
8 months ago
function onDragStart(event) {
8 months ago
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
8 months ago
// Update the subject (onDrag node) position during drag.
function onDrag(event) {
8 months ago
event.subject.fx = event.x;
event.subject.fy = event.y;
}
// Restore the target alpha so the simulation cools after dragging ends.
8 months ago
// Unfix the subject position now that its no longer being onDrag.
function onDragEnd(event) {
8 months ago
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
function zoomed(e) {
if (e.transform.k > 2 && lastK != e.transform.k) {
lastK = e.transform.k;
console.log("zoomed");
zoomLvl = Math.log2(e.transform.k);
globalNode.attr("stroke-width", 1 / zoomLvl);
link.attr("stroke-width", 1 / zoomLvl);
8 months ago
labels.attr("font-size", (d) => (d.type === "directory" ? 12 : 3) / zoomLvl);
8 months ago
}
g.attr("transform", e.transform);
}
}
function playAudio(src) {
console.log("play audio: ", src);
audioEl.src = src;
audioEl.currentTime = 0;
audioEl.play();
}
window.onload = function () {
8 months ago
// TODO: add resize function
width = window.innerWidth;
height = window.innerHeight;
8 months ago
8 months ago
parseExifData(dataset);
8 months ago
createGraph();
8 months ago
// clear loading screen
8 months ago
document.querySelector(".loading").style.display = "none";
};