diff --git a/index.html b/index.html
new file mode 100644
index 0000000..5ef1704
--- /dev/null
+++ b/index.html
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/script.js b/script.js
new file mode 100644
index 0000000..fa7387c
--- /dev/null
+++ b/script.js
@@ -0,0 +1,195 @@
+const SVG_WIDTH = 960;
+const SVG_HEIGHT = 500;
+
+const BOX_WIDTH = 60;
+const BOX_HEIGHT = 20;
+
+const graph = {
+ nodes: [
+ {
+ name: "from",
+ fixed: true,
+ x: 0,
+ y: 0,
+ w: BOX_WIDTH,
+ h: BOX_HEIGHT,
+ },
+ {
+ name: 'to',
+ fixed: true,
+ x: 250,
+ y: 250,
+ w: BOX_WIDTH,
+ h: BOX_HEIGHT,
+ }
+ ],
+ links: [
+ {
+ // id: 0,
+ source: 0,
+ target: 1,
+ }
+ ]
+}
+
+const generateMarkerId = link => `arrowhead-${link.source.index}-${link.target.index}`.replace(/\s/g, "-");
+
+const svg = d3
+ .select("body")
+ .append("svg")
+ .attr("width", SVG_WIDTH)
+ .attr("height", SVG_HEIGHT);
+
+const forceLink = d3
+ .forceLink()
+ .id(link => link.id)
+ .strength(_ => 0.002);
+
+const forceNode = d3
+ .forceManyBody()
+ .strength(-400);
+
+const forceDragDrop = d3
+ .drag()
+ .on("start", node => {
+ node.fx = node.x;
+ node.fy = node.y;
+ })
+ .on("drag", (event, node) => {
+ force.alphaTarget(0.1).restart();
+ node.fx = event.x;
+ node.fy = event.y;
+ })
+ .on("end", (event, node) => {
+ if (!event.active) force.alphaTarget(0);
+ node.fx = null;
+ node.fy = null;
+ })
+
+const force = d3
+ .forceSimulation()
+ .force("charge", forceNode)
+ .force("link", forceLink);
+
+const link = svg
+ .selectAll(".link")
+ .data(graph.links)
+ .enter()
+ .append("line")
+ .attr("class", "link")
+ .style("stroke-width", "5")
+ .attr("marker-end", "url(#arrow)")
+ .each(link => {
+ svg
+ .append("marker")
+ .attr("id", generateMarkerId(link))
+ .attr("viewBox", "0 -5 10 5")
+ .attr("markerWidth", 2)
+ .attr("markerHeight", 4)
+ .attr("orient", "auto")
+ .attr("refX", 30)
+ .attr("refY", 0)
+ .append("path")
+ .attr("d", "M 0 5 L 10 0 L 0 -5 Z")
+ .attr("fill", link.color); // Set the fill color to the link's color
+ })
+ .attr("marker-end", link => `url(#${generateMarkerId(link)})`)
+ .call(forceDragDrop)
+
+const node = svg
+ .selectAll(".node")
+ .data(graph.nodes)
+ .enter()
+ .append("rect")
+ .attr("class", "node")
+ .attr("width", d => d.w)
+ .attr("height", d => d.h)
+ .style("fill", "blue")
+ .call(forceDragDrop)
+ .append("title")
+ .text(d => d.name);
+
+// force.start();
+
+force.on("tick", () => {
+ console.log("lmao")
+ link
+ .attr("x1", d => d.source.x) // + d.source.w / 2
+ .attr("y1", d => d.source.y) // + d.source.h / 2
+ .attr("x2", d => d.target.x) // + d.target.w / 2
+ .attr("y2", d => d.target.y) // + d.target.h / 2
+ // .each(link => {
+ // const markerId = generateMarkerId(link);
+
+ // const linkAngle = angle_between(
+ // link.source.x,
+ // -link.source.y,
+ // link.target.x,
+ // -link.target.y
+ // );
+
+ // const intersectionPoint = box_intersection(
+ // linkAngle,
+ // link.target.w,
+ // link.target.h,
+ // 0
+ // );
+
+ // const x = d3.select(`#${markerId}`)
+ // const distance = Math.sqrt( ((intersectionPoint.x)**2) + ((intersectionPoint.y) ** 2) );
+ // //console.log(distance)
+ // x.attr('refX', _ => distance);
+ // });
+
+ node
+ .attr("x", d => d.x)
+ .attr("y", d => d.y);
+});
+
+const angle_between = (x1, y1, x2, y2) => {
+ let angle = Math.atan2(y2-y1, x2-x1);
+ if (angle < 0) angle += 2*Math.PI
+ return angle;
+}
+
+const mod = (n, m) => ((n % m) + m) % m;
+
+const hits_border_radius = (angle, box_width, box_height, border_radius) => {
+ const height = box_height / 2;
+ const width = box_width / 2;
+
+ const p1 = [width, height - border_radius]
+ const p2 = [width - border_radius, height]
+
+ const border_angle1 = angle_between(0,0, p1[0], p1[1])
+ const border_angle2 = angle_between(0,0, p2[0], p2[1])
+
+ return [
+ angle,
+ mod(angle-(Math.PI/2), Math.PI*2),
+ mod(angle-(Math.PI), Math.PI*2),
+ mod(angle-(3*Math.PI/2), Math.PI*2)
+ ].some(angle => border_angle1 < angle && angle < border_angle2);
+}
+
+const box_intersection = (angle, box_width, box_height, border_radius) => {
+ const half_height = box_height / 2;
+ const half_width = box_width / 2;
+
+ if (hits_border_radius(angle, half_width, half_height, border_radius)) {
+ return arc_border_box_intersection(angle, half_width, half_height, border_radius)
+ }
+
+ return straight_border_box_intersection(angle, half_width, half_height)
+}
+
+const straight_border_box_intersection = (angle, box_width, box_height) => {
+ return {y: Math.cos(angle)*box_width, x: Math.sin(angle)*box_height}
+}
+
+const arc_border_box_intersection = (angle, box_width, box_height, border_radius) => {
+ //TODO:
+ return 1;
+}
+
+// console.log(straight_border_box_intersection(angle_between(10,0,0,10), 10, 10))