first draft
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
+109
-55
@@ -1,65 +1,119 @@
|
|||||||
{
|
{
|
||||||
"nodes": [
|
"nodes": [
|
||||||
{
|
{
|
||||||
"id": "1",
|
"id": "Dios",
|
||||||
"module": 0,
|
"image": "images/dios.jpg",
|
||||||
"i": 0
|
"group": "Pop/Alternative Rock",
|
||||||
|
"link": "https://www.youtube.com/@Dios_band"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3",
|
"id": "The Home Team",
|
||||||
"module": 0,
|
"image": "images/the home team.jpg",
|
||||||
"i": 1,
|
"group": "Pop/Alternative Rock",
|
||||||
"image": "https://legonzaur.fr/Luka/condo/2026_01/20251109_020401.jpg"
|
"link": "https://www.youtube.com/@TheHomeTeamOfficial"
|
||||||
},
|
},
|
||||||
{ "id": "8", "module": 2, "i": 2 },
|
|
||||||
{
|
{
|
||||||
"id": "9",
|
"id": "Itchika Nito",
|
||||||
"module": 4,
|
"image": "images/itchika nito.jpg",
|
||||||
"i": 3,
|
"group": "Prog Rock",
|
||||||
"image": "https://legonzaur.fr/Luka/condo/2026_01/20251109_020401.jpg"
|
"link": "https://www.youtube.com/@ichika_nito"
|
||||||
},
|
},
|
||||||
{ "id": "12", "module": 2, "i": 4 },
|
{
|
||||||
{ "id": "15", "module": 2, "i": 5 },
|
"id": "Bernth",
|
||||||
{ "id": "23", "module": 3, "i": 6 },
|
"image": "images/bernth.jpg",
|
||||||
{ "id": "26", "module": 3, "i": 7 },
|
"link": "https://www.youtube.com/@Bernthguitar"
|
||||||
{ "id": "37", "module": 10, "i": 8 },
|
},
|
||||||
{ "id": "46", "module": 2, "i": 9 },
|
{
|
||||||
{ "id": "2", "module": 1, "i": 10 },
|
"id": "Syncatto",
|
||||||
{ "id": "4", "module": 2, "i": 11 },
|
"image": "images/syncatto.png",
|
||||||
{ "id": "5", "module": 3, "i": 12 },
|
"link": "https://www.youtube.com/channel/UCs6hz1aQNlSBwAkxEGhx2-g"
|
||||||
{ "id": "6", "module": 3, "i": 13 },
|
},
|
||||||
{ "id": "7", "module": 1, "i": 14 },
|
{
|
||||||
{ "id": "10", "module": 4, "i": 15 },
|
"id": "Plini",
|
||||||
{ "id": "11", "module": 1, "i": 16 },
|
"image": "images/plini.png",
|
||||||
{ "id": "13", "module": 1, "i": 17 },
|
"link": "https://www.youtube.com/@plinimusic"
|
||||||
{ "id": "14", "module": 1, "i": 18 },
|
},
|
||||||
{ "id": "16", "module": 5, "i": 19 },
|
|
||||||
{ "id": "18", "module": 1, "i": 20 },
|
{
|
||||||
{ "id": "19", "module": 3, "i": 21 },
|
"id": "Tim Henson",
|
||||||
{ "id": "20", "module": 1, "i": 22 },
|
"image": "images/tim henson.png",
|
||||||
{ "id": "21", "module": 6, "i": 23 },
|
"link": "https://www.youtube.com/channel/UCdKj0VzVR0Af0wzd7-68Avw"
|
||||||
{ "id": "22", "module": 2, "i": 24 },
|
},
|
||||||
{ "id": "25", "module": 3, "i": 25 },
|
{
|
||||||
{ "id": "27", "module": 1, "i": 26 },
|
"id": "Polyphia",
|
||||||
{ "id": "28", "module": 3, "i": 27 },
|
"image": "images/polyphia.png",
|
||||||
{ "id": "29", "module": 1, "i": 28 },
|
"link": "https://www.youtube.com/c/Polyphia/videos"
|
||||||
{ "id": "31", "module": 2, "i": 29 },
|
},
|
||||||
{ "id": "33", "module": 2, "i": 30 },
|
{
|
||||||
{ "id": "34", "module": 9, "i": 31 },
|
"id": "Anomalie",
|
||||||
{ "id": "35", "module": 1, "i": 32 },
|
"image": "images/anomalie.png",
|
||||||
{ "id": "38", "module": 2, "i": 33 },
|
"link": "https://www.youtube.com/@anomaliebeats"
|
||||||
{ "id": "41", "module": 4, "i": 34 },
|
},
|
||||||
{ "id": "32", "module": 2, "i": 35 },
|
{
|
||||||
{ "id": "40", "module": 2, "i": 36 },
|
"id": "Cory Wong",
|
||||||
{ "id": "17", "module": 4, "i": 37 },
|
"image": "images/cory wong.png",
|
||||||
{ "id": "36", "module": 3, "i": 38 },
|
"link": "https://www.youtube.com/@CoryWongMusic/videos"
|
||||||
{ "id": "39", "module": 3, "i": 39 },
|
}
|
||||||
{ "id": "44", "module": 3, "i": 40 },
|
|
||||||
{ "id": "45", "module": 3, "i": 41 },
|
|
||||||
{ "id": "42", "module": 9, "i": 42 },
|
|
||||||
{ "id": "43", "module": 11, "i": 43 },
|
|
||||||
{ "id": "24", "module": 7, "i": 44 },
|
|
||||||
{ "id": "30", "module": 8, "i": 45 }
|
|
||||||
],
|
],
|
||||||
"edges": [{ "source": "1", "target": "3" }]
|
"edges": [
|
||||||
|
{
|
||||||
|
"source": "Itchika Nito",
|
||||||
|
"target": "Dios",
|
||||||
|
"relation": "Member",
|
||||||
|
"directional": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "Bernth",
|
||||||
|
"target": "Itchika Nito",
|
||||||
|
"relation": "Collab",
|
||||||
|
"link": "https://www.youtube.com/watch?v=sYnbnRuNLNQ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "Bernth",
|
||||||
|
"target": "Syncatto",
|
||||||
|
"relation": "Collab",
|
||||||
|
"link": "https://www.youtube.com/watch?v=9XnHJNKEcW0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "Tim Henson",
|
||||||
|
"target": "Itchika Nito",
|
||||||
|
"relation": "Has anyone seen them both in the same room ?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "Tim Henson",
|
||||||
|
"target": "Polyphia",
|
||||||
|
"relation": "Member",
|
||||||
|
"directional": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "Tim Henson",
|
||||||
|
"target": "Plini",
|
||||||
|
"relation": "Collab",
|
||||||
|
"link": "https://www.youtube.com/watch?v=DRPX8kI06v8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "Tim Henson",
|
||||||
|
"target": "Cory Wong",
|
||||||
|
"relation": "Collab",
|
||||||
|
"link": "https://www.youtube.com/watch?v=DRPX8kI06v8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "Plini",
|
||||||
|
"target": "Cory Wong",
|
||||||
|
"relation": "Collab",
|
||||||
|
"link": "https://www.youtube.com/watch?v=DRPX8kI06v8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "Anomalie",
|
||||||
|
"target": "Plini",
|
||||||
|
"relation": "Collab",
|
||||||
|
"link": "https://www.youtube.com/watch?v=M9JMXAYB2q8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "Anomalie",
|
||||||
|
"target": "Polyphia",
|
||||||
|
"relation": "Collab",
|
||||||
|
"link": "https://www.youtube.com/watch?v=FHalHkfM5Aw"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+10
-1
@@ -11,10 +11,19 @@
|
|||||||
<meta name="description" content="" />
|
<meta name="description" content="" />
|
||||||
|
|
||||||
<link rel="icon" href="favicon.png" />
|
<link rel="icon" href="favicon.png" />
|
||||||
|
<style>
|
||||||
|
text {
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
image {
|
||||||
|
clip-path: inset(0% round 50%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<svg width="1000" height="1000"></svg>
|
<svg width="1000" height="1000" viewBox="-500 -500 1000 1000"></svg>
|
||||||
<script src="dist/graph.js" type="module"></script>
|
<script src="dist/graph.js" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+106
-14
@@ -1,12 +1,22 @@
|
|||||||
import {
|
import {
|
||||||
|
BaseType,
|
||||||
|
drag,
|
||||||
|
DragBehavior,
|
||||||
forceCenter,
|
forceCenter,
|
||||||
|
forceCollide,
|
||||||
forceLink,
|
forceLink,
|
||||||
forceManyBody,
|
forceManyBody,
|
||||||
forceSimulation,
|
forceSimulation,
|
||||||
|
forceX,
|
||||||
|
forceY,
|
||||||
json,
|
json,
|
||||||
select,
|
select,
|
||||||
|
selectAll,
|
||||||
|
Selection,
|
||||||
} from "d3";
|
} from "d3";
|
||||||
|
|
||||||
|
const NODESIZE = 75;
|
||||||
|
const CONTAINERSIZE = 250;
|
||||||
interface Node {
|
interface Node {
|
||||||
id: string;
|
id: string;
|
||||||
module: number;
|
module: number;
|
||||||
@@ -14,11 +24,14 @@ interface Node {
|
|||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
image?: string;
|
image?: string;
|
||||||
|
radius?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Edge {
|
interface Edge {
|
||||||
source: Node;
|
source: Node;
|
||||||
target: Node;
|
target: Node;
|
||||||
|
relation?: string;
|
||||||
|
directional?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Graph {
|
interface Graph {
|
||||||
@@ -26,26 +39,39 @@ interface Graph {
|
|||||||
edges: Edge[];
|
edges: Edge[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const graph = await json<Graph>("graph.json");
|
const rawGraph = await json<{ [key: string]: any[] }>("graph.json");
|
||||||
|
|
||||||
if (!graph) {
|
if (!rawGraph) {
|
||||||
throw Error("graph does not exists");
|
throw Error("graph does not exists");
|
||||||
}
|
}
|
||||||
|
rawGraph.edges.forEach((e) => {
|
||||||
|
e.source = String(rawGraph.nodes.findIndex((n) => n.id == e.source));
|
||||||
|
e.target = String(rawGraph.nodes.findIndex((n) => n.id == e.target));
|
||||||
|
});
|
||||||
|
|
||||||
|
const graph = rawGraph as unknown as Graph;
|
||||||
|
|
||||||
const { nodes, edges } = graph;
|
const { nodes, edges } = graph;
|
||||||
|
|
||||||
const svg = select("svg");
|
const svg = select("svg");
|
||||||
const width = Number(svg.attr("width"));
|
|
||||||
const height = Number(svg.attr("height"));
|
|
||||||
|
|
||||||
// Define the force simulation
|
// Define the force simulation
|
||||||
const simulation = forceSimulation(nodes)
|
const simulation = forceSimulation(nodes)
|
||||||
.force(
|
.force(
|
||||||
"link",
|
"link",
|
||||||
forceLink(edges).id((d) => String(d.index))
|
forceLink(edges)
|
||||||
|
.id((d) => String(d.index))
|
||||||
|
.distance(NODESIZE * 1.7)
|
||||||
)
|
)
|
||||||
.force("charge", forceManyBody().strength(-100))
|
.force(
|
||||||
.force("center", forceCenter(width / 2, height / 2));
|
"charge",
|
||||||
|
forceManyBody()
|
||||||
|
.strength((d) => nodes[d.index!].radius || -2000)
|
||||||
|
.distanceMax(500)
|
||||||
|
)
|
||||||
|
.force("x", forceX())
|
||||||
|
.force("y", forceY());
|
||||||
|
// .force("center", forceCenter(0, 0).strength(0.5));
|
||||||
|
|
||||||
// Adding links
|
// Adding links
|
||||||
const link = svg
|
const link = svg
|
||||||
@@ -56,23 +82,77 @@ const link = svg
|
|||||||
.data(edges)
|
.data(edges)
|
||||||
.join("line");
|
.join("line");
|
||||||
|
|
||||||
|
const lineLegend = svg
|
||||||
|
.append("g")
|
||||||
|
.selectAll("line")
|
||||||
|
.data(edges)
|
||||||
|
.join("text")
|
||||||
|
.text((d) => d.relation || "")
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.attr("font-size", ".7em")
|
||||||
|
.attr("transform", "translate(0, 3)");
|
||||||
|
|
||||||
// Adding nodes
|
// Adding nodes
|
||||||
const node = svg
|
const node = svg
|
||||||
.append("g")
|
.append("g")
|
||||||
.attr("stroke", "#fff")
|
.attr("stroke", "#fff")
|
||||||
.attr("stroke-width", 1.5)
|
.attr("stroke-width", 1.5)
|
||||||
.selectAll("image")
|
.selectAll("g")
|
||||||
.data(nodes)
|
.data(nodes)
|
||||||
.join("image")
|
.join("svg")
|
||||||
.attr("r", 5)
|
.attr("width", CONTAINERSIZE)
|
||||||
.attr("height", 50)
|
.attr("height", CONTAINERSIZE)
|
||||||
.attr("width", 50)
|
.attr(
|
||||||
|
"viewBox",
|
||||||
|
`${-CONTAINERSIZE / 2 + NODESIZE / 2}
|
||||||
|
${-CONTAINERSIZE / 2 + NODESIZE / 2}
|
||||||
|
${CONTAINERSIZE} ${CONTAINERSIZE}`
|
||||||
|
);
|
||||||
|
|
||||||
|
node
|
||||||
|
.append("image")
|
||||||
|
.attr("height", NODESIZE)
|
||||||
|
.attr("width", NODESIZE)
|
||||||
.attr("fill", "#69b3a2")
|
.attr("fill", "#69b3a2")
|
||||||
.attr(
|
.attr(
|
||||||
"xlink:href",
|
"xlink:href",
|
||||||
(d) => d.image || "https://legonzaur.fr/Luka/condo/2026_01/images-6.jpeg"
|
(d) => d.image || "https://legonzaur.fr/Luka/condo/2026_01/images-6.jpeg"
|
||||||
);
|
);
|
||||||
// .call(drag(simulation as unknown));
|
|
||||||
|
node
|
||||||
|
.append("text")
|
||||||
|
.attr("x", NODESIZE / 2)
|
||||||
|
.attr("y", NODESIZE + 5)
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.attr("paint-order", "stroke")
|
||||||
|
.attr("stroke", "#ffffff")
|
||||||
|
.attr("fill", "#000000")
|
||||||
|
.text((d) => d.id);
|
||||||
|
|
||||||
|
// Drag
|
||||||
|
|
||||||
|
function dragstarted(this: SVGImageElement, event: any, d: Node) {
|
||||||
|
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(this: SVGImageElement, event: any, d: Node) {
|
||||||
|
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 it’s no longer being dragged.
|
||||||
|
function dragended(this: SVGImageElement, event: any, d: Node) {
|
||||||
|
if (!event.active) simulation.alphaTarget(0);
|
||||||
|
event.subject.fx = null;
|
||||||
|
event.subject.fy = null;
|
||||||
|
}
|
||||||
|
const d = drag<SVGImageElement, Node>();
|
||||||
|
d.on("start", dragstarted).on("drag", dragged).on("end", dragended);
|
||||||
|
selectAll<SVGImageElement, Node>("image").call(d);
|
||||||
|
|
||||||
// Updating node positions
|
// Updating node positions
|
||||||
simulation.on("tick", () => {
|
simulation.on("tick", () => {
|
||||||
@@ -82,5 +162,17 @@ simulation.on("tick", () => {
|
|||||||
.attr("x2", (d) => d.target.x)
|
.attr("x2", (d) => d.target.x)
|
||||||
.attr("y2", (d) => d.target.y);
|
.attr("y2", (d) => d.target.y);
|
||||||
|
|
||||||
node.attr("x", (d) => d.x - 25).attr("y", (d) => d.y - 25);
|
lineLegend.attr("x", (d) => d.source.x + (d.target.x - d.source.x) / 2);
|
||||||
|
lineLegend.attr("y", (d) => d.source.y + (d.target.y - d.source.y) / 2);
|
||||||
|
// node
|
||||||
|
// .attr("cx", function (d) {
|
||||||
|
// return d.x;
|
||||||
|
// })
|
||||||
|
// .attr("cy", function (d) {
|
||||||
|
// return d.y;
|
||||||
|
// });
|
||||||
|
|
||||||
|
node
|
||||||
|
.attr("x", (d) => d.x - CONTAINERSIZE / 2)
|
||||||
|
.attr("y", (d) => d.y - CONTAINERSIZE / 2);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user