fix
This commit is contained in:
@@ -26,6 +26,10 @@ namespace AGVNavigationCore.Controls
|
||||
}
|
||||
|
||||
var g = e.Graphics;
|
||||
|
||||
// 🔥 배경색 그리기 (변환 행렬 적용 전에 전체 화면을 배경색으로 채움)
|
||||
g.Clear(this.BackColor);
|
||||
|
||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
g.InterpolationMode = InterpolationMode.High;
|
||||
|
||||
@@ -77,7 +81,8 @@ namespace AGVNavigationCore.Controls
|
||||
}
|
||||
|
||||
// UI 정보 그리기 (변환 없이)
|
||||
DrawUIInfo(g);
|
||||
if (_showGrid)
|
||||
DrawUIInfo(g);
|
||||
}
|
||||
|
||||
private void DrawGrid(Graphics g)
|
||||
@@ -689,8 +694,8 @@ namespace AGVNavigationCore.Controls
|
||||
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);
|
||||
}
|
||||
// 선택된 노드 강조
|
||||
else if (node == _selectedNode)
|
||||
// 선택된 노드 강조 (단일 또는 다중)
|
||||
else if (node == _selectedNode || (_selectedNodes != null && _selectedNodes.Contains(node)))
|
||||
{
|
||||
g.DrawEllipse(_selectedNodePen, rect);
|
||||
}
|
||||
@@ -938,19 +943,22 @@ namespace AGVNavigationCore.Controls
|
||||
// 아래쪽에 표시할 값 (RFID 우선, 없으면 노드ID)
|
||||
if (node.HasRfid())
|
||||
{
|
||||
// RFID가 있는 경우: 순수 RFID 값만 표시 (진한 색상)
|
||||
// RFID가 있는 경우: 순수 RFID 값만 표시 (노드 전경색 사용)
|
||||
displayText = node.RfidId;
|
||||
textColor = Color.Black;
|
||||
textColor = node.ForeColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
// RFID가 없는 경우: 노드 ID 표시 (연한 색상)
|
||||
// RFID가 없는 경우: 노드 ID 표시 (노드 전경색의 50% 투명도)
|
||||
displayText = node.NodeId;
|
||||
textColor = Color.Gray;
|
||||
textColor = Color.FromArgb(128, node.ForeColor);
|
||||
}
|
||||
|
||||
var font = new Font("Arial", 7, FontStyle.Bold);
|
||||
var descFont = new Font("Arial", 8, FontStyle.Bold);
|
||||
// 🔥 노드의 폰트 설정 사용 (0 이하일 경우 기본값 7.0f 사용)
|
||||
var fontStyle = node.TextFontBold ? FontStyle.Bold : FontStyle.Regular;
|
||||
var fontSize = node.TextFontSize > 0 ? node.TextFontSize : 7.0f;
|
||||
var font = new Font("Arial", fontSize, fontStyle);
|
||||
var descFont = new Font("Arial", fontSize + 1, fontStyle);
|
||||
|
||||
// 메인 텍스트 크기 측정
|
||||
var textSize = g.MeasureString(displayText, font);
|
||||
@@ -971,12 +979,8 @@ namespace AGVNavigationCore.Controls
|
||||
// 설명 텍스트 그리기 (설명이 있는 경우에만)
|
||||
if (!string.IsNullOrEmpty(descriptionText))
|
||||
{
|
||||
// 노드 이름 입력 여부에 따라 색상 구분
|
||||
// 입력된 경우: 진한 색상 (잘 보이게)
|
||||
// 기본값인 경우: 흐린 색상 (현재처럼)
|
||||
Color descColor = string.IsNullOrEmpty(node.Name)
|
||||
? Color.FromArgb(120, Color.Black) // 입력 안됨: 흐린 색상
|
||||
: Color.FromArgb(200, Color.Black); // 입력됨: 진한 색상
|
||||
// 🔥 노드의 말풍선 글자색 사용 (NameBubbleForeColor)
|
||||
Color descColor = node.NameBubbleForeColor;
|
||||
|
||||
var rectpaddingx = 4;
|
||||
var rectpaddingy = 2;
|
||||
@@ -985,10 +989,10 @@ namespace AGVNavigationCore.Controls
|
||||
(int)descSize.Width + rectpaddingx * 2,
|
||||
(int)descSize.Height + rectpaddingy * 2);
|
||||
|
||||
// 라운드 사각형 그리기 (빨간 배경)
|
||||
using (var backgroundBrush = new SolidBrush(Color.Gold))
|
||||
// 라운드 사각형 그리기 (노드 이름 말풍선 배경색 사용)
|
||||
using (var backgroundBrush = new SolidBrush(node.NameBubbleBackColor))
|
||||
{
|
||||
DrawRoundedRectangle(g, backgroundBrush, roundRect, 3); // 모서리 반지름 6px
|
||||
DrawRoundedRectangle(g, backgroundBrush, roundRect, 3); // 모서리 반지름 3px
|
||||
}
|
||||
|
||||
// 라운드 사각형 테두리 그리기 (진한 빨간색)
|
||||
@@ -1287,26 +1291,19 @@ namespace AGVNavigationCore.Controls
|
||||
|
||||
private Brush GetNodeBrush(MapNode node)
|
||||
{
|
||||
// RFID가 없는 노드는 회색 계통으로 표시
|
||||
// 🔥 노드의 DisplayColor를 배경색으로 사용
|
||||
// RFID가 없는 노드는 DisplayColor를 50% 투명도로 표시
|
||||
bool hasRfid = node.HasRfid();
|
||||
|
||||
switch (node.Type)
|
||||
Color bgColor = node.DisplayColor;
|
||||
|
||||
// RFID가 없는 경우 투명도 50%
|
||||
if (!hasRfid)
|
||||
{
|
||||
case NodeType.Normal:
|
||||
return hasRfid ? _normalNodeBrush : new SolidBrush(Color.LightGray);
|
||||
case NodeType.Rotation:
|
||||
return hasRfid ? _rotationNodeBrush : new SolidBrush(Color.DarkGray);
|
||||
case NodeType.Docking:
|
||||
return hasRfid ? _dockingNodeBrush : new SolidBrush(Color.Gray);
|
||||
case NodeType.Charging:
|
||||
return hasRfid ? _chargingNodeBrush : new SolidBrush(Color.Silver);
|
||||
case NodeType.Label:
|
||||
return new SolidBrush(Color.Purple);
|
||||
case NodeType.Image:
|
||||
return new SolidBrush(Color.Brown);
|
||||
default:
|
||||
return hasRfid ? _normalNodeBrush : new SolidBrush(Color.LightGray);
|
||||
bgColor = Color.FromArgb(128, bgColor);
|
||||
}
|
||||
|
||||
return new SolidBrush(bgColor);
|
||||
}
|
||||
|
||||
private void DrawAGVs(Graphics g)
|
||||
|
||||
@@ -17,6 +17,54 @@ namespace AGVNavigationCore.Controls
|
||||
var worldPoint = ScreenToWorld(e.Location);
|
||||
var hitNode = GetNodeAt(worldPoint);
|
||||
|
||||
// 🔥 어떤 모드에서든 노드/빈 공간 클릭 시 선택 이벤트 발생 (속성창 업데이트)
|
||||
bool ctrlPressed = (ModifierKeys & Keys.Control) == Keys.Control;
|
||||
|
||||
if (hitNode != null)
|
||||
{
|
||||
// 노드 클릭
|
||||
if (ctrlPressed && _editMode == EditMode.Select)
|
||||
{
|
||||
// Ctrl+클릭: 다중 선택 토글
|
||||
if (_selectedNodes.Contains(hitNode))
|
||||
{
|
||||
_selectedNodes.Remove(hitNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
_selectedNodes.Add(hitNode);
|
||||
}
|
||||
|
||||
// 마지막 선택된 노드 업데이트 (단일 참조용)
|
||||
_selectedNode = _selectedNodes.Count > 0 ? _selectedNodes[_selectedNodes.Count - 1] : null;
|
||||
|
||||
// 다중 선택 이벤트만 발생 (OnNodesSelected에서 단일/다중 구분 처리)
|
||||
NodesSelected?.Invoke(this, _selectedNodes);
|
||||
Invalidate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 일반 클릭: 단일 선택
|
||||
_selectedNode = hitNode;
|
||||
_selectedNodes.Clear();
|
||||
_selectedNodes.Add(hitNode);
|
||||
|
||||
// NodesSelected 이벤트만 발생 (OnNodesSelected에서 단일/다중 구분 처리)
|
||||
NodesSelected?.Invoke(this, _selectedNodes);
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
else if (_editMode == EditMode.Select)
|
||||
{
|
||||
// 빈 공간 클릭 (Select 모드에서만) - 선택 해제
|
||||
_selectedNode = null;
|
||||
_selectedNodes.Clear();
|
||||
|
||||
// NodesSelected 이벤트만 발생 (OnNodesSelected에서 빈 리스트 처리)
|
||||
NodesSelected?.Invoke(this, _selectedNodes);
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
switch (_editMode)
|
||||
{
|
||||
case EditMode.Select:
|
||||
@@ -76,7 +124,10 @@ namespace AGVNavigationCore.Controls
|
||||
|
||||
default:
|
||||
// 기본 동작: 노드 선택 이벤트 발생
|
||||
NodeSelected?.Invoke(this, hitNode);
|
||||
_selectedNode = hitNode;
|
||||
_selectedNodes.Clear();
|
||||
_selectedNodes.Add(hitNode);
|
||||
NodesSelected?.Invoke(this, _selectedNodes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -98,8 +149,11 @@ namespace AGVNavigationCore.Controls
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
// 노드 선택 이벤트도 발생 (속성창 업데이트)
|
||||
NodeSelected?.Invoke(this, node);
|
||||
// 더블클릭 시 해당 노드만 선택 (다중 선택 해제)
|
||||
_selectedNode = node;
|
||||
_selectedNodes.Clear();
|
||||
_selectedNodes.Add(node);
|
||||
NodesSelected?.Invoke(this, _selectedNodes);
|
||||
}
|
||||
|
||||
private void HandleLabelNodeDoubleClick(MapNode node)
|
||||
@@ -118,14 +172,20 @@ namespace AGVNavigationCore.Controls
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
// 노드 선택 이벤트도 발생 (속성창 업데이트)
|
||||
NodeSelected?.Invoke(this, node);
|
||||
// 더블클릭 시 해당 노드만 선택 (다중 선택 해제)
|
||||
_selectedNode = node;
|
||||
_selectedNodes.Clear();
|
||||
_selectedNodes.Add(node);
|
||||
NodesSelected?.Invoke(this, _selectedNodes);
|
||||
}
|
||||
|
||||
private void HandleImageNodeDoubleClick(MapNode node)
|
||||
{
|
||||
// 이미지 노드는 선택 이벤트만 발생 (MainForm에서 이미지 편집 버튼 활성화됨)
|
||||
NodeSelected?.Invoke(this, node);
|
||||
// 더블클릭 시 해당 노드만 선택 (다중 선택 해제)
|
||||
_selectedNode = node;
|
||||
_selectedNodes.Clear();
|
||||
_selectedNodes.Add(node);
|
||||
NodesSelected?.Invoke(this, _selectedNodes);
|
||||
|
||||
// 이미지 편집 이벤트 발생 (MainForm에서 처리)
|
||||
ImageNodeDoubleClicked?.Invoke(this, node);
|
||||
@@ -519,13 +579,8 @@ namespace AGVNavigationCore.Controls
|
||||
{
|
||||
if (hitNode != null)
|
||||
{
|
||||
// 노드 선택
|
||||
if (hitNode != _selectedNode)
|
||||
{
|
||||
_selectedNode = hitNode;
|
||||
NodeSelected?.Invoke(this, hitNode);
|
||||
Invalidate();
|
||||
}
|
||||
// 노드 선택은 위쪽 MouseClick에서 이미 처리됨 (NodesSelected 이벤트 발생)
|
||||
// 여기서는 추가 처리 없음
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -565,10 +620,11 @@ namespace AGVNavigationCore.Controls
|
||||
else
|
||||
{
|
||||
// 빈 공간 클릭 시 선택 해제
|
||||
if (_selectedNode != null)
|
||||
if (_selectedNode != null || _selectedNodes.Count > 0)
|
||||
{
|
||||
_selectedNode = null;
|
||||
NodeSelected?.Invoke(this, null);
|
||||
_selectedNodes.Clear();
|
||||
NodesSelected?.Invoke(this, _selectedNodes);
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
@@ -745,7 +801,13 @@ namespace AGVNavigationCore.Controls
|
||||
|
||||
if (hitNode != null)
|
||||
{
|
||||
_contextMenu.Items.Add("노드 속성...", null, (s, e) => NodeSelected?.Invoke(this, hitNode));
|
||||
_contextMenu.Items.Add("노드 속성...", null, (s, e) =>
|
||||
{
|
||||
_selectedNode = hitNode;
|
||||
_selectedNodes.Clear();
|
||||
_selectedNodes.Add(hitNode);
|
||||
NodesSelected?.Invoke(this, _selectedNodes);
|
||||
});
|
||||
_contextMenu.Items.Add("노드 삭제", null, (s, e) => HandleDeleteClick(hitNode));
|
||||
_contextMenu.Items.Add("-");
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace AGVNavigationCore.Controls
|
||||
// 맵 데이터
|
||||
private List<MapNode> _nodes;
|
||||
private MapNode _selectedNode;
|
||||
private List<MapNode> _selectedNodes; // 다중 선택
|
||||
private MapNode _hoveredNode;
|
||||
private MapNode _destinationNode;
|
||||
|
||||
@@ -95,6 +96,11 @@ namespace AGVNavigationCore.Controls
|
||||
private Point _connectionEndPoint;
|
||||
private int _mouseMoveCounter = 0; // 디버그용: MouseMove 실행 횟수
|
||||
|
||||
// 영역 선택 관련
|
||||
private bool _isAreaSelecting;
|
||||
private Point _areaSelectStart;
|
||||
private Point _areaSelectEnd;
|
||||
|
||||
// 그리드 및 줌 관련
|
||||
private bool _showGrid = true;
|
||||
private float _zoomFactor = 1.0f;
|
||||
@@ -141,6 +147,7 @@ namespace AGVNavigationCore.Controls
|
||||
// 맵 편집 이벤트
|
||||
public event EventHandler<MapNode> NodeAdded;
|
||||
public event EventHandler<MapNode> NodeSelected;
|
||||
public event EventHandler<List<MapNode>> NodesSelected; // 다중 선택 이벤트
|
||||
public event EventHandler<MapNode> NodeDeleted;
|
||||
public event EventHandler<MapNode> NodeMoved;
|
||||
public event EventHandler<(MapNode From, MapNode To)> ConnectionDeleted;
|
||||
@@ -212,10 +219,15 @@ namespace AGVNavigationCore.Controls
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 선택된 노드
|
||||
/// 선택된 노드 (단일)
|
||||
/// </summary>
|
||||
public MapNode SelectedNode => _selectedNode;
|
||||
|
||||
/// <summary>
|
||||
/// 선택된 노드들 (다중)
|
||||
/// </summary>
|
||||
public List<MapNode> SelectedNodes => _selectedNodes ?? new List<MapNode>();
|
||||
|
||||
/// <summary>
|
||||
/// 노드 목록
|
||||
/// </summary>
|
||||
@@ -365,6 +377,7 @@ namespace AGVNavigationCore.Controls
|
||||
ControlStyles.ResizeRedraw, true);
|
||||
|
||||
_nodes = new List<MapNode>();
|
||||
_selectedNodes = new List<MapNode>(); // 다중 선택 리스트 초기화
|
||||
_agvList = new List<IAGV>();
|
||||
_agvPositions = new Dictionary<string, Point>();
|
||||
_agvDirections = new Dictionary<string, AgvDirection>();
|
||||
|
||||
@@ -12,6 +12,15 @@ namespace AGVNavigationCore.Models
|
||||
/// </summary>
|
||||
public static class MapLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// 맵 설정 정보 (배경색, 그리드 표시 등)
|
||||
/// </summary>
|
||||
public class MapSettings
|
||||
{
|
||||
public int BackgroundColorArgb { get; set; } = System.Drawing.Color.White.ToArgb();
|
||||
public bool ShowGrid { get; set; } = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 맵 파일 로딩 결과
|
||||
/// </summary>
|
||||
@@ -19,6 +28,7 @@ namespace AGVNavigationCore.Models
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public List<MapNode> Nodes { get; set; } = new List<MapNode>();
|
||||
public MapSettings Settings { get; set; } = new MapSettings();
|
||||
public string ErrorMessage { get; set; } = string.Empty;
|
||||
public string Version { get; set; } = string.Empty;
|
||||
public DateTime CreatedDate { get; set; }
|
||||
@@ -30,8 +40,9 @@ namespace AGVNavigationCore.Models
|
||||
public class MapFileData
|
||||
{
|
||||
public List<MapNode> Nodes { get; set; } = new List<MapNode>();
|
||||
public MapSettings Settings { get; set; } = new MapSettings();
|
||||
public DateTime CreatedDate { get; set; }
|
||||
public string Version { get; set; } = "1.0";
|
||||
public string Version { get; set; } = "1.1"; // 버전 업그레이드 (설정 추가)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -66,6 +77,7 @@ namespace AGVNavigationCore.Models
|
||||
if (mapData != null)
|
||||
{
|
||||
result.Nodes = mapData.Nodes ?? new List<MapNode>();
|
||||
result.Settings = mapData.Settings ?? new MapSettings(); // 설정 로드
|
||||
result.Version = mapData.Version ?? "1.0";
|
||||
result.CreatedDate = mapData.CreatedDate;
|
||||
|
||||
@@ -111,8 +123,9 @@ namespace AGVNavigationCore.Models
|
||||
/// </summary>
|
||||
/// <param name="filePath">저장할 파일 경로</param>
|
||||
/// <param name="nodes">맵 노드 목록</param>
|
||||
/// <param name="settings">맵 설정 (배경색, 그리드 표시 등)</param>
|
||||
/// <returns>저장 성공 여부</returns>
|
||||
public static bool SaveMapToFile(string filePath, List<MapNode> nodes)
|
||||
public static bool SaveMapToFile(string filePath, List<MapNode> nodes, MapSettings settings = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -122,8 +135,9 @@ namespace AGVNavigationCore.Models
|
||||
var mapData = new MapFileData
|
||||
{
|
||||
Nodes = nodes,
|
||||
Settings = settings ?? new MapSettings(), // 설정 저장
|
||||
CreatedDate = DateTime.Now,
|
||||
Version = "1.0"
|
||||
Version = "1.1"
|
||||
};
|
||||
|
||||
var json = JsonConvert.SerializeObject(mapData, Formatting.Indented);
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace AGVNavigationCore.Models
|
||||
public FontStyle FontStyle { get; set; } = FontStyle.Regular;
|
||||
|
||||
/// <summary>
|
||||
/// 라벨 전경색 (NodeType.Label인 경우 사용)
|
||||
/// 텍스트 전경색 (모든 노드 타입에서 사용)
|
||||
/// </summary>
|
||||
public Color ForeColor { get; set; } = Color.Black;
|
||||
|
||||
@@ -133,6 +133,33 @@ namespace AGVNavigationCore.Models
|
||||
/// </summary>
|
||||
public Color BackColor { get; set; } = Color.Transparent;
|
||||
|
||||
private float _textFontSize = 7.0f;
|
||||
|
||||
/// <summary>
|
||||
/// 텍스트 폰트 크기 (모든 노드 타입의 텍스트 표시에 사용, 픽셀 단위)
|
||||
/// 0 이하의 값이 설정되면 기본값 7.0f로 자동 설정
|
||||
/// </summary>
|
||||
public float TextFontSize
|
||||
{
|
||||
get => _textFontSize;
|
||||
set => _textFontSize = value > 0 ? value : 7.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 텍스트 볼드체 여부 (모든 노드 타입의 텍스트 표시에 사용)
|
||||
/// </summary>
|
||||
public bool TextFontBold { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 노드 이름 말풍선 배경색 (하단에 표시되는 노드 이름의 배경색)
|
||||
/// </summary>
|
||||
public Color NameBubbleBackColor { get; set; } = Color.Gold;
|
||||
|
||||
/// <summary>
|
||||
/// 노드 이름 말풍선 글자색 (하단에 표시되는 노드 이름의 글자색)
|
||||
/// </summary>
|
||||
public Color NameBubbleForeColor { get; set; } = Color.Black;
|
||||
|
||||
/// <summary>
|
||||
/// 라벨 배경 표시 여부 (NodeType.Label인 경우 사용)
|
||||
/// </summary>
|
||||
@@ -347,6 +374,10 @@ namespace AGVNavigationCore.Models
|
||||
FontStyle = FontStyle,
|
||||
ForeColor = ForeColor,
|
||||
BackColor = BackColor,
|
||||
TextFontSize = TextFontSize,
|
||||
TextFontBold = TextFontBold,
|
||||
NameBubbleBackColor = NameBubbleBackColor,
|
||||
NameBubbleForeColor = NameBubbleForeColor,
|
||||
ShowBackground = ShowBackground,
|
||||
Padding = Padding,
|
||||
ImagePath = ImagePath,
|
||||
|
||||
@@ -306,15 +306,15 @@ namespace AGVNavigationCore.Models
|
||||
}
|
||||
|
||||
// 5. 방향체크
|
||||
if(CurrentDirection != TargetNode.MotorDirection)
|
||||
{
|
||||
return new AGVCommand(
|
||||
MotorCommand.Stop,
|
||||
MagnetPosition.S,
|
||||
SpeedLevel.L,
|
||||
$"(재탐색요청)모터방향 불일치 현재위치:{_currentNode.NodeId}"
|
||||
);
|
||||
}
|
||||
//if(CurrentDirection != TargetNode.MotorDirection)
|
||||
//{
|
||||
// return new AGVCommand(
|
||||
// MotorCommand.Stop,
|
||||
// MagnetPosition.S,
|
||||
// SpeedLevel.L,
|
||||
// $"(재탐색요청)모터방향 불일치 현재위치:{_currentNode.NodeId}"
|
||||
// );
|
||||
//}
|
||||
|
||||
|
||||
//this.CurrentNodeId
|
||||
|
||||
Reference in New Issue
Block a user