fix: 그리드 화면 전체 표시 및 스케일 동적 계산
주요 변경사항: - 그리드가 화면 전체를 덮도록 수정 - ScreenToWorld를 사용하여 정확한 월드 좌표 계산 - 스케일 표시를 동적으로 계산 (줌에 따라 변경) - 스케일 정보를 별도 박스로 표시 (좌하단) - 줌 정보와 스케일 정보 분리 표시 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -68,30 +68,36 @@ namespace AGVNavigationCore.Controls
|
|||||||
{
|
{
|
||||||
if (!_showGrid) return;
|
if (!_showGrid) return;
|
||||||
|
|
||||||
var bounds = GetVisibleBounds();
|
|
||||||
var gridSize = (int)(GRID_SIZE * _zoomFactor);
|
var gridSize = (int)(GRID_SIZE * _zoomFactor);
|
||||||
|
|
||||||
if (gridSize < 5) return; // 너무 작으면 그리지 않음
|
if (gridSize < 5) return; // 너무 작으면 그리지 않음
|
||||||
|
|
||||||
// 그리드 시작 위치 (월드 좌표에서 GRID_SIZE의 배수 찾기)
|
// 화면 전체를 덮는 월드 좌표 범위 계산
|
||||||
int startX = (bounds.Left / GRID_SIZE) * GRID_SIZE;
|
var topLeft = ScreenToWorld(new Point(0, 0));
|
||||||
int startY = (bounds.Top / GRID_SIZE) * GRID_SIZE;
|
var bottomRight = ScreenToWorld(new Point(Width, Height));
|
||||||
|
|
||||||
// 월드 좌표로 그리드 라인 계산 (Transform이 자동으로 적용됨)
|
// 그리드 시작 위치 (월드 좌표에서 GRID_SIZE의 배수로 정렬)
|
||||||
for (int x = startX; x <= bounds.Right; x += GRID_SIZE)
|
int startX = (topLeft.X / GRID_SIZE) * GRID_SIZE - GRID_SIZE;
|
||||||
{
|
int startY = (topLeft.Y / GRID_SIZE) * GRID_SIZE - GRID_SIZE;
|
||||||
if (x % (GRID_SIZE * 5) == 0)
|
int endX = bottomRight.X + GRID_SIZE;
|
||||||
g.DrawLine(new Pen(Color.Gray, 1), x, bounds.Top, x, bounds.Bottom);
|
int endY = bottomRight.Y + GRID_SIZE;
|
||||||
else
|
|
||||||
g.DrawLine(_gridPen, x, bounds.Top, x, bounds.Bottom);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int y = startY; y <= bounds.Bottom; y += GRID_SIZE)
|
// 그리드 펜 (가는 선과 굵은 선)
|
||||||
|
using (var thinPen = new Pen(Color.FromArgb(200, 200, 200), 1))
|
||||||
|
using (var thickPen = new Pen(Color.FromArgb(150, 150, 150), 1))
|
||||||
{
|
{
|
||||||
if (y % (GRID_SIZE * 5) == 0)
|
// 수직선 그리기
|
||||||
g.DrawLine(new Pen(Color.Gray, 1), bounds.Left, y, bounds.Right, y);
|
for (int x = startX; x <= endX; x += GRID_SIZE)
|
||||||
else
|
{
|
||||||
g.DrawLine(_gridPen, bounds.Left, y, bounds.Right, y);
|
var pen = (x % (GRID_SIZE * 5) == 0) ? thickPen : thinPen;
|
||||||
|
g.DrawLine(pen, x, startY, x, endY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 수평선 그리기
|
||||||
|
for (int y = startY; y <= endY; y += GRID_SIZE)
|
||||||
|
{
|
||||||
|
var pen = (y % (GRID_SIZE * 5) == 0) ? thickPen : thinPen;
|
||||||
|
g.DrawLine(pen, startX, y, endX, y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1563,41 +1569,51 @@ namespace AGVNavigationCore.Controls
|
|||||||
g.DrawImage(_companyLogo, logoRect);
|
g.DrawImage(_companyLogo, logoRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 측정 정보
|
// 줌 및 스케일 정보 (동적 계산)
|
||||||
if (!string.IsNullOrEmpty(_measurementInfo))
|
// 스케일: 1픽셀 = GRID_SIZE / _zoomFactor mm
|
||||||
|
// 예: GRID_SIZE=10, zoom=1.0 → 1:10, zoom=0.1 → 1:100
|
||||||
|
double scaleRatio = GRID_SIZE / _zoomFactor;
|
||||||
|
var zoomText = $"Zoom: {_zoomFactor:P0}";
|
||||||
|
var scaleText = $"스케일: 1:{scaleRatio:F0}";
|
||||||
|
|
||||||
|
using (var font = new Font("맑은 고딕", 10, FontStyle.Bold))
|
||||||
|
using (var bgBrush = new SolidBrush(Color.FromArgb(220, Color.White)))
|
||||||
{
|
{
|
||||||
var font = new Font("Arial", 9);
|
// 줌 정보 (좌하단)
|
||||||
var textBrush = new SolidBrush(Color.Black);
|
var zoomSize = g.MeasureString(zoomText, font);
|
||||||
var backgroundBrush = new SolidBrush(Color.FromArgb(200, Color.White));
|
var zoomRect = new RectangleF(10, Height - zoomSize.Height - 15, zoomSize.Width + 10, zoomSize.Height + 5);
|
||||||
|
g.FillRectangle(bgBrush, zoomRect);
|
||||||
|
g.DrawRectangle(Pens.Gray, zoomRect.X, zoomRect.Y, zoomRect.Width, zoomRect.Height);
|
||||||
|
g.DrawString(zoomText, font, Brushes.Black, zoomRect.X + 5, zoomRect.Y + 2);
|
||||||
|
|
||||||
var textSize = g.MeasureString(_measurementInfo, font);
|
// 스케일 정보 (줌 정보 위에)
|
||||||
var textRect = new Rectangle(
|
var scaleSize = g.MeasureString(scaleText, font);
|
||||||
Width - (int)textSize.Width - 20,
|
var scaleRect = new RectangleF(10, Height - zoomSize.Height - scaleSize.Height - 25, scaleSize.Width + 10, scaleSize.Height + 5);
|
||||||
Height - (int)textSize.Height - 20,
|
g.FillRectangle(bgBrush, scaleRect);
|
||||||
(int)textSize.Width + 10,
|
g.DrawRectangle(Pens.Gray, scaleRect.X, scaleRect.Y, scaleRect.Width, scaleRect.Height);
|
||||||
(int)textSize.Height + 10
|
g.DrawString(scaleText, font, Brushes.Black, scaleRect.X + 5, scaleRect.Y + 2);
|
||||||
);
|
|
||||||
|
|
||||||
g.FillRectangle(backgroundBrush, textRect);
|
|
||||||
g.DrawRectangle(Pens.Gray, textRect);
|
|
||||||
g.DrawString(_measurementInfo, font, textBrush, textRect.X + 5, textRect.Y + 5);
|
|
||||||
|
|
||||||
font.Dispose();
|
|
||||||
textBrush.Dispose();
|
|
||||||
backgroundBrush.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 줌 정보
|
// 측정 정보 (우하단 - 사용자 정의 정보가 있을 경우)
|
||||||
var zoomText = $"Zoom: {_zoomFactor:P0}";
|
if (!string.IsNullOrEmpty(_measurementInfo))
|
||||||
var zoomFont = new Font("Arial", 10, FontStyle.Bold);
|
{
|
||||||
var zoomSize = g.MeasureString(zoomText, zoomFont);
|
using (var font = new Font("맑은 고딕", 9))
|
||||||
var zoomPoint = new Point(10, Height - (int)zoomSize.Height - 10);
|
using (var textBrush = new SolidBrush(Color.Black))
|
||||||
|
using (var backgroundBrush = new SolidBrush(Color.FromArgb(200, Color.White)))
|
||||||
|
{
|
||||||
|
var textSize = g.MeasureString(_measurementInfo, font);
|
||||||
|
var textRect = new Rectangle(
|
||||||
|
Width - (int)textSize.Width - 20,
|
||||||
|
Height - (int)textSize.Height - 20,
|
||||||
|
(int)textSize.Width + 10,
|
||||||
|
(int)textSize.Height + 10
|
||||||
|
);
|
||||||
|
|
||||||
g.FillRectangle(new SolidBrush(Color.FromArgb(200, Color.White)),
|
g.FillRectangle(backgroundBrush, textRect);
|
||||||
zoomPoint.X - 5, zoomPoint.Y - 5,
|
g.DrawRectangle(Pens.Gray, textRect);
|
||||||
zoomSize.Width + 10, zoomSize.Height + 10);
|
g.DrawString(_measurementInfo, font, textBrush, textRect.X + 5, textRect.Y + 5);
|
||||||
g.DrawString(zoomText, zoomFont, Brushes.Black, zoomPoint);
|
}
|
||||||
zoomFont.Dispose();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
// UI 요소들
|
// UI 요소들
|
||||||
private Image _companyLogo;
|
private Image _companyLogo;
|
||||||
private string _companyLogoPath = string.Empty;
|
private string _companyLogoPath = string.Empty;
|
||||||
private string _measurementInfo = "스케일: 1:100\n면적: 1000㎡\n최종 수정: " + DateTime.Now.ToString("yyyy-MM-dd");
|
private string _measurementInfo = string.Empty;
|
||||||
|
|
||||||
// 편집 관련 (EditMode에서만 사용)
|
// 편집 관련 (EditMode에서만 사용)
|
||||||
private bool _isDragging;
|
private bool _isDragging;
|
||||||
|
|||||||
@@ -292,6 +292,23 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
$"계산시간: {CalculationTimeMs}ms";
|
$"계산시간: {CalculationTimeMs}ms";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 경로의 노드 정보를 포함
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string GetDetailedPathInfo()
|
||||||
|
{
|
||||||
|
if (!Success)
|
||||||
|
{
|
||||||
|
return $"경로 계산 실패: {ErrorMessage} (계산시간: {CalculationTimeMs}ms)";
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = DetailedPath.Select(t => {
|
||||||
|
return $"{t.RfidId}[{t.NodeId}] {t.MotorDirection.ToString().Substring(0,1)}-{t.MagnetDirection.ToString().Substring(0,1)}";
|
||||||
|
});
|
||||||
|
return string.Join(" → ",data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 단순 경로 목록 반환 (호환성용 - 노드 ID 문자열 목록)
|
/// 단순 경로 목록 반환 (호환성용 - 노드 ID 문자열 목록)
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Forms\PathTestLogItem.cs" />
|
||||||
<Compile Include="Forms\ProgressLogForm.cs">
|
<Compile Include="Forms\ProgressLogForm.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|||||||
25
Cs_HMI/AGVLogic/AGVSimulator/Forms/PathTestLogItem.cs
Normal file
25
Cs_HMI/AGVLogic/AGVSimulator/Forms/PathTestLogItem.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AGVSimulator.Forms
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 경로 예측 테스트 결과 로그 항목
|
||||||
|
/// </summary>
|
||||||
|
public class PathTestLogItem
|
||||||
|
{
|
||||||
|
public string PreviousPosition { get; set; }
|
||||||
|
public string MotorDirection { get; set; } // 정방향 or 역방향
|
||||||
|
public string CurrentPosition { get; set; }
|
||||||
|
public string TargetPosition { get; set; }
|
||||||
|
public string DockingPosition { get; set; } // 도킹위치
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
public string DetailedPath { get; set; }
|
||||||
|
public DateTime Timestamp { get; set; }
|
||||||
|
|
||||||
|
public PathTestLogItem()
|
||||||
|
{
|
||||||
|
Timestamp = DateTime.Now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,26 +7,6 @@ using System.Windows.Forms;
|
|||||||
|
|
||||||
namespace AGVSimulator.Forms
|
namespace AGVSimulator.Forms
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 경로 예측 테스트 결과 로그 항목
|
|
||||||
/// </summary>
|
|
||||||
public class PathTestLogItem
|
|
||||||
{
|
|
||||||
public string PreviousPosition { get; set; }
|
|
||||||
public string MotorDirection { get; set; } // 정방향 or 역방향
|
|
||||||
public string CurrentPosition { get; set; }
|
|
||||||
public string TargetPosition { get; set; }
|
|
||||||
public string DockingPosition { get; set; } // 도킹위치
|
|
||||||
public bool Success { get; set; }
|
|
||||||
public string Message { get; set; }
|
|
||||||
public string DetailedPath { get; set; }
|
|
||||||
public DateTime Timestamp { get; set; }
|
|
||||||
|
|
||||||
public PathTestLogItem()
|
|
||||||
{
|
|
||||||
Timestamp = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 경로 예측 테스트 진행 상황 로그 표시 폼
|
/// 경로 예측 테스트 진행 상황 로그 표시 폼
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ namespace AGVSimulator.Forms
|
|||||||
// 도킹 검증이 없는 경우 추가 검증 수행
|
// 도킹 검증이 없는 경우 추가 검증 수행
|
||||||
if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired)
|
if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired)
|
||||||
{
|
{
|
||||||
if(advancedResult.Path.Count < 1)
|
if (advancedResult.Path.Count < 1)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1228,7 +1228,7 @@ namespace AGVSimulator.Forms
|
|||||||
_statusLabel.Text = "초기화 완료";
|
_statusLabel.Text = "초기화 완료";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async void toolStripButton1_Click(object sender, EventArgs e)
|
private async void toolStripButton1_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
// 맵과 AGV 확인
|
// 맵과 AGV 확인
|
||||||
@@ -1382,13 +1382,15 @@ namespace AGVSimulator.Forms
|
|||||||
{
|
{
|
||||||
logItem.Success = true;
|
logItem.Success = true;
|
||||||
logItem.Message = "성공";
|
logItem.Message = "성공";
|
||||||
logItem.DetailedPath = string.Join(" → ", currentPath.GetDetailedInfo());
|
//logItem.DetailedPath = string.Join(" → ", currentPath.GetDetailedInfo());
|
||||||
|
logItem.DetailedPath = currentPath.GetDetailedPathInfo();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logItem.Success = false;
|
logItem.Success = false;
|
||||||
logItem.Message = $"도킹 검증 실패: {dockingValidation.ValidationError}";
|
logItem.Message = $"도킹 검증 실패: {dockingValidation.ValidationError}";
|
||||||
logItem.DetailedPath = string.Join(" → ", currentPath.GetDetailedInfo());
|
//logItem.DetailedPath = string.Join(" → ", currentPath.GetDetailedInfo());
|
||||||
|
logItem.DetailedPath = currentPath.GetDetailedPathInfo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user