refactor: Consolidate RFID mapping and add bidirectional pathfinding
Major improvements to AGV navigation system: • Consolidated RFID management into MapNode, removing duplicate RfidMapping class • Enhanced MapNode with RFID metadata fields (RfidStatus, RfidDescription) • Added automatic bidirectional connection generation in pathfinding algorithms • Updated all components to use unified MapNode-based RFID system • Added command line argument support for AGVMapEditor auto-loading files • Fixed pathfinding failures by ensuring proper node connectivity Technical changes: - Removed RfidMapping class and dependencies across all projects - Updated AStarPathfinder with EnsureBidirectionalConnections() method - Modified MapLoader to use AssignAutoRfidIds() for RFID automation - Enhanced UnifiedAGVCanvas, SimulatorForm, and MainForm for MapNode integration - Improved data consistency and reduced memory footprint 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
59
Cs_HMI/AGVMapEditor/Controls/MapCanvas.Designer.cs
generated
59
Cs_HMI/AGVMapEditor/Controls/MapCanvas.Designer.cs
generated
@@ -1,59 +0,0 @@
|
||||
namespace AGVMapEditor.Controls
|
||||
{
|
||||
partial class MapCanvas
|
||||
{
|
||||
/// <summary>
|
||||
/// 필수 디자이너 변수입니다.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// 사용 중인 모든 리소스를 정리합니다.
|
||||
/// </summary>
|
||||
/// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (components != null)
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
|
||||
// MapCanvas의 추가 리소스 정리
|
||||
_normalNodeBrush?.Dispose();
|
||||
_rotationNodeBrush?.Dispose();
|
||||
_dockingNodeBrush?.Dispose();
|
||||
_chargingNodeBrush?.Dispose();
|
||||
_selectedNodeBrush?.Dispose();
|
||||
_hoveredNodeBrush?.Dispose();
|
||||
_connectionPen?.Dispose();
|
||||
_gridPen?.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region 구성 요소 디자이너에서 생성한 코드
|
||||
|
||||
/// <summary>
|
||||
/// 디자이너 지원에 필요한 메서드입니다.
|
||||
/// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// MapCanvas
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.White;
|
||||
this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.Name = "MapCanvas";
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,608 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using AGVMapEditor.Models;
|
||||
|
||||
namespace AGVMapEditor.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// 맵 편집을 위한 그래픽 캔버스 컨트롤
|
||||
/// </summary>
|
||||
public partial class MapCanvas : UserControl
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int NODE_SIZE = 20;
|
||||
private const int NODE_RADIUS = NODE_SIZE / 2;
|
||||
private const int GRID_SIZE = 20;
|
||||
private const float CONNECTION_WIDTH = 2.0f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
private List<MapNode> _nodes;
|
||||
private MapNode _selectedNode;
|
||||
private MapNode _hoveredNode;
|
||||
private bool _isDragging;
|
||||
private Point _dragOffset;
|
||||
private Point _lastMousePosition;
|
||||
|
||||
// 그리드 및 줌 관련
|
||||
private bool _showGrid = true;
|
||||
private float _zoomFactor = 1.0f;
|
||||
private Point _panOffset = Point.Empty;
|
||||
|
||||
// 브러쉬 및 펜
|
||||
private Brush _normalNodeBrush;
|
||||
private Brush _rotationNodeBrush;
|
||||
private Brush _dockingNodeBrush;
|
||||
private Brush _chargingNodeBrush;
|
||||
private Brush _selectedNodeBrush;
|
||||
private Brush _hoveredNodeBrush;
|
||||
private Pen _connectionPen;
|
||||
private Pen _gridPen;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// 노드가 선택되었을 때 발생하는 이벤트
|
||||
/// </summary>
|
||||
public event EventHandler<MapNode> NodeSelected;
|
||||
|
||||
/// <summary>
|
||||
/// 노드가 이동되었을 때 발생하는 이벤트
|
||||
/// </summary>
|
||||
public event EventHandler<MapNode> NodeMoved;
|
||||
|
||||
/// <summary>
|
||||
/// 배경이 클릭되었을 때 발생하는 이벤트
|
||||
/// </summary>
|
||||
public event EventHandler<Point> BackgroundClicked;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
public MapCanvas() : this(new List<MapNode>())
|
||||
{
|
||||
}
|
||||
|
||||
public MapCanvas(List<MapNode> nodes)
|
||||
{
|
||||
InitializeComponent();
|
||||
InitializeGraphics();
|
||||
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw, true);
|
||||
|
||||
_nodes = nodes ?? new List<MapNode>();
|
||||
|
||||
// 이벤트 연결
|
||||
this.MouseDown += MapCanvas_MouseDown;
|
||||
this.MouseMove += MapCanvas_MouseMove;
|
||||
this.MouseUp += MapCanvas_MouseUp;
|
||||
this.MouseWheel += MapCanvas_MouseWheel;
|
||||
this.KeyDown += MapCanvas_KeyDown;
|
||||
|
||||
// 포커스를 받을 수 있도록 설정
|
||||
this.TabStop = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Graphics Initialization
|
||||
|
||||
private void InitializeGraphics()
|
||||
{
|
||||
_normalNodeBrush = new SolidBrush(Color.Blue);
|
||||
_rotationNodeBrush = new SolidBrush(Color.Orange);
|
||||
_dockingNodeBrush = new SolidBrush(Color.Green);
|
||||
_chargingNodeBrush = new SolidBrush(Color.Red);
|
||||
_selectedNodeBrush = new SolidBrush(Color.Yellow);
|
||||
_hoveredNodeBrush = new SolidBrush(Color.LightBlue);
|
||||
|
||||
_connectionPen = new Pen(Color.Gray, CONNECTION_WIDTH);
|
||||
_gridPen = new Pen(Color.LightGray, 1.0f) { DashStyle = DashStyle.Dot };
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// 그리드 표시 여부
|
||||
/// </summary>
|
||||
public bool ShowGrid
|
||||
{
|
||||
get { return _showGrid; }
|
||||
set
|
||||
{
|
||||
_showGrid = value;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 줌 팩터
|
||||
/// </summary>
|
||||
public float ZoomFactor
|
||||
{
|
||||
get { return _zoomFactor; }
|
||||
set
|
||||
{
|
||||
_zoomFactor = Math.Max(0.1f, Math.Min(5.0f, value));
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 선택된 노드
|
||||
/// </summary>
|
||||
public MapNode SelectedNode
|
||||
{
|
||||
get { return _selectedNode; }
|
||||
set
|
||||
{
|
||||
if (_selectedNode != value)
|
||||
{
|
||||
_selectedNode = value;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mouse Events
|
||||
|
||||
private void MapCanvas_MouseDown(object sender, MouseEventArgs e)
|
||||
{
|
||||
this.Focus(); // 키보드 이벤트를 받기 위해 포커스 설정
|
||||
|
||||
var worldPoint = ScreenToWorld(e.Location);
|
||||
var hitNode = GetNodeAt(worldPoint);
|
||||
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
if (hitNode != null)
|
||||
{
|
||||
// 노드 선택 및 드래그 시작
|
||||
SelectNode(hitNode);
|
||||
_isDragging = true;
|
||||
_dragOffset = new Point(worldPoint.X - hitNode.Position.X, worldPoint.Y - hitNode.Position.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 배경 클릭
|
||||
SelectNode(null);
|
||||
BackgroundClicked?.Invoke(this, worldPoint);
|
||||
}
|
||||
}
|
||||
else if (e.Button == MouseButtons.Right)
|
||||
{
|
||||
// 우클릭 메뉴 (추후 구현)
|
||||
ShowContextMenu(worldPoint, hitNode);
|
||||
}
|
||||
|
||||
_lastMousePosition = e.Location;
|
||||
}
|
||||
|
||||
private void MapCanvas_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
var worldPoint = ScreenToWorld(e.Location);
|
||||
|
||||
if (_isDragging && _selectedNode != null)
|
||||
{
|
||||
// 노드 드래그
|
||||
var newPosition = new Point(worldPoint.X - _dragOffset.X, worldPoint.Y - _dragOffset.Y);
|
||||
|
||||
// 그리드에 맞춤 (Ctrl 키를 누르지 않은 경우)
|
||||
if (!ModifierKeys.HasFlag(Keys.Control))
|
||||
{
|
||||
newPosition.X = (newPosition.X / GRID_SIZE) * GRID_SIZE;
|
||||
newPosition.Y = (newPosition.Y / GRID_SIZE) * GRID_SIZE;
|
||||
}
|
||||
|
||||
_selectedNode.Position = newPosition;
|
||||
Invalidate();
|
||||
}
|
||||
else if (e.Button == MouseButtons.Middle || (e.Button == MouseButtons.Left && ModifierKeys.HasFlag(Keys.Space)))
|
||||
{
|
||||
// 팬 (화면 이동)
|
||||
var deltaX = e.X - _lastMousePosition.X;
|
||||
var deltaY = e.Y - _lastMousePosition.Y;
|
||||
_panOffset.X += deltaX;
|
||||
_panOffset.Y += deltaY;
|
||||
Invalidate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 호버 효과
|
||||
var hitNode = GetNodeAt(worldPoint);
|
||||
if (_hoveredNode != hitNode)
|
||||
{
|
||||
_hoveredNode = hitNode;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
_lastMousePosition = e.Location;
|
||||
}
|
||||
|
||||
private void MapCanvas_MouseUp(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (_isDragging && _selectedNode != null)
|
||||
{
|
||||
NodeMoved?.Invoke(this, _selectedNode);
|
||||
}
|
||||
|
||||
_isDragging = false;
|
||||
}
|
||||
|
||||
private void MapCanvas_MouseWheel(object sender, MouseEventArgs e)
|
||||
{
|
||||
// 줌
|
||||
var delta = e.Delta > 0 ? 1.1f : 0.9f;
|
||||
ZoomFactor *= delta;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Keyboard Events
|
||||
|
||||
private void MapCanvas_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
switch (e.KeyCode)
|
||||
{
|
||||
case Keys.Delete:
|
||||
// 선택된 노드 삭제 (메인 폼에서 처리하도록 이벤트 발생)
|
||||
if (_selectedNode != null)
|
||||
{
|
||||
// 삭제 확인 후 처리는 메인 폼에서
|
||||
}
|
||||
break;
|
||||
|
||||
case Keys.G:
|
||||
// 그리드 토글
|
||||
ShowGrid = !ShowGrid;
|
||||
break;
|
||||
|
||||
case Keys.Home:
|
||||
// 뷰 리셋
|
||||
ZoomFactor = 1.0f;
|
||||
_panOffset = Point.Empty;
|
||||
Invalidate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Coordinate Conversion
|
||||
|
||||
/// <summary>
|
||||
/// 스크린 좌표를 월드 좌표로 변환
|
||||
/// </summary>
|
||||
private Point ScreenToWorld(Point screenPoint)
|
||||
{
|
||||
var worldX = (int)((screenPoint.X - _panOffset.X) / _zoomFactor);
|
||||
var worldY = (int)((screenPoint.Y - _panOffset.Y) / _zoomFactor);
|
||||
return new Point(worldX, worldY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 월드 좌표를 스크린 좌표로 변환
|
||||
/// </summary>
|
||||
private Point WorldToScreen(Point worldPoint)
|
||||
{
|
||||
var screenX = (int)(worldPoint.X * _zoomFactor + _panOffset.X);
|
||||
var screenY = (int)(worldPoint.Y * _zoomFactor + _panOffset.Y);
|
||||
return new Point(screenX, screenY);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Node Management
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 위치의 노드 검색
|
||||
/// </summary>
|
||||
private MapNode GetNodeAt(Point worldPoint)
|
||||
{
|
||||
foreach (var node in _nodes)
|
||||
{
|
||||
var distance = Math.Sqrt(Math.Pow(worldPoint.X - node.Position.X, 2) + Math.Pow(worldPoint.Y - node.Position.Y, 2));
|
||||
if (distance <= NODE_RADIUS)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 노드 선택
|
||||
/// </summary>
|
||||
private void SelectNode(MapNode node)
|
||||
{
|
||||
if (_selectedNode != node)
|
||||
{
|
||||
_selectedNode = node;
|
||||
NodeSelected?.Invoke(this, node);
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 우클릭 컨텍스트 메뉴 표시
|
||||
/// </summary>
|
||||
private void ShowContextMenu(Point worldPoint, MapNode node)
|
||||
{
|
||||
var contextMenu = new ContextMenuStrip();
|
||||
|
||||
if (node != null)
|
||||
{
|
||||
contextMenu.Items.Add("노드 삭제", null, (s, e) => { /* 삭제 처리 */ });
|
||||
contextMenu.Items.Add(new ToolStripSeparator());
|
||||
contextMenu.Items.Add("일반 노드로 변경", null, (s, e) => ChangeNodeType(node, NodeType.Normal));
|
||||
contextMenu.Items.Add("회전 지점으로 변경", null, (s, e) => ChangeNodeType(node, NodeType.Rotation));
|
||||
contextMenu.Items.Add("도킹 스테이션으로 변경", null, (s, e) => ChangeNodeType(node, NodeType.Docking));
|
||||
contextMenu.Items.Add("충전 스테이션으로 변경", null, (s, e) => ChangeNodeType(node, NodeType.Charging));
|
||||
}
|
||||
else
|
||||
{
|
||||
contextMenu.Items.Add("새 노드 추가", null, (s, e) => { /* 노드 추가 처리 */ });
|
||||
}
|
||||
|
||||
var screenPoint = WorldToScreen(worldPoint);
|
||||
contextMenu.Show(this, screenPoint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 노드 타입 변경
|
||||
/// </summary>
|
||||
private void ChangeNodeType(MapNode node, NodeType newType)
|
||||
{
|
||||
node.Type = newType;
|
||||
node.SetDefaultColorByType(newType);
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Painting
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
base.OnPaint(e);
|
||||
|
||||
var g = e.Graphics;
|
||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
|
||||
// 변환 매트릭스 적용
|
||||
g.ResetTransform();
|
||||
g.TranslateTransform(_panOffset.X, _panOffset.Y);
|
||||
g.ScaleTransform(_zoomFactor, _zoomFactor);
|
||||
|
||||
// 배경 그리드 그리기
|
||||
if (_showGrid)
|
||||
{
|
||||
DrawGrid(g);
|
||||
}
|
||||
|
||||
// 연결선 그리기
|
||||
DrawConnections(g);
|
||||
|
||||
// 노드 그리기
|
||||
DrawNodes(g);
|
||||
|
||||
// 선택된 노드의 연결 정보 강조 표시
|
||||
if (_selectedNode != null)
|
||||
{
|
||||
DrawSelectedNodeConnections(g);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGrid(Graphics g)
|
||||
{
|
||||
var bounds = GetVisibleWorldBounds();
|
||||
|
||||
// 수직선
|
||||
for (int x = (bounds.Left / GRID_SIZE) * GRID_SIZE; x <= bounds.Right; x += GRID_SIZE)
|
||||
{
|
||||
g.DrawLine(_gridPen, x, bounds.Top, x, bounds.Bottom);
|
||||
}
|
||||
|
||||
// 수평선
|
||||
for (int y = (bounds.Top / GRID_SIZE) * GRID_SIZE; y <= bounds.Bottom; y += GRID_SIZE)
|
||||
{
|
||||
g.DrawLine(_gridPen, bounds.Left, y, bounds.Right, y);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawConnections(Graphics g)
|
||||
{
|
||||
foreach (var node in _nodes)
|
||||
{
|
||||
foreach (var connectedNodeId in node.ConnectedNodes)
|
||||
{
|
||||
var connectedNode = _nodes.FirstOrDefault(n => n.NodeId == connectedNodeId);
|
||||
if (connectedNode != null)
|
||||
{
|
||||
g.DrawLine(_connectionPen, node.Position, connectedNode.Position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawNodes(Graphics g)
|
||||
{
|
||||
foreach (var node in _nodes)
|
||||
{
|
||||
DrawNode(g, node);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawNode(Graphics g, MapNode node)
|
||||
{
|
||||
var brush = GetNodeBrush(node);
|
||||
var rect = new Rectangle(
|
||||
node.Position.X - NODE_RADIUS,
|
||||
node.Position.Y - NODE_RADIUS,
|
||||
NODE_SIZE,
|
||||
NODE_SIZE);
|
||||
|
||||
// 노드 그리기
|
||||
switch (node.Type)
|
||||
{
|
||||
case NodeType.Normal:
|
||||
g.FillEllipse(brush, rect);
|
||||
break;
|
||||
case NodeType.Rotation:
|
||||
g.FillRectangle(brush, rect);
|
||||
break;
|
||||
case NodeType.Docking:
|
||||
g.FillRectangle(brush, rect);
|
||||
// 도킹 방향 표시
|
||||
DrawDockingDirection(g, node);
|
||||
break;
|
||||
case NodeType.Charging:
|
||||
g.FillEllipse(brush, rect);
|
||||
// 충전 표시 (+)
|
||||
DrawChargingSymbol(g, node);
|
||||
break;
|
||||
}
|
||||
|
||||
// 노드 테두리
|
||||
g.DrawEllipse(Pens.Black, rect);
|
||||
|
||||
// 노드 이름 표시
|
||||
if (_zoomFactor > 0.5f) // 줌이 충분히 큰 경우만 텍스트 표시
|
||||
{
|
||||
var font = new Font("Arial", 8 * _zoomFactor);
|
||||
var textRect = new RectangleF(
|
||||
node.Position.X - 30,
|
||||
node.Position.Y + NODE_RADIUS + 2,
|
||||
60, 15);
|
||||
|
||||
var format = new StringFormat
|
||||
{
|
||||
Alignment = StringAlignment.Center,
|
||||
LineAlignment = StringAlignment.Near
|
||||
};
|
||||
|
||||
g.DrawString(node.Name, font, Brushes.Black, textRect, format);
|
||||
}
|
||||
}
|
||||
|
||||
private Brush GetNodeBrush(MapNode node)
|
||||
{
|
||||
if (node == _selectedNode)
|
||||
return _selectedNodeBrush;
|
||||
|
||||
if (node == _hoveredNode)
|
||||
return _hoveredNodeBrush;
|
||||
|
||||
switch (node.Type)
|
||||
{
|
||||
case NodeType.Normal:
|
||||
return _normalNodeBrush;
|
||||
case NodeType.Rotation:
|
||||
return _rotationNodeBrush;
|
||||
case NodeType.Docking:
|
||||
return _dockingNodeBrush;
|
||||
case NodeType.Charging:
|
||||
return _chargingNodeBrush;
|
||||
default:
|
||||
return _normalNodeBrush;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDockingDirection(Graphics g, MapNode node)
|
||||
{
|
||||
if (node.DockDirection == null)
|
||||
return;
|
||||
|
||||
var arrowSize = 8;
|
||||
var arrowPen = new Pen(Color.White, 2);
|
||||
|
||||
Point arrowStart, arrowEnd;
|
||||
|
||||
if (node.DockDirection == DockingDirection.Forward)
|
||||
{
|
||||
arrowStart = new Point(node.Position.X - arrowSize/2, node.Position.Y);
|
||||
arrowEnd = new Point(node.Position.X + arrowSize/2, node.Position.Y);
|
||||
}
|
||||
else // Backward
|
||||
{
|
||||
arrowStart = new Point(node.Position.X + arrowSize/2, node.Position.Y);
|
||||
arrowEnd = new Point(node.Position.X - arrowSize/2, node.Position.Y);
|
||||
}
|
||||
|
||||
g.DrawLine(arrowPen, arrowStart, arrowEnd);
|
||||
|
||||
// 화살표 머리
|
||||
var headSize = 3;
|
||||
var headPoints = new Point[]
|
||||
{
|
||||
arrowEnd,
|
||||
new Point(arrowEnd.X - headSize, arrowEnd.Y - headSize),
|
||||
new Point(arrowEnd.X - headSize, arrowEnd.Y + headSize)
|
||||
};
|
||||
g.FillPolygon(Brushes.White, headPoints);
|
||||
}
|
||||
|
||||
private void DrawChargingSymbol(Graphics g, MapNode node)
|
||||
{
|
||||
var symbolSize = 6;
|
||||
var symbolPen = new Pen(Color.White, 2);
|
||||
|
||||
// + 모양
|
||||
g.DrawLine(symbolPen,
|
||||
node.Position.X - symbolSize/2, node.Position.Y,
|
||||
node.Position.X + symbolSize/2, node.Position.Y);
|
||||
g.DrawLine(symbolPen,
|
||||
node.Position.X, node.Position.Y - symbolSize/2,
|
||||
node.Position.X, node.Position.Y + symbolSize/2);
|
||||
}
|
||||
|
||||
private void DrawSelectedNodeConnections(Graphics g)
|
||||
{
|
||||
var highlightPen = new Pen(Color.Yellow, CONNECTION_WIDTH + 2);
|
||||
|
||||
foreach (var connectedNodeId in _selectedNode.ConnectedNodes)
|
||||
{
|
||||
var connectedNode = _nodes.FirstOrDefault(n => n.NodeId == connectedNodeId);
|
||||
if (connectedNode != null)
|
||||
{
|
||||
g.DrawLine(highlightPen, _selectedNode.Position, connectedNode.Position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
/// <summary>
|
||||
/// 현재 보이는 월드 영역 계산
|
||||
/// </summary>
|
||||
private Rectangle GetVisibleWorldBounds()
|
||||
{
|
||||
var topLeft = ScreenToWorld(new Point(0, 0));
|
||||
var bottomRight = ScreenToWorld(new Point(Width, Height));
|
||||
|
||||
return new Rectangle(
|
||||
topLeft.X, topLeft.Y,
|
||||
bottomRight.X - topLeft.X,
|
||||
bottomRight.Y - topLeft.Y);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
34
Cs_HMI/AGVMapEditor/Controls/MapCanvas_Interactive.Designer.cs
generated
Normal file
34
Cs_HMI/AGVMapEditor/Controls/MapCanvas_Interactive.Designer.cs
generated
Normal file
@@ -0,0 +1,34 @@
|
||||
namespace AGVMapEditor.Controls
|
||||
{
|
||||
partial class MapCanvasInteractive
|
||||
{
|
||||
/// <summary>
|
||||
/// 필수 디자이너 변수입니다.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
|
||||
#region 구성 요소 디자이너에서 생성한 코드
|
||||
|
||||
/// <summary>
|
||||
/// 디자이너 지원에 필요한 메서드입니다.
|
||||
/// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// MapCanvasInteractive
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.White;
|
||||
this.Name = "MapCanvasInteractive";
|
||||
this.Size = new System.Drawing.Size(800, 600);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1358
Cs_HMI/AGVMapEditor/Controls/MapCanvas_Interactive.cs
Normal file
1358
Cs_HMI/AGVMapEditor/Controls/MapCanvas_Interactive.cs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user