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.

324 lines
8.1 KiB
JavaScript

8 months ago
var audioEl;
var links = [],
nodes = [],
groups = [];
let zoomLvl = 1;
let lastK = 0;
const c = {
circle: {
fill: "rgba(255,255,255,.5)",
hover: "grey",
active: "black",
},
text: {
size: {
xl: 12,
md: 3,
},
},
};
function parseData(set) {
for (var i = 0; i < set.length; i++) {
nodes.push(set[i]);
set[i].children?.forEach((child) => {
links.push({
source: set[i].id,
target: child.id,
depth: set[i].depth,
});
});
if (set[i].children) {
parseData(set[i].children);
}
}
}
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;
});
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 getGroups() {
nodes.forEach((item) => {
if (!groups.includes(item.group)) {
groups.push(item.group);
}
});
}
function createGraph() {
// 0 to 3 GB
var rScale = d3.scaleLinear().domain([0, 30000000]).range([2, 15]);
const width = window.innerWidth;
const height = window.innerHeight;
var colorS = d3
.scaleLinear() //
.domain([1, groups.length])
.range(["red", "green"]);
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")
.attr("font-size", (d) => (d.type === "directory" ? c.text.size.xl : c.text.size.md))
.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";
})
// .attr("fill", c.circle.fill)
.attr("label", (d) => d.id)
.attr("path", (d) => d.id)
.attr("group", (d) => d.group);
// Add a drag behavior.
node.call(d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended));
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);
});
svg.call(
d3
.zoom()
.extent([
[0, 0],
[width, height],
])
.scaleExtent([1, 100])
.on("zoom", zoomed)
);
container.append(svg.node());
svg
.selectAll("circle")
.on("mouseenter", function (e) {
if (d3.select(this).attr("fill") !== c.circle.active) {
d3.select(this).attr("fill", c.circle.hover);
}
})
.on("click", function () {
d3.selectAll("circle").attr("fill", c.circle.fill);
d3.select(this).attr("fill", c.circle.active);
var path = this.getAttribute("path");
console.log(path);
if (path.match(/\.(?:wav|mp3|flac)$/i)) {
playAudio(path);
}
})
.on("mouseleave", function () {
if (d3.select(this).attr("fill") !== c.circle.active) {
d3.select(this).attr("fill", c.circle.fill);
}
});
Object.assign(svg.node(), {
update({ nodes, links }) {
// Make a shallow copy to protect against mutation, while
// recycling old nodes to preserve position and velocity.
const old = new Map(node.data().map((d) => [d.id, d]));
nodes = nodes.map((d) => ({ ...old.get(d.id), ...d }));
links = links.map((d) => ({ ...d }));
node = node
.data(nodes, (d) => d.id)
.join((enter) =>
enter
.append("circle")
.attr("r", 5)
.call(drag(simulation))
.call((node) => node.append("title").text((d) => d.id))
);
link = link.data(links, (d) => [d.source, d.target]).join("line");
simulation.nodes(nodes);
simulation.force("link").links(links);
simulation.alpha(1).restart().tick();
ticked(); // render now!
},
});
// Reheat the simulation when drag starts, and fix the subject position.
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
// Update the subject (dragged node) position during drag.
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
// Restore the target alpha so the simulation cools after dragging ends.
// Unfix the subject position now that its no longer being dragged.
function dragended(event) {
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);
labels.attr("font-size", (d) => (d.type === "directory" ? c.text.size.xl : c.text.size.md) / zoomLvl);
}
g.attr("transform", e.transform);
}
}
function playAudio(src) {
console.log("play audio: ", src);
audioEl.src = src;
audioEl.currentTime = 0;
audioEl.play();
}
function prepareAudio() {
// var context = window.AudioContext || window.webkitAudioContext;
// var ctx = new AudioContext();
audioEl = document.querySelector("audio");
// var track = ctx.createMediaElementSource(audioEl);
// var gainNode = ctx.createGain();
// track.connect(gainNode);
// track.connect(ctx.destination);
// gainNode.gain.value to change volume
}
window.onload = function () {
parseExifData(dataset);
// getGroups();
createGraph();
document.querySelector(".loading").style.display = "none";
prepareAudio();
};