Changed around line 1
+ class GraphVisualizer {
+ constructor(canvasId) {
+ this.canvas = document.getElementById(canvasId);
+ this.ctx = this.canvas.getContext('2d');
+ this.nodes = [];
+ this.edges = [];
+ this.isDragging = false;
+ this.selectedNode = null;
+
+ this.initializeCanvas();
+ this.addEventListeners();
+ }
+
+ initializeCanvas() {
+ this.resizeCanvas();
+ window.addEventListener('resize', () => this.resizeCanvas());
+ }
+
+ resizeCanvas() {
+ const container = this.canvas.parentElement;
+ this.canvas.width = container.clientWidth;
+ this.canvas.height = container.clientHeight;
+ this.render();
+ }
+
+ addEventListeners() {
+ 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('touchstart', (e) => this.handleTouchStart(e));
+ this.canvas.addEventListener('touchmove', (e) => this.handleTouchMove(e));
+ this.canvas.addEventListener('touchend', () => this.handleTouchEnd());
+ }
+
+ addNode(x, y) {
+ const node = {
+ x: x || Math.random() * this.canvas.width,
+ y: y || Math.random() * this.canvas.height,
+ radius: 20,
+ color: `hsl(${Math.random() * 360}, 70%, 50%)`
+ };
+ this.nodes.push(node);
+ this.render();
+ }
+
+ render() {
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
+
+ // Draw edges
+ this.edges.forEach(edge => {
+ this.ctx.beginPath();
+ this.ctx.moveTo(edge.start.x, edge.start.y);
+ this.ctx.lineTo(edge.end.x, edge.end.y);
+ this.ctx.strokeStyle = '#999';
+ 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.color;
+ this.ctx.fill();
+ this.ctx.strokeStyle = '#fff';
+ this.ctx.lineWidth = 3;
+ this.ctx.stroke();
+ });
+ }
+
+ handleMouseDown(e) {
+ const rect = this.canvas.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+
+ this.selectedNode = this.nodes.find(node => {
+ const dx = node.x - x;
+ const dy = node.y - y;
+ return Math.sqrt(dx * dx + dy * dy) < node.radius;
+ });
+
+ if (this.selectedNode) {
+ this.isDragging = true;
+ }
+ }
+
+ handleMouseMove(e) {
+ if (this.isDragging && this.selectedNode) {
+ const rect = this.canvas.getBoundingClientRect();
+ this.selectedNode.x = e.clientX - rect.left;
+ this.selectedNode.y = e.clientY - rect.top;
+ this.render();
+ }
+ }
+
+ handleMouseUp() {
+ this.isDragging = false;
+ this.selectedNode = null;
+ }
+
+ // Touch event handlers
+ handleTouchStart(e) {
+ e.preventDefault();
+ const touch = e.touches[0];
+ const mouseEvent = new MouseEvent('mousedown', {
+ clientX: touch.clientX,
+ clientY: touch.clientY
+ });
+ this.handleMouseDown(mouseEvent);
+ }
+
+ handleTouchMove(e) {
+ e.preventDefault();
+ const touch = e.touches[0];
+ const mouseEvent = new MouseEvent('mousemove', {
+ clientX: touch.clientX,
+ clientY: touch.clientY
+ });
+ this.handleMouseMove(mouseEvent);
+ }
+
+ handleTouchEnd() {
+ this.handleMouseUp();
+ }
+ }
+
+ // Initialize the application
+ document.addEventListener('DOMContentLoaded', () => {
+ const graphViz = new GraphVisualizer('graphCanvas');
+
+ // Add button event listeners
+ document.getElementById('addNode').addEventListener('click', () => {
+ graphViz.addNode();
+ });
+
+ document.getElementById('clear').addEventListener('click', () => {
+ graphViz.nodes = [];
+ graphViz.edges = [];
+ graphViz.render();
+ });
+
+ // Add some initial nodes
+ for (let i = 0; i < 5; i++) {
+ graphViz.addNode();
+ }
+ });