diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs index 5a0e4fd..6aa1681 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs @@ -14,8 +14,17 @@ namespace AGVNavigationCore.Controls { #region Paint Events + private static int _paintCounter = 0; + private void UnifiedAGVCanvas_Paint(object sender, PaintEventArgs e) { + // 디버그: Paint 이벤트 실행 확인 + if (_isDragging) + { + _paintCounter++; + //System.Diagnostics.Debug.WriteLine($"Paint #{_paintCounter} 실행! 현재 노드 위치: ({_selectedNode?.Position.X}, {_selectedNode?.Position.Y})"); + } + var g = e.Graphics; g.SmoothingMode = SmoothingMode.AntiAlias; g.InterpolationMode = InterpolationMode.High; @@ -50,6 +59,12 @@ namespace AGVNavigationCore.Controls // 노드 그리기 (라벨 제외) DrawNodesOnly(g); + // 드래그 고스트 그리기 (노드 위에 표시되도록 나중에 그리기) + if (_isDragging && _selectedNode != null) + { + DrawDragGhost(g); + } + // AGV 그리기 DrawAGVs(g); @@ -382,7 +397,203 @@ namespace AGVNavigationCore.Controls } } + /// + /// 드래그 고스트 그리기 - 원래 위치에 반투명 노드 표시 + /// + private void DrawDragGhost(Graphics g) + { + if (_selectedNode == null || !_isDragging) return; + // 반투명 효과를 위한 브러시 생성 + Brush ghostBrush = null; + switch (_selectedNode.Type) + { + case NodeType.Normal: + ghostBrush = new SolidBrush(Color.FromArgb(120, 100, 149, 237)); // 반투명 파란색 + break; + case NodeType.Rotation: + ghostBrush = new SolidBrush(Color.FromArgb(120, 255, 165, 0)); // 반투명 주황색 + break; + case NodeType.Docking: + ghostBrush = new SolidBrush(Color.FromArgb(120, 50, 205, 50)); // 반투명 초록색 + break; + case NodeType.Charging: + ghostBrush = new SolidBrush(Color.FromArgb(120, 255, 215, 0)); // 반투명 금색 + break; + default: + ghostBrush = new SolidBrush(Color.FromArgb(120, 200, 200, 200)); // 반투명 회색 + break; + } + + // 고스트 노드 그리기 + switch (_selectedNode.Type) + { + case NodeType.Label: + DrawLabelGhost(g); + break; + case NodeType.Image: + DrawImageGhost(g); + break; + case NodeType.Docking: + DrawPentagonGhost(g, ghostBrush); + break; + case NodeType.Charging: + DrawTriangleGhost(g, ghostBrush); + break; + default: + DrawCircleGhost(g, ghostBrush); + break; + } + + ghostBrush?.Dispose(); + } + + private void DrawCircleGhost(Graphics g, Brush ghostBrush) + { + var rect = new Rectangle( + _dragStartPosition.X - NODE_RADIUS, + _dragStartPosition.Y - NODE_RADIUS, + NODE_SIZE, + NODE_SIZE + ); + g.FillEllipse(ghostBrush, rect); + // 회색 점선 테두리 + g.DrawEllipse(new Pen(Color.FromArgb(180, 128, 128, 128), 2) { DashStyle = DashStyle.Dash }, rect); + // 빨간색 외곽 테두리 (디버깅용 - 고스트가 확실히 보이도록) + var outerRect = new Rectangle(rect.X - 2, rect.Y - 2, rect.Width + 4, rect.Height + 4); + g.DrawEllipse(new Pen(Color.FromArgb(200, 255, 0, 0), 1), outerRect); + } + + private void DrawPentagonGhost(Graphics g, Brush ghostBrush) + { + var points = new Point[5]; + for (int i = 0; i < 5; i++) + { + var angle = (Math.PI * 2 * i / 5) - Math.PI / 2; + points[i] = new Point( + (int)(_dragStartPosition.X + NODE_RADIUS * Math.Cos(angle)), + (int)(_dragStartPosition.Y + NODE_RADIUS * Math.Sin(angle)) + ); + } + g.FillPolygon(ghostBrush, points); + // 회색 점선 테두리 + g.DrawPolygon(new Pen(Color.FromArgb(180, 128, 128, 128), 2) { DashStyle = DashStyle.Dash }, points); + // 빨간색 외곽 테두리 (디버깅용) + var outerPoints = new Point[5]; + for (int i = 0; i < 5; i++) + { + var angle = (Math.PI * 2 * i / 5) - Math.PI / 2; + outerPoints[i] = new Point( + (int)(_dragStartPosition.X + (NODE_RADIUS + 3) * Math.Cos(angle)), + (int)(_dragStartPosition.Y + (NODE_RADIUS + 3) * Math.Sin(angle)) + ); + } + g.DrawPolygon(new Pen(Color.FromArgb(200, 255, 0, 0), 1), outerPoints); + } + + private void DrawTriangleGhost(Graphics g, Brush ghostBrush) + { + var points = new Point[3]; + for (int i = 0; i < 3; i++) + { + var angle = (Math.PI * 2 * i / 3) - Math.PI / 2; + points[i] = new Point( + (int)(_dragStartPosition.X + NODE_RADIUS * Math.Cos(angle)), + (int)(_dragStartPosition.Y + NODE_RADIUS * Math.Sin(angle)) + ); + } + g.FillPolygon(ghostBrush, points); + // 회색 점선 테두리 + g.DrawPolygon(new Pen(Color.FromArgb(180, 128, 128, 128), 2) { DashStyle = DashStyle.Dash }, points); + // 빨간색 외곽 테두리 (디버깅용) + var outerPoints = new Point[3]; + for (int i = 0; i < 3; i++) + { + var angle = (Math.PI * 2 * i / 3) - Math.PI / 2; + outerPoints[i] = new Point( + (int)(_dragStartPosition.X + (NODE_RADIUS + 3) * Math.Cos(angle)), + (int)(_dragStartPosition.Y + (NODE_RADIUS + 3) * Math.Sin(angle)) + ); + } + g.DrawPolygon(new Pen(Color.FromArgb(200, 255, 0, 0), 1), outerPoints); + } + + private void DrawLabelGhost(Graphics g) + { + var text = string.IsNullOrEmpty(_selectedNode.LabelText) ? _selectedNode.NodeId : _selectedNode.LabelText; + var font = new Font(_selectedNode.FontFamily, _selectedNode.FontSize, _selectedNode.FontStyle); + var textBrush = new SolidBrush(Color.FromArgb(120, _selectedNode.ForeColor)); + var textSize = g.MeasureString(text, font); + var textPoint = new Point( + (int)(_dragStartPosition.X - textSize.Width / 2), + (int)(_dragStartPosition.Y - textSize.Height / 2) + ); + + if (_selectedNode.ShowBackground) + { + var backgroundBrush = new SolidBrush(Color.FromArgb(120, _selectedNode.BackColor)); + var backgroundRect = new Rectangle( + textPoint.X - _selectedNode.Padding, + textPoint.Y - _selectedNode.Padding, + (int)textSize.Width + (_selectedNode.Padding * 2), + (int)textSize.Height + (_selectedNode.Padding * 2) + ); + g.FillRectangle(backgroundBrush, backgroundRect); + g.DrawRectangle(new Pen(Color.FromArgb(180, 128, 128, 128), 2) { DashStyle = DashStyle.Dash }, backgroundRect); + backgroundBrush.Dispose(); + } + + g.DrawString(text, font, textBrush, textPoint); + + // 배경이 없어도 테두리 표시 + if (!_selectedNode.ShowBackground) + { + var borderRect = new Rectangle( + textPoint.X - 2, + textPoint.Y - 2, + (int)textSize.Width + 4, + (int)textSize.Height + 4 + ); + g.DrawRectangle(new Pen(Color.FromArgb(180, 128, 128, 128), 2) { DashStyle = DashStyle.Dash }, borderRect); + } + + // 빨간색 외곽 테두리 (디버깅용) + var outerRect = new Rectangle( + textPoint.X - 4, + textPoint.Y - 4, + (int)textSize.Width + 8, + (int)textSize.Height + 8 + ); + g.DrawRectangle(new Pen(Color.FromArgb(200, 255, 0, 0), 1), outerRect); + + font.Dispose(); + textBrush.Dispose(); + } + + private void DrawImageGhost(Graphics g) + { + var displaySize = _selectedNode.GetDisplaySize(); + if (displaySize.IsEmpty) + displaySize = new Size(50, 50); + + var imageRect = new Rectangle( + _dragStartPosition.X - displaySize.Width / 2, + _dragStartPosition.Y - displaySize.Height / 2, + displaySize.Width, + displaySize.Height + ); + + // 반투명 회색 사각형 + using (var ghostBrush = new SolidBrush(Color.FromArgb(120, 200, 200, 200))) + { + g.FillRectangle(ghostBrush, imageRect); + g.DrawRectangle(new Pen(Color.FromArgb(180, 128, 128, 128), 2) { DashStyle = DashStyle.Dash }, imageRect); + } + + // 빨간색 외곽 테두리 (디버깅용) + var outerRect = new Rectangle(imageRect.X - 2, imageRect.Y - 2, imageRect.Width + 4, imageRect.Height + 4); + g.DrawRectangle(new Pen(Color.FromArgb(200, 255, 0, 0), 1), outerRect); + } private void DrawNodesOnly(Graphics g) { @@ -444,19 +655,42 @@ namespace AGVNavigationCore.Controls private void DrawCircleNodeShape(Graphics g, MapNode node, Brush brush) { + // 드래그 중인 노드는 약간 크게 그리기 + bool isDraggingThisNode = _isDragging && node == _selectedNode; + int sizeAdjustment = isDraggingThisNode ? 4 : 0; + var rect = new Rectangle( - node.Position.X - NODE_RADIUS, - node.Position.Y - NODE_RADIUS, - NODE_SIZE, - NODE_SIZE + node.Position.X - NODE_RADIUS - sizeAdjustment, + node.Position.Y - NODE_RADIUS - sizeAdjustment, + NODE_SIZE + sizeAdjustment * 2, + NODE_SIZE + sizeAdjustment * 2 ); + // 드래그 중인 노드의 그림자 효과 + if (isDraggingThisNode) + { + var shadowRect = new Rectangle(rect.X + 3, rect.Y + 3, rect.Width, rect.Height); + using (var shadowBrush = new SolidBrush(Color.FromArgb(100, 0, 0, 0))) + { + g.FillEllipse(shadowBrush, shadowRect); + } + } + // 노드 그리기 g.FillEllipse(brush, rect); g.DrawEllipse(Pens.Black, rect); + // 드래그 중인 노드 강조 (가장 강력한 효과) + if (isDraggingThisNode) + { + // 청록색 두꺼운 테두리 + g.DrawEllipse(new Pen(Color.Cyan, 3), rect); + // 펄스 효과 + var pulseRect = new Rectangle(rect.X - 4, rect.Y - 4, rect.Width + 8, rect.Height + 8); + g.DrawEllipse(new Pen(Color.FromArgb(150, 0, 255, 255), 2) { DashStyle = DashStyle.Dash }, pulseRect); + } // 선택된 노드 강조 - if (node == _selectedNode) + else if (node == _selectedNode) { g.DrawEllipse(_selectedNodePen, rect); } @@ -472,8 +706,8 @@ namespace AGVNavigationCore.Controls g.DrawEllipse(new Pen(Color.Gold, 2) { DashStyle = DashStyle.Dash }, pulseRect); } - // 호버된 노드 강조 - if (node == _hoveredNode) + // 호버된 노드 강조 (드래그 중이 아닐 때만) + if (node == _hoveredNode && !isDraggingThisNode) { var hoverRect = new Rectangle(rect.X - 2, rect.Y - 2, rect.Width + 4, rect.Height + 4); g.DrawEllipse(new Pen(Color.Orange, 2), hoverRect); @@ -488,7 +722,11 @@ namespace AGVNavigationCore.Controls private void DrawPentagonNodeShape(Graphics g, MapNode node, Brush brush) { - var radius = NODE_RADIUS; + // 드래그 중인 노드는 약간 크게 그리기 + bool isDraggingThisNode = _isDragging && node == _selectedNode; + int radiusAdjustment = isDraggingThisNode ? 4 : 0; + + var radius = NODE_RADIUS + radiusAdjustment; var center = node.Position; // 5각형 꼭짓점 계산 (위쪽부터 시계방향) @@ -502,12 +740,43 @@ namespace AGVNavigationCore.Controls ); } + // 드래그 중인 노드의 그림자 효과 + if (isDraggingThisNode) + { + var shadowPoints = new Point[5]; + for (int i = 0; i < 5; i++) + { + shadowPoints[i] = new Point(points[i].X + 3, points[i].Y + 3); + } + using (var shadowBrush = new SolidBrush(Color.FromArgb(100, 0, 0, 0))) + { + g.FillPolygon(shadowBrush, shadowPoints); + } + } + // 5각형 그리기 g.FillPolygon(brush, points); g.DrawPolygon(Pens.Black, points); + // 드래그 중인 노드 강조 (가장 강력한 효과) + if (isDraggingThisNode) + { + // 청록색 두꺼운 테두리 + g.DrawPolygon(new Pen(Color.Cyan, 3), points); + // 펄스 효과 + var pulsePoints = new Point[5]; + for (int i = 0; i < 5; i++) + { + var angle = (Math.PI * 2 * i / 5) - Math.PI / 2; + pulsePoints[i] = new Point( + (int)(center.X + (radius + 5) * Math.Cos(angle)), + (int)(center.Y + (radius + 5) * Math.Sin(angle)) + ); + } + g.DrawPolygon(new Pen(Color.FromArgb(150, 0, 255, 255), 2) { DashStyle = DashStyle.Dash }, pulsePoints); + } // 선택된 노드 강조 - if (node == _selectedNode) + else if (node == _selectedNode) { g.DrawPolygon(_selectedNodePen, points); } @@ -531,8 +800,8 @@ namespace AGVNavigationCore.Controls g.DrawPolygon(new Pen(Color.Gold, 2) { DashStyle = DashStyle.Dash }, pulsePoints); } - // 호버된 노드 강조 - if (node == _hoveredNode) + // 호버된 노드 강조 (드래그 중이 아닐 때만) + if (node == _hoveredNode && !isDraggingThisNode) { // 확장된 5각형 계산 var hoverPoints = new Point[5]; @@ -556,7 +825,11 @@ namespace AGVNavigationCore.Controls private void DrawTriangleNodeShape(Graphics g, MapNode node, Brush brush) { - var radius = NODE_RADIUS; + // 드래그 중인 노드는 약간 크게 그리기 + bool isDraggingThisNode = _isDragging && node == _selectedNode; + int radiusAdjustment = isDraggingThisNode ? 4 : 0; + + var radius = NODE_RADIUS + radiusAdjustment; var center = node.Position; // 삼각형 꼭짓점 계산 (위쪽 꼭짓점부터 시계방향) @@ -570,12 +843,43 @@ namespace AGVNavigationCore.Controls ); } + // 드래그 중인 노드의 그림자 효과 + if (isDraggingThisNode) + { + var shadowPoints = new Point[3]; + for (int i = 0; i < 3; i++) + { + shadowPoints[i] = new Point(points[i].X + 3, points[i].Y + 3); + } + using (var shadowBrush = new SolidBrush(Color.FromArgb(100, 0, 0, 0))) + { + g.FillPolygon(shadowBrush, shadowPoints); + } + } + // 삼각형 그리기 g.FillPolygon(brush, points); g.DrawPolygon(Pens.Black, points); + // 드래그 중인 노드 강조 (가장 강력한 효과) + if (isDraggingThisNode) + { + // 청록색 두꺼운 테두리 + g.DrawPolygon(new Pen(Color.Cyan, 3), points); + // 펄스 효과 + var pulsePoints = new Point[3]; + for (int i = 0; i < 3; i++) + { + var angle = (Math.PI * 2 * i / 3) - Math.PI / 2; + pulsePoints[i] = new Point( + (int)(center.X + (radius + 5) * Math.Cos(angle)), + (int)(center.Y + (radius + 5) * Math.Sin(angle)) + ); + } + g.DrawPolygon(new Pen(Color.FromArgb(150, 0, 255, 255), 2) { DashStyle = DashStyle.Dash }, pulsePoints); + } // 선택된 노드 강조 - if (node == _selectedNode) + else if (node == _selectedNode) { g.DrawPolygon(_selectedNodePen, points); } @@ -599,8 +903,8 @@ namespace AGVNavigationCore.Controls g.DrawPolygon(new Pen(Color.Gold, 2) { DashStyle = DashStyle.Dash }, pulsePoints); } - // 호버된 노드 강조 - if (node == _hoveredNode) + // 호버된 노드 강조 (드래그 중이 아닐 때만) + if (node == _hoveredNode && !isDraggingThisNode) { // 확장된 삼각형 계산 var hoverPoints = new Point[3]; @@ -662,7 +966,7 @@ namespace AGVNavigationCore.Controls var descPoint = new Point( (int)(node.Position.X - descSize.Width / 2), (int)(node.Position.Y + NODE_RADIUS + 2) - ); + ); // 설명 텍스트 그리기 (설명이 있는 경우에만) if (!string.IsNullOrEmpty(descriptionText)) @@ -725,6 +1029,9 @@ namespace AGVNavigationCore.Controls private void DrawLabelNode(Graphics g, MapNode node) { + // 드래그 중인 노드 확인 + bool isDraggingThisNode = _isDragging && node == _selectedNode; + var text = string.IsNullOrEmpty(node.LabelText) ? node.NodeId : node.LabelText; // 폰트 설정 @@ -738,6 +1045,16 @@ namespace AGVNavigationCore.Controls (int)(node.Position.Y - textSize.Height / 2) ); + // 드래그 중일 때 그림자 효과 + if (isDraggingThisNode) + { + var shadowPoint = new Point(textPoint.X + 3, textPoint.Y + 3); + using (var shadowBrush = new SolidBrush(Color.FromArgb(100, 0, 0, 0))) + { + g.DrawString(text, font, shadowBrush, shadowPoint); + } + } + // 배경 그리기 (설정된 경우) if (node.ShowBackground) { @@ -756,8 +1073,24 @@ namespace AGVNavigationCore.Controls // 텍스트 그리기 g.DrawString(text, font, textBrush, textPoint); + // 드래그 중인 노드 강조 (가장 강력한 효과) + if (isDraggingThisNode) + { + var dragPadding = node.Padding + 4; + var dragRect = new Rectangle( + textPoint.X - dragPadding, + textPoint.Y - dragPadding, + (int)textSize.Width + (dragPadding * 2), + (int)textSize.Height + (dragPadding * 2) + ); + g.DrawRectangle(new Pen(Color.Cyan, 3), dragRect); + + // 펄스 효과 + var pulseRect = new Rectangle(dragRect.X - 2, dragRect.Y - 2, dragRect.Width + 4, dragRect.Height + 4); + g.DrawRectangle(new Pen(Color.FromArgb(150, 0, 255, 255), 2) { DashStyle = DashStyle.Dash }, pulseRect); + } // 선택된 노드 강조 - if (node == _selectedNode) + else if (node == _selectedNode) { var selectionPadding = node.Padding + 2; var selectionRect = new Rectangle( @@ -769,8 +1102,8 @@ namespace AGVNavigationCore.Controls g.DrawRectangle(_selectedNodePen, selectionRect); } - // 호버된 노드 강조 - if (node == _hoveredNode) + // 호버된 노드 강조 (드래그 중이 아닐 때만) + if (node == _hoveredNode && !isDraggingThisNode) { var hoverPadding = node.Padding + 4; var hoverRect = new Rectangle( @@ -788,6 +1121,9 @@ namespace AGVNavigationCore.Controls private void DrawImageNode(Graphics g, MapNode node) { + // 드래그 중인 노드 확인 + bool isDraggingThisNode = _isDragging && node == _selectedNode; + // 이미지 로드 (필요시) if (node.LoadedImage == null && !string.IsNullOrEmpty(node.ImagePath)) { @@ -801,6 +1137,12 @@ namespace AGVNavigationCore.Controls if (displaySize.IsEmpty) displaySize = new Size(50, 50); // 기본 크기 + // 드래그 중일 때 약간 크게 표시 + if (isDraggingThisNode) + { + displaySize = new Size((int)(displaySize.Width * 1.1), (int)(displaySize.Height * 1.1)); + } + var imageRect = new Rectangle( node.Position.X - displaySize.Width / 2, node.Position.Y - displaySize.Height / 2, @@ -808,6 +1150,16 @@ namespace AGVNavigationCore.Controls displaySize.Height ); + // 드래그 중일 때 그림자 효과 + if (isDraggingThisNode) + { + var shadowRect = new Rectangle(imageRect.X + 3, imageRect.Y + 3, imageRect.Width, imageRect.Height); + using (var shadowBrush = new SolidBrush(Color.FromArgb(100, 0, 0, 0))) + { + g.FillRectangle(shadowBrush, shadowRect); + } + } + // 회전이 있는 경우 if (node.Rotation != 0) { @@ -855,14 +1207,21 @@ namespace AGVNavigationCore.Controls } } + // 드래그 중인 노드 강조 (가장 강력한 효과) + if (isDraggingThisNode) + { + g.DrawRectangle(new Pen(Color.Cyan, 3), imageRect); + var pulseRect = new Rectangle(imageRect.X - 3, imageRect.Y - 3, imageRect.Width + 6, imageRect.Height + 6); + g.DrawRectangle(new Pen(Color.FromArgb(150, 0, 255, 255), 2) { DashStyle = DashStyle.Dash }, pulseRect); + } // 선택된 노드 강조 - if (node == _selectedNode) + else if (node == _selectedNode) { g.DrawRectangle(_selectedNodePen, imageRect); } - // 호버된 노드 강조 - if (node == _hoveredNode) + // 호버된 노드 강조 (드래그 중이 아닐 때만) + if (node == _hoveredNode && !isDraggingThisNode) { var hoverRect = new Rectangle(imageRect.X - 2, imageRect.Y - 2, imageRect.Width + 4, imageRect.Height + 4); g.DrawRectangle(new Pen(Color.Orange, 2), hoverRect); @@ -872,13 +1231,24 @@ namespace AGVNavigationCore.Controls else { // 이미지가 없는 경우 기본 사각형으로 표시 + int sizeAdjustment = isDraggingThisNode ? 5 : 0; var rect = new Rectangle( - node.Position.X - 25, - node.Position.Y - 25, - 50, - 50 + node.Position.X - 25 - sizeAdjustment, + node.Position.Y - 25 - sizeAdjustment, + 50 + sizeAdjustment * 2, + 50 + sizeAdjustment * 2 ); + // 드래그 중일 때 그림자 효과 + if (isDraggingThisNode) + { + var shadowRect = new Rectangle(rect.X + 3, rect.Y + 3, rect.Width, rect.Height); + using (var shadowBrush = new SolidBrush(Color.FromArgb(100, 0, 0, 0))) + { + g.FillRectangle(shadowBrush, shadowRect); + } + } + g.FillRectangle(Brushes.LightGray, rect); g.DrawRectangle(Pens.Black, rect); @@ -893,14 +1263,21 @@ namespace AGVNavigationCore.Controls g.DrawString(text, font, Brushes.Black, textPoint); font.Dispose(); + // 드래그 중인 노드 강조 (가장 강력한 효과) + if (isDraggingThisNode) + { + g.DrawRectangle(new Pen(Color.Cyan, 3), rect); + var pulseRect = new Rectangle(rect.X - 3, rect.Y - 3, rect.Width + 6, rect.Height + 6); + g.DrawRectangle(new Pen(Color.FromArgb(150, 0, 255, 255), 2) { DashStyle = DashStyle.Dash }, pulseRect); + } // 선택된 노드 강조 - if (node == _selectedNode) + else if (node == _selectedNode) { g.DrawRectangle(_selectedNodePen, rect); } - // 호버된 노드 강조 - if (node == _hoveredNode) + // 호버된 노드 강조 (드래그 중이 아닐 때만) + if (node == _hoveredNode && !isDraggingThisNode) { var hoverRect = new Rectangle(rect.X - 2, rect.Y - 2, rect.Width + 4, rect.Height + 4); g.DrawRectangle(new Pen(Color.Orange, 2), hoverRect); @@ -1600,6 +1977,7 @@ namespace AGVNavigationCore.Controls g.DrawString(scaleText, font, Brushes.Black, scaleRect.X + 5, scaleRect.Y + 2); } + // 측정 정보 (우하단 - 사용자 정의 정보가 있을 경우) if (!string.IsNullOrEmpty(_measurementInfo)) { diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Mouse.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Mouse.cs index efb0df7..ae488d0 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Mouse.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Mouse.cs @@ -143,12 +143,17 @@ namespace AGVNavigationCore.Controls if (hitNode != null) { _isDragging = true; + _isPanning = false; // 🔥 팬 모드 비활성화 - 중요! _selectedNode = hitNode; + _dragStartPosition = hitNode.Position; // 원래 위치 저장 (고스트용) _dragOffset = new Point( worldPoint.X - hitNode.Position.X, worldPoint.Y - hitNode.Position.Y ); + _mouseMoveCounter = 0; // 디버그: 카운터 리셋 Cursor = Cursors.SizeAll; + Capture = true; // 🔥 마우스 캡처 활성화 - 이게 핵심! + //System.Diagnostics.Debug.WriteLine($"MouseDown: 드래그 시작! Capture={Capture}, isDragging={_isDragging}, isPanning={_isPanning}, Node={hitNode.NodeId}"); Invalidate(); return; } @@ -158,6 +163,7 @@ namespace AGVNavigationCore.Controls _isPanning = true; _lastMousePosition = e.Location; Cursor = Cursors.SizeAll; + Capture = true; // 🔥 마우스 캡처 활성화 } else if (e.Button == MouseButtons.Middle) { @@ -165,6 +171,7 @@ namespace AGVNavigationCore.Controls _isPanning = true; _lastMousePosition = e.Location; Cursor = Cursors.Hand; + Capture = true; // 🔥 마우스 캡처 활성화 } else if (e.Button == MouseButtons.Right) { @@ -181,6 +188,12 @@ namespace AGVNavigationCore.Controls { var worldPoint = ScreenToWorld(e.Location); + // 디버그: MouseMove 카운터 증가 + if (_isDragging) + { + _mouseMoveCounter++; + } + // 호버 노드 업데이트 var newHoveredNode = GetNodeAt(worldPoint); if (newHoveredNode != _hoveredNode) @@ -191,21 +204,25 @@ namespace AGVNavigationCore.Controls if (_isPanning) { - // 팬 처리 + // 팬 처리 - 줌 레벨 보정 적용 var deltaX = e.X - _lastMousePosition.X; var deltaY = e.Y - _lastMousePosition.Y; - _panOffset.X += deltaX; - _panOffset.Y += deltaY; + // 🔥 스크린 좌표 델타를 월드 좌표 델타로 변환 (줌 레벨 보정) + // PointF로 float 정밀도 유지 (소수점 손실 방지) + _panOffset.X += deltaX / _zoomFactor; + _panOffset.Y += deltaY / _zoomFactor; _lastMousePosition = e.Location; Invalidate(); + Update(); // 팬도 실시간 업데이트 } else if (_isDragging && _canvasMode == CanvasMode.Edit) { // 노드 드래그 if (_selectedNode != null) { + var oldPosition = _selectedNode.Position; var newPosition = new Point( worldPoint.X - _dragOffset.X, worldPoint.Y - _dragOffset.Y @@ -222,6 +239,7 @@ namespace AGVNavigationCore.Controls NodeMoved?.Invoke(this, _selectedNode); MapChanged?.Invoke(this, EventArgs.Empty); Invalidate(); + Update(); // 🔥 즉시 Paint 이벤트 처리하여 화면 업데이트 } } else if (_isConnectionMode && _canvasMode == CanvasMode.Edit) @@ -242,12 +260,14 @@ namespace AGVNavigationCore.Controls if (_isDragging && _canvasMode == CanvasMode.Edit) { _isDragging = false; + Capture = false; // 🔥 마우스 캡처 해제 Cursor = GetCursorForMode(_editMode); } if (_isPanning) { _isPanning = false; + Capture = false; // 🔥 마우스 캡처 해제 Cursor = Cursors.Default; } } @@ -257,6 +277,7 @@ namespace AGVNavigationCore.Controls if (_isPanning) { _isPanning = false; + Capture = false; // 🔥 마우스 캡처 해제 Cursor = Cursors.Default; } } @@ -896,7 +917,7 @@ namespace AGVNavigationCore.Controls public void ResetZoom() { _zoomFactor = 1.0f; - _panOffset = Point.Empty; + _panOffset = PointF.Empty; Invalidate(); } diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs index c340465..ee9a9ad 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs @@ -88,15 +88,17 @@ namespace AGVNavigationCore.Controls // 편집 관련 (EditMode에서만 사용) private bool _isDragging; private Point _dragOffset; + private Point _dragStartPosition; // 드래그 시작 위치 (고스트 표시용) private Point _lastMousePosition; private bool _isConnectionMode; private MapNode _connectionStartNode; private Point _connectionEndPoint; + private int _mouseMoveCounter = 0; // 디버그용: MouseMove 실행 횟수 // 그리드 및 줌 관련 private bool _showGrid = true; private float _zoomFactor = 1.0f; - private Point _panOffset = Point.Empty; + private PointF _panOffset = PointF.Empty; // float 정밀도로 변경 (팬 이동 정확도 개선) private bool _isPanning; // 자동 증가 카운터