Changed around line 1
+ class GraphVisualizer {
+ constructor() {
+ this.canvas = document.getElementById('graphCanvas');
+ this.ctx = this.canvas.getContext('2d');
+ this.nodes = [];
+ this.edges = [];
+ this.isDragging = false;
+ this.selectedNode = null;
+ this.mode = 'node';
+
+ this.initializeCanvas();
+ this.addEventListeners();
+ this.animate();
+ }
+
+ initializeCanvas() {
+ this.resizeCanvas();
+ window.addEventListener('resize', () => this.resizeCanvas());
+ }
+
+ resizeCanvas() {
+ this.canvas.width = this.canvas.offsetWidth;
+ this.canvas.height = this.canvas.offsetHeight;
+ }
+
+ addEventListeners() {
+ document.getElementById('addNode').addEventListener('click', () => this.mode = 'node');
+ document.getElementById('addEdge').addEventListener('click', () => this.mode = 'edge');
+ document.getElementById('clear').addEventListener('click', () => this.clear());
+
+ this.canvas.addEventListener('mousedown', (e) => this.handleMouseDown(e));
+ this.canvas.addEventListener('mousemove', (e) => this.handleMouseMove(e));
+ this.canvas.addEventListener('mouseup', () => this.handleMouseUp());
+ this.canvas.addEventListener('dblclick', (e) => this.handleDoubleClick(e));
+ }
+
+ getMousePos(e) {
+ const rect = this.canvas.getBoundingClientRect();
+ return {
+ x: e.clientX - rect.left,
+ y: e.clientY - rect.top
+ };
+ }
+
+ handleMouseDown(e) {
+ const pos = this.getMousePos(e);
+ const clickedNode = this.findNodeAtPosition(pos);
+
+ if (clickedNode) {
+ if (this.mode === 'edge' && this.selectedNode) {
+ this.addEdge(this.selectedNode, clickedNode);
+ this.selectedNode = null;
+ } else if (this.mode === 'edge') {
+ this.selectedNode = clickedNode;
+ } else {
+ this.isDragging = true;
+ this.selectedNode = clickedNode;
+ }
+ } else if (this.mode === 'node') {
+ this.addNode(pos.x, pos.y);
+ }
+ }
+
+ handleMouseMove(e) {
+ if (this.isDragging && this.selectedNode) {
+ const pos = this.getMousePos(e);
+ this.selectedNode.x = pos.x;
+ this.selectedNode.y = pos.y;
+ }
+ }
+
+ handleMouseUp() {
+ this.isDragging = false;
+ }
+
+ handleDoubleClick(e) {
+ const pos = this.getMousePos(e);
+ const node = this.findNodeAtPosition(pos);
+ if (node) {
+ this.removeNode(node);
+ }
+ }
+
+ addNode(x, y) {
+ this.nodes.push({
+ x,
+ y,
+ radius: 20,
+ color: this.getRandomColor()
+ });
+ }
+
+ addEdge(node1, node2) {
+ if (node1 !== node2) {
+ this.edges.push({
+ from: node1,
+ to: node2
+ });
+ }
+ }
+
+ removeNode(node) {
+ const index = this.nodes.indexOf(node);
+ if (index > -1) {
+ this.nodes.splice(index, 1);
+ this.edges = this.edges.filter(edge =>
+ edge.from !== node && edge.to !== node
+ );
+ }
+ }
+
+ clear() {
+ this.nodes = [];
+ this.edges = [];
+ this.selectedNode = null;
+ }
+
+ findNodeAtPosition(pos) {
+ return this.nodes.find(node =>
+ Math.hypot(node.x - pos.x, node.y - pos.y) < node.radius
+ );
+ }
+
+ getRandomColor() {
+ const colors = ['#2ecc71', '#3498db', '#e74c3c', '#f1c40f', '#9b59b6'];
+ return colors[Math.floor(Math.random() * colors.length)];
+ }
+
+ draw() {
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
+
+ // Draw edges
+ this.edges.forEach(edge => {
+ this.ctx.beginPath();
+ this.ctx.moveTo(edge.from.x, edge.from.y);
+ this.ctx.lineTo(edge.to.x, edge.to.y);
+ this.ctx.strokeStyle = '#95a5a6';
+ this.ctx.lineWidth = 2;
+ this.ctx.stroke();
+ });
+
+ // Draw nodes
+ this.nodes.forEach(node => {
+ this.ctx.beginPath();
+ this.ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
+ this.ctx.fillStyle = node === this.selectedNode ? '#e74c3c' : node.color;
+ this.ctx.fill();
+ this.ctx.strokeStyle = '#2c3e50';
+ this.ctx.lineWidth = 2;
+ this.ctx.stroke();
+ });
+ }
+
+ animate() {
+ this.draw();
+ requestAnimationFrame(() => this.animate());
+ }
+ }
+
+ document.addEventListener('DOMContentLoaded', () => {
+ new GraphVisualizer();
+ });