fix: 좌표 변환 시스템 수정 - Matrix 역변환 적용
ScreenToWorld 함수를 Matrix 역변환 방식으로 변경하여 줌/팬 상태에서도 정확한 마우스 좌표 변환 구현. 좌측 영역 객체 선택 문제 해결. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -14,8 +14,6 @@ namespace AGVNavigationCore.Controls
|
|||||||
{
|
{
|
||||||
Focus(); // 포커스 설정
|
Focus(); // 포커스 설정
|
||||||
|
|
||||||
if (_canvasMode == CanvasMode.ViewOnly) return;
|
|
||||||
|
|
||||||
var worldPoint = ScreenToWorld(e.Location);
|
var worldPoint = ScreenToWorld(e.Location);
|
||||||
var hitNode = GetNodeAt(worldPoint);
|
var hitNode = GetNodeAt(worldPoint);
|
||||||
|
|
||||||
@@ -45,8 +43,6 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
private void UnifiedAGVCanvas_MouseDoubleClick(object sender, MouseEventArgs e)
|
private void UnifiedAGVCanvas_MouseDoubleClick(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
if (_canvasMode == CanvasMode.ViewOnly) return;
|
|
||||||
|
|
||||||
var worldPoint = ScreenToWorld(e.Location);
|
var worldPoint = ScreenToWorld(e.Location);
|
||||||
var hitNode = GetNodeAt(worldPoint);
|
var hitNode = GetNodeAt(worldPoint);
|
||||||
|
|
||||||
@@ -63,18 +59,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
if (e.Button == MouseButtons.Left)
|
if (e.Button == MouseButtons.Left)
|
||||||
{
|
{
|
||||||
// 목적지 선택 모드 처리 (시뮬레이터)
|
if (_editMode == EditMode.Move)
|
||||||
if (_editMode == EditMode.SelectTarget)
|
|
||||||
{
|
|
||||||
var hitNode = GetNodeAt(worldPoint);
|
|
||||||
if (hitNode != null)
|
|
||||||
{
|
|
||||||
TargetNodeSelected?.Invoke(this, hitNode);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_canvasMode == CanvasMode.Edit && _editMode == EditMode.Move)
|
|
||||||
{
|
{
|
||||||
var hitNode = GetNodeAt(worldPoint);
|
var hitNode = GetNodeAt(worldPoint);
|
||||||
if (hitNode != null)
|
if (hitNode != null)
|
||||||
@@ -208,10 +193,17 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
private Point ScreenToWorld(Point screenPoint)
|
private Point ScreenToWorld(Point screenPoint)
|
||||||
{
|
{
|
||||||
return new Point(
|
// 변환 행렬 생성 (렌더링과 동일)
|
||||||
(int)((screenPoint.X - _panOffset.X) / _zoomFactor),
|
var transform = new System.Drawing.Drawing2D.Matrix();
|
||||||
(int)((screenPoint.Y - _panOffset.Y) / _zoomFactor)
|
transform.Scale(_zoomFactor, _zoomFactor);
|
||||||
);
|
transform.Translate(_panOffset.X, _panOffset.Y);
|
||||||
|
|
||||||
|
// 역변환 행렬로 화면 좌표를 월드 좌표로 변환
|
||||||
|
transform.Invert();
|
||||||
|
var points = new System.Drawing.PointF[] { new System.Drawing.PointF(screenPoint.X, screenPoint.Y) };
|
||||||
|
transform.TransformPoints(points);
|
||||||
|
|
||||||
|
return new Point((int)points[0].X, (int)points[0].Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Point WorldToScreen(Point worldPoint)
|
private Point WorldToScreen(Point worldPoint)
|
||||||
@@ -268,7 +260,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
// 화면에서 최소 20픽셀 정도의 히트 영역을 확보하되, 노드 크기보다 작아지지 않게 함
|
// 화면에서 최소 20픽셀 정도의 히트 영역을 확보하되, 노드 크기보다 작아지지 않게 함
|
||||||
var minHitRadiusInScreen = 20;
|
var minHitRadiusInScreen = 20;
|
||||||
var hitRadius = Math.Max(NODE_RADIUS, minHitRadiusInScreen / _zoomFactor);
|
var hitRadius = Math.Max(NODE_RADIUS, minHitRadiusInScreen / _zoomFactor);
|
||||||
|
|
||||||
var distance = Math.Sqrt(
|
var distance = Math.Sqrt(
|
||||||
Math.Pow(node.Position.X - point.X, 2) +
|
Math.Pow(node.Position.X - point.X, 2) +
|
||||||
Math.Pow(node.Position.Y - point.Y, 2)
|
Math.Pow(node.Position.Y - point.Y, 2)
|
||||||
@@ -282,7 +274,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
var minHitRadiusInScreen = 20;
|
var minHitRadiusInScreen = 20;
|
||||||
var radius = Math.Max(NODE_RADIUS, minHitRadiusInScreen / _zoomFactor);
|
var radius = Math.Max(NODE_RADIUS, minHitRadiusInScreen / _zoomFactor);
|
||||||
var center = node.Position;
|
var center = node.Position;
|
||||||
|
|
||||||
// 5각형 꼭짓점 계산
|
// 5각형 꼭짓점 계산
|
||||||
var points = new Point[5];
|
var points = new Point[5];
|
||||||
for (int i = 0; i < 5; i++)
|
for (int i = 0; i < 5; i++)
|
||||||
@@ -303,7 +295,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
var minHitRadiusInScreen = 20;
|
var minHitRadiusInScreen = 20;
|
||||||
var radius = Math.Max(NODE_RADIUS, minHitRadiusInScreen / _zoomFactor);
|
var radius = Math.Max(NODE_RADIUS, minHitRadiusInScreen / _zoomFactor);
|
||||||
var center = node.Position;
|
var center = node.Position;
|
||||||
|
|
||||||
// 삼각형 꼭짓점 계산
|
// 삼각형 꼭짓점 계산
|
||||||
var points = new Point[3];
|
var points = new Point[3];
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
@@ -345,21 +337,21 @@ namespace AGVNavigationCore.Controls
|
|||||||
private bool IsPointInLabelNode(Point point, MapNode node)
|
private bool IsPointInLabelNode(Point point, MapNode node)
|
||||||
{
|
{
|
||||||
var text = string.IsNullOrEmpty(node.LabelText) ? node.NodeId : node.LabelText;
|
var text = string.IsNullOrEmpty(node.LabelText) ? node.NodeId : node.LabelText;
|
||||||
|
|
||||||
// 임시 Graphics로 텍스트 크기 측정
|
// 임시 Graphics로 텍스트 크기 측정
|
||||||
using (var tempBitmap = new Bitmap(1, 1))
|
using (var tempBitmap = new Bitmap(1, 1))
|
||||||
using (var tempGraphics = Graphics.FromImage(tempBitmap))
|
using (var tempGraphics = Graphics.FromImage(tempBitmap))
|
||||||
{
|
{
|
||||||
var font = new Font(node.FontFamily, node.FontSize, node.FontStyle);
|
var font = new Font(node.FontFamily, node.FontSize, node.FontStyle);
|
||||||
var textSize = tempGraphics.MeasureString(text, font);
|
var textSize = tempGraphics.MeasureString(text, font);
|
||||||
|
|
||||||
var textRect = new Rectangle(
|
var textRect = new Rectangle(
|
||||||
(int)(node.Position.X - textSize.Width / 2),
|
(int)(node.Position.X - textSize.Width / 2),
|
||||||
(int)(node.Position.Y - textSize.Height / 2),
|
(int)(node.Position.Y - textSize.Height / 2),
|
||||||
(int)textSize.Width,
|
(int)textSize.Width,
|
||||||
(int)textSize.Height
|
(int)textSize.Height
|
||||||
);
|
);
|
||||||
|
|
||||||
font.Dispose();
|
font.Dispose();
|
||||||
return textRect.Contains(point);
|
return textRect.Contains(point);
|
||||||
}
|
}
|
||||||
@@ -627,7 +619,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
private (MapNode From, MapNode To)? GetConnectionAt(Point worldPoint)
|
private (MapNode From, MapNode To)? GetConnectionAt(Point worldPoint)
|
||||||
{
|
{
|
||||||
const int CONNECTION_HIT_TOLERANCE = 10;
|
const int CONNECTION_HIT_TOLERANCE = 10;
|
||||||
|
|
||||||
// 모든 연결선을 확인하여 클릭한 위치와 가장 가까운 연결선 찾기
|
// 모든 연결선을 확인하여 클릭한 위치와 가장 가까운 연결선 찾기
|
||||||
foreach (var fromNode in _nodes)
|
foreach (var fromNode in _nodes)
|
||||||
{
|
{
|
||||||
@@ -645,7 +637,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -659,11 +651,11 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
var dot = A * C + B * D;
|
var dot = A * C + B * D;
|
||||||
var lenSq = C * C + D * D;
|
var lenSq = C * C + D * D;
|
||||||
|
|
||||||
if (lenSq == 0) return CalculateDistance(point, lineStart);
|
if (lenSq == 0) return CalculateDistance(point, lineStart);
|
||||||
|
|
||||||
var param = dot / lenSq;
|
var param = dot / lenSq;
|
||||||
|
|
||||||
Point xx, yy;
|
Point xx, yy;
|
||||||
if (param < 0)
|
if (param < 0)
|
||||||
{
|
{
|
||||||
@@ -680,7 +672,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
xx = new Point((int)(lineStart.X + param * C), (int)(lineStart.Y + param * D));
|
xx = new Point((int)(lineStart.X + param * C), (int)(lineStart.Y + param * D));
|
||||||
yy = xx;
|
yy = xx;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CalculateDistance(point, xx);
|
return CalculateDistance(point, xx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user