1701 lines
66 KiB
C#
1701 lines
66 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Drawing.Design;
|
|
using System.Drawing.Drawing2D;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Windows.Forms;
|
|
using AGVControl.Models;
|
|
|
|
namespace AGVControl
|
|
{
|
|
public partial class MapControl : Control
|
|
{
|
|
private List<RFIDPoint> rfidPoints;
|
|
private List<MagnetLine> magnetLines;
|
|
private List<MapText> mapTexts;
|
|
private List<CustomLine> customLines;
|
|
private List<RFIDLine> rfidLines;
|
|
private AGV agv;
|
|
private float zoom = 1.0f;
|
|
private PointF offset = PointF.Empty;
|
|
private Point lastMousePosition;
|
|
private bool isDragging = false;
|
|
private Point? previewStartPoint = null;
|
|
private Point currentMousePosition;
|
|
private const int SNAP_DISTANCE = 10; // 점 근접 거리
|
|
private const int LINE_WIDTH = 20; // 선 굵기
|
|
private const int TOOLBAR_WIDTH = 58;
|
|
private const int TOOLBAR_WIDTHR = 78;
|
|
private const int TOOLBAR_BUTTON_HEIGHT = 40;
|
|
private const int TOOLBAR_MARGIN = 5;
|
|
|
|
private List<ToolBarItem> toolbarRects;
|
|
|
|
|
|
private bool isAddingText = false;
|
|
private bool isAddingPoint = false;
|
|
private bool isDrawingCustomLine = false;
|
|
private bool isDrawingMagnetLine = false;
|
|
private bool isDrawingRFIDLine = false;
|
|
private bool isDeletingRFIDLine = false;
|
|
private bool isDrawingLine = false;
|
|
private MapText selectedText = null;
|
|
private CustomLine selectedLine = null;
|
|
private RFIDPoint selectedRFID = null;
|
|
private MagnetLine selectedMagnetLine = null;
|
|
private RFIDLine selectedRFIDLine = null;
|
|
private Point? draggingPoint = null;
|
|
private bool isDraggingPoint = false;
|
|
private const int SELECTION_DISTANCE = 15; // 선택 가능 거리를 늘림
|
|
private bool isDraggingText = false;
|
|
private Point dragOffset;
|
|
private List<Point> currentPath;
|
|
|
|
|
|
public MapText SelectedText => selectedText;
|
|
public CustomLine SelectedLine => selectedLine;
|
|
public RFIDPoint SelectedRFID => selectedRFID;
|
|
public MagnetLine SelectedMagnetLine => selectedMagnetLine;
|
|
public RFIDLine SelectedRFIDLine => selectedRFIDLine;
|
|
|
|
|
|
|
|
public float GetDistance(Point p1, Point p2)
|
|
{
|
|
float dx = p1.X - p2.X;
|
|
float dy = p1.Y - p2.Y;
|
|
return (float)Math.Sqrt(dx * dx + dy * dy); // double을 float로 명시적 캐스팅
|
|
}
|
|
|
|
public MapControl()
|
|
{
|
|
this.DoubleBuffered = true;
|
|
rfidPoints = new List<RFIDPoint>();
|
|
magnetLines = new List<MagnetLine>();
|
|
mapTexts = new List<MapText>();
|
|
customLines = new List<CustomLine>();
|
|
rfidLines = new List<RFIDLine>();
|
|
agv = new AGV();
|
|
|
|
// 툴바 버튼 영역 초기화
|
|
UpdateToolbarRects();
|
|
|
|
// 마우스 이벤트 핸들러 추가
|
|
this.MouseWheel += MapControl_MouseWheel;
|
|
this.MouseDown += MapControl_MouseDown;
|
|
this.MouseMove += MapControl_MouseMove;
|
|
this.MouseUp += MapControl_MouseUp;
|
|
this.MouseClick += MapControl_MouseClick;
|
|
this.MouseDoubleClick += MapControl_MouseDoubleClick;
|
|
this.Resize += MapControl_Resize;
|
|
}
|
|
|
|
private void MapControl_Resize(object sender, EventArgs e)
|
|
{
|
|
UpdateToolbarRects();
|
|
this.Invalidate();
|
|
}
|
|
|
|
private void UpdateToolbarRects()
|
|
{
|
|
int x, y, c, row;
|
|
var idx = 0;
|
|
|
|
if (this.toolbarRects == null) this.toolbarRects = new List<ToolBarItem>();
|
|
else this.toolbarRects.Clear();
|
|
|
|
//left toolbar
|
|
x = TOOLBAR_MARGIN;
|
|
y = 10;
|
|
toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "+", Bounds = new Rectangle(x, y, TOOLBAR_WIDTH - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
|
|
|
|
y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
|
|
toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "-", Bounds = new Rectangle(x, y, TOOLBAR_WIDTH - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
|
|
|
|
y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
|
|
toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "1:1", Bounds = new Rectangle(x, y, TOOLBAR_WIDTH - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
|
|
|
|
y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
|
|
toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Cut", Bounds = new Rectangle(x, y, TOOLBAR_WIDTH - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
|
|
|
|
|
|
//right toolbar
|
|
y = 10;
|
|
row = 0;
|
|
x = DisplayRectangle.Right - TOOLBAR_WIDTHR - TOOLBAR_MARGIN;
|
|
toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Text", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
|
|
|
|
y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
|
|
toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Line", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
|
|
|
|
y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
|
|
toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Point", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
|
|
|
|
y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
|
|
toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Magnet", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
|
|
|
|
y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
|
|
toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Load", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
|
|
|
|
y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
|
|
toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Save", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
|
|
|
|
y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
|
|
toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Pos", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
|
|
|
|
y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
|
|
toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Path", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
|
|
|
|
}
|
|
|
|
public void SetPreviewStartPoint(Point? point)
|
|
{
|
|
previewStartPoint = point;
|
|
this.Invalidate();
|
|
}
|
|
|
|
public void UpdatePreviewLine(Point currentPosition)
|
|
{
|
|
currentMousePosition = currentPosition;
|
|
this.Invalidate();
|
|
}
|
|
|
|
private Point SnapToPoint(Point point)
|
|
{
|
|
// RFID 포인트와 근접한지 확인
|
|
foreach (var rfid in rfidPoints)
|
|
{
|
|
if (GetDistance(point, rfid.Location) <= SNAP_DISTANCE)
|
|
{
|
|
return rfid.Location;
|
|
}
|
|
}
|
|
|
|
// 마그넷 라인의 끝점과 근접한지 확인
|
|
foreach (var line in magnetLines)
|
|
{
|
|
if (GetDistance(point, line.StartPoint) <= SNAP_DISTANCE)
|
|
{
|
|
return line.StartPoint;
|
|
}
|
|
if (GetDistance(point, line.EndPoint) <= SNAP_DISTANCE)
|
|
{
|
|
return line.EndPoint;
|
|
}
|
|
}
|
|
|
|
return point;
|
|
}
|
|
|
|
// 화면 좌표를 실제 맵 좌표로 변환
|
|
public Point ScreenToMap(Point screenPoint)
|
|
{
|
|
int adjustedX = screenPoint.X;
|
|
return new Point(
|
|
(int)((adjustedX - offset.X) / zoom),
|
|
(int)((screenPoint.Y - offset.Y) / zoom)
|
|
);
|
|
}
|
|
|
|
public RFIDPoint FindRFIDPoint(string rfidValue)
|
|
{
|
|
return rfidPoints.FirstOrDefault(r => r.RFIDValue.ToUpper() == rfidValue.ToUpper());
|
|
}
|
|
|
|
public bool AGVMoveToRFID(string rfidValue)
|
|
{
|
|
var rfidPoint = FindRFIDPoint(rfidValue);
|
|
if (rfidPoint != null)
|
|
{
|
|
agv.CurrentPosition = rfidPoint.Location;
|
|
agv.CurrentRFID = rfidValue;
|
|
this.Invalidate();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
private void MapControl_MouseWheel(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Delta > 0)
|
|
{
|
|
zoom *= 1.1f;
|
|
}
|
|
else
|
|
{
|
|
zoom /= 1.1f;
|
|
}
|
|
|
|
zoom = Math.Max(0.1f, Math.Min(10.0f, zoom));
|
|
this.Invalidate();
|
|
}
|
|
|
|
private void MapControl_MouseDown(object sender, MouseEventArgs e)
|
|
{
|
|
lastMousePosition = e.Location;
|
|
var mapPoint = ScreenToMap(e.Location);
|
|
|
|
if (e.Button == MouseButtons.Middle)
|
|
{
|
|
isDragging = true;
|
|
this.Cursor = Cursors.SizeAll;
|
|
}
|
|
else if (e.Button == MouseButtons.Left && !isAddingText && !isDrawingCustomLine && !isDrawingRFIDLine && !isDrawingMagnetLine)
|
|
{
|
|
isDragging = true;
|
|
|
|
// 텍스트 선택 및 드래그 시작
|
|
foreach (var text in mapTexts)
|
|
{
|
|
var textSize = CreateGraphics().MeasureString(text.Text, text.Font);
|
|
var rect = new RectangleF(
|
|
text.Location.X - SELECTION_DISTANCE,
|
|
text.Location.Y - SELECTION_DISTANCE,
|
|
textSize.Width + SELECTION_DISTANCE * 2,
|
|
textSize.Height + SELECTION_DISTANCE * 2
|
|
);
|
|
|
|
if (rect.Contains(mapPoint))
|
|
{
|
|
selectedText = text;
|
|
isDraggingText = true;
|
|
// 드래그 시작점과 텍스트 위치의 차이를 저장
|
|
dragOffset = new Point(
|
|
text.Location.X - mapPoint.X,
|
|
text.Location.Y - mapPoint.Y
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 커스텀 라인 선택 및 드래그 포인트 설정
|
|
foreach (var line in customLines)
|
|
{
|
|
float startDistance = GetDistance(mapPoint, line.StartPoint);
|
|
float endDistance = GetDistance(mapPoint, line.EndPoint);
|
|
|
|
if (startDistance < SELECTION_DISTANCE)
|
|
{
|
|
selectedLine = line;
|
|
draggingPoint = line.StartPoint;
|
|
isDraggingPoint = true;
|
|
return;
|
|
}
|
|
else if (endDistance < SELECTION_DISTANCE)
|
|
{
|
|
selectedLine = line;
|
|
draggingPoint = line.EndPoint;
|
|
isDraggingPoint = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// RFID 포인트 선택
|
|
var clickedRFID = rfidPoints.FirstOrDefault(r =>
|
|
GetDistance(mapPoint, r.Location) <= SNAP_DISTANCE);
|
|
if (clickedRFID != null)
|
|
{
|
|
selectedRFID = clickedRFID;
|
|
draggingPoint = clickedRFID.Location;
|
|
isDraggingPoint = true;
|
|
return;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
Point? lineStartPoint = null;
|
|
Point? branchPoint = null;
|
|
|
|
private void MapControl_MouseMove(object sender, MouseEventArgs e)
|
|
{
|
|
currentMousePosition = e.Location;
|
|
var mapPoint = ScreenToMap(e.Location);
|
|
|
|
if ((mousemode == eMouseMode.addcustomline || mousemode == eMouseMode.addrfidline) && branchPoint.HasValue)
|
|
{
|
|
UpdatePreviewLine(e.Location);
|
|
}
|
|
|
|
// 툴바 버튼 호버 상태 업데이트
|
|
var oldHovering = toolbarRects.OrderBy(t => t.Idx).Select(t => t.isHovering).ToArray();
|
|
|
|
//toolbar check
|
|
toolbarRects.ForEach(t => t.isHovering = t.Bounds.Contains(e.Location));
|
|
|
|
//hovering check
|
|
if (toolbarRects.Where(t => t.isHovering).Any())
|
|
{
|
|
this.Cursor = Cursors.Hand;
|
|
}
|
|
else if (isDeletingRFIDLine)
|
|
{
|
|
this.Cursor = GetScissorsCursor();
|
|
}
|
|
else
|
|
{
|
|
this.Cursor = Cursors.Default;
|
|
}
|
|
|
|
//hovering display update
|
|
if (toolbarRects.Where(t => t.Dirty).Any())
|
|
{
|
|
this.Invalidate();
|
|
}
|
|
|
|
if (isDragging)
|
|
{
|
|
if (e.Button == MouseButtons.Middle)
|
|
{
|
|
offset = new PointF(
|
|
offset.X + (e.Location.X - lastMousePosition.X),
|
|
offset.Y + (e.Location.Y - lastMousePosition.Y)
|
|
);
|
|
}
|
|
else if (isDraggingText && selectedText != null)
|
|
{
|
|
// 텍스트 이동 - 드래그 오프셋 적용
|
|
selectedText.Location = new Point(
|
|
mapPoint.X + dragOffset.X,
|
|
mapPoint.Y + dragOffset.Y
|
|
);
|
|
}
|
|
else if (isDraggingPoint && draggingPoint.HasValue)
|
|
{
|
|
// 포인트 이동
|
|
var delta = new Point(
|
|
mapPoint.X - ScreenToMap(lastMousePosition).X,
|
|
mapPoint.Y - ScreenToMap(lastMousePosition).Y
|
|
);
|
|
|
|
if (selectedLine != null)
|
|
{
|
|
// 커스텀 라인 포인트 이동
|
|
if (draggingPoint.Value == selectedLine.StartPoint)
|
|
{
|
|
selectedLine.StartPoint = new Point(
|
|
selectedLine.StartPoint.X + delta.X,
|
|
selectedLine.StartPoint.Y + delta.Y
|
|
);
|
|
}
|
|
else if (draggingPoint.Value == selectedLine.EndPoint)
|
|
{
|
|
selectedLine.EndPoint = new Point(
|
|
selectedLine.EndPoint.X + delta.X,
|
|
selectedLine.EndPoint.Y + delta.Y
|
|
);
|
|
}
|
|
}
|
|
else if (selectedRFID != null) // RFID 포인트 이동
|
|
{
|
|
// RFID 포인트 위치 업데이트
|
|
selectedRFID.Location = new Point(
|
|
selectedRFID.Location.X + delta.X,
|
|
selectedRFID.Location.Y + delta.Y
|
|
);
|
|
|
|
// 연결된 RFID 라인 업데이트
|
|
foreach (var line in rfidLines)
|
|
{
|
|
if (line.StartPoint == draggingPoint.Value)
|
|
{
|
|
line.StartPoint = selectedRFID.Location;
|
|
line.Distance = GetDistance(line.StartPoint, line.EndPoint);
|
|
}
|
|
if (line.EndPoint == draggingPoint.Value)
|
|
{
|
|
line.EndPoint = selectedRFID.Location;
|
|
line.Distance = GetDistance(line.StartPoint, line.EndPoint);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
lastMousePosition = e.Location;
|
|
this.Invalidate();
|
|
}
|
|
|
|
// 미리보기 라인 업데이트를 위한 마우스 위치 저장
|
|
if (isDrawingRFIDLine || isDrawingCustomLine || previewStartPoint.HasValue)
|
|
{
|
|
currentMousePosition = mapPoint;
|
|
}
|
|
|
|
this.Invalidate();
|
|
}
|
|
private List<Point> CalculatePath(Point start, Point end)
|
|
{
|
|
var openList = new List<Point> { start };
|
|
var closedList = new List<Point>();
|
|
var cameFrom = new Dictionary<Point, Point>();
|
|
var gScore = new Dictionary<Point, float> { { start, 0 } };
|
|
var fScore = new Dictionary<Point, float> { { start, Heuristic(start, end) } };
|
|
|
|
while (openList.Count > 0)
|
|
{
|
|
var current = openList.OrderBy(p => fScore.ContainsKey(p) ? fScore[p] : float.MaxValue).First();
|
|
|
|
if (current == end)
|
|
{
|
|
return ReconstructPath(cameFrom, current);
|
|
}
|
|
|
|
openList.Remove(current);
|
|
closedList.Add(current);
|
|
|
|
foreach (var neighbor in GetNeighbors(current))
|
|
{
|
|
if (closedList.Contains(neighbor))
|
|
continue;
|
|
|
|
float tentativeGScore = gScore[current] + Distance(current, neighbor);
|
|
|
|
if (!openList.Contains(neighbor))
|
|
openList.Add(neighbor);
|
|
else if (tentativeGScore >= gScore[neighbor])
|
|
continue;
|
|
|
|
cameFrom[neighbor] = current;
|
|
gScore[neighbor] = tentativeGScore;
|
|
fScore[neighbor] = gScore[neighbor] + Heuristic(neighbor, end);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
private List<Point> ReconstructPath(Dictionary<Point, Point> cameFrom, Point current)
|
|
{
|
|
var path = new List<Point> { current };
|
|
while (cameFrom.ContainsKey(current))
|
|
{
|
|
current = cameFrom[current];
|
|
path.Insert(0, current);
|
|
}
|
|
return path;
|
|
}
|
|
private float Heuristic(Point a, Point b)
|
|
{
|
|
return (float)Math.Sqrt(Math.Pow(a.X - b.X, 2) + Math.Pow(a.Y - b.Y, 2));
|
|
}
|
|
|
|
|
|
private void MapControl_MouseUp(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Middle)
|
|
{
|
|
isDragging = false;
|
|
this.Cursor = Cursors.Default;
|
|
}
|
|
else if (e.Button == MouseButtons.Left)
|
|
{
|
|
isDragging = false;
|
|
isDraggingPoint = false;
|
|
isDraggingText = false;
|
|
}
|
|
}
|
|
private float Distance(Point a, Point b)
|
|
{
|
|
// RFID 라인을 통한 연결 확인
|
|
var rfidLines = GetRFIDLines();
|
|
var directConnection = rfidLines.FirstOrDefault(line =>
|
|
(line.StartPoint == a && line.EndPoint == b) ||
|
|
(line.IsBidirectional && line.StartPoint == b && line.EndPoint == a));
|
|
|
|
if (directConnection != null)
|
|
{
|
|
return directConnection.Distance;
|
|
}
|
|
|
|
// 직접 연결되지 않은 경우 매우 큰 값 반환
|
|
return float.MaxValue;
|
|
}
|
|
|
|
private List<Point> GetNeighbors(Point point)
|
|
{
|
|
var neighbors = new List<Point>();
|
|
var rfidLines = GetRFIDLines();
|
|
|
|
// RFID 라인에서 이웃 노드 찾기
|
|
foreach (var line in rfidLines)
|
|
{
|
|
if (line.StartPoint == point)
|
|
{
|
|
neighbors.Add(line.EndPoint);
|
|
}
|
|
else if (line.EndPoint == point)
|
|
{
|
|
// 양방향 연결인 경우에만 시작점을 이웃으로 추가
|
|
if (line.IsBidirectional)
|
|
{
|
|
neighbors.Add(line.StartPoint);
|
|
}
|
|
}
|
|
}
|
|
|
|
return neighbors.Distinct().ToList();
|
|
}
|
|
public void SetPath(List<string> rfids)
|
|
{
|
|
if (rfids == null || rfids.Count == 0)
|
|
return;
|
|
|
|
var path = new List<Point>();
|
|
foreach (var rfid in rfids)
|
|
{
|
|
var point = GetRFIDPoints().FirstOrDefault(p => p.RFIDValue == rfid);
|
|
if (point != null)
|
|
{
|
|
path.Add(point.Location);
|
|
}
|
|
}
|
|
|
|
if (path.Count > 0)
|
|
{
|
|
DrawPath(path);
|
|
}
|
|
}
|
|
|
|
|
|
public string RFIDStartNo { get; set; } = string.Empty;
|
|
public int RFIDLastNumber = 0;
|
|
string filename = string.Empty;
|
|
private void MapControl_MouseClick(object sender, MouseEventArgs e)
|
|
{
|
|
var mapPoint = ScreenToMap(e.Location);
|
|
if (e.Button == MouseButtons.Right)
|
|
{
|
|
OnRightClick?.Invoke(this, e);
|
|
this.MouseMode = eMouseMode.Default;
|
|
}
|
|
else if (e.Button == MouseButtons.Left)
|
|
{
|
|
// 툴바 버튼 클릭 처리
|
|
var toolbar = toolbarRects.FirstOrDefault(t => t.Bounds.Contains(e.Location));
|
|
if (toolbar != null)
|
|
{
|
|
switch (toolbar.Title.ToLower())
|
|
{
|
|
case "+": ZoomIn(); break;
|
|
case "-": ZoomOut(); break;
|
|
case "1:1": ResetZoom(); break;
|
|
case "cut": MouseMode = (eMouseMode.rfidcut); break;
|
|
case "text": MouseMode = (eMouseMode.addtext); break;
|
|
case "line": MouseMode = (eMouseMode.addrfidline); break;
|
|
case "cline": MouseMode = (eMouseMode.addcustomline); break;
|
|
case "point": MouseMode = (eMouseMode.addrfidpoint); break;
|
|
case "path":
|
|
|
|
var input1 = AR.UTIL.InputBox("input start");
|
|
if (input1.Item1 == false) return;
|
|
var input2 = AR.UTIL.InputBox("input end");
|
|
if (input2.Item1 == false) return;
|
|
|
|
var startRFID = input1.Item2;
|
|
var endRFID = input2.Item2;
|
|
var startPoint = FindRFIDPoint(startRFID);
|
|
var endPoint = FindRFIDPoint(endRFID);
|
|
|
|
if (startPoint == null || endPoint == null)
|
|
{
|
|
MessageBox.Show("유효한 RFID 값을 입력해주세요.", "경로 계산", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
|
|
var path = CalculatePath(startPoint.Location, endPoint.Location);
|
|
if (path != null && path.Count > 0)
|
|
{
|
|
DrawPath(path);
|
|
|
|
// 경로 상의 모든 RFID 값을 가져옴
|
|
var rfidPath = new List<string>();
|
|
foreach (var point in path)
|
|
{
|
|
var rfid = GetRFIDPoints()
|
|
.FirstOrDefault(r => r.Location == point);
|
|
if (rfid != null)
|
|
{
|
|
rfidPath.Add(rfid.RFIDValue);
|
|
}
|
|
}
|
|
|
|
MessageBox.Show($"경로가 계산되었습니다.\nRFID 순서: {string.Join(" -> ", rfidPath)}",
|
|
"경로 계산", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
}
|
|
else
|
|
{
|
|
MessageBox.Show("경로를 찾을 수 없습니다.", "경로 계산", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
}
|
|
break;
|
|
case "pos":
|
|
var tag = AR.UTIL.InputBox("input rfid tag value");
|
|
if (tag.Item1 && tag.Item2 != "")
|
|
{
|
|
var targetRFID = AGVMoveToRFID(tag.Item2);
|
|
}
|
|
break;
|
|
case "save":
|
|
using (var od = new SaveFileDialog())
|
|
{
|
|
od.Filter = "path data|*.route";
|
|
od.FilterIndex = 0;
|
|
od.RestoreDirectory = true;
|
|
od.FileName = System.IO.Path.GetFileName(this.filename);
|
|
od.InitialDirectory = System.IO.Path.GetDirectoryName(this.filename);
|
|
if (od.ShowDialog() == DialogResult.OK)
|
|
{
|
|
filename = od.FileName;
|
|
this.SaveToFile(filename);
|
|
this.Invalidate();
|
|
}
|
|
}
|
|
break;
|
|
case "load":
|
|
using (var od = new OpenFileDialog())
|
|
{
|
|
od.Filter = "path data|*.route";
|
|
od.FilterIndex = 0;
|
|
|
|
|
|
if (string.IsNullOrEmpty(this.filename) == false)
|
|
{
|
|
od.FileName = System.IO.Path.GetFileName(this.filename);
|
|
od.InitialDirectory = System.IO.Path.GetDirectoryName(this.filename);
|
|
}
|
|
else
|
|
{
|
|
od.RestoreDirectory = true;
|
|
}
|
|
|
|
|
|
if (od.ShowDialog() == DialogResult.OK)
|
|
{
|
|
filename = od.FileName;
|
|
this.LoadFromFile(filename);
|
|
this.Invalidate();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// RFID 포인트 선택
|
|
var clickedRFID = rfidPoints.FirstOrDefault(r => GetDistance(mapPoint, r.Location) <= SNAP_DISTANCE);
|
|
switch (mousemode)
|
|
{
|
|
case eMouseMode.rfidcut:
|
|
DeleteNearbyRFIDLine(mapPoint);
|
|
break;
|
|
case eMouseMode.addrfidpoint:
|
|
if (string.IsNullOrEmpty(this.RFIDStartNo) == false)
|
|
{
|
|
|
|
AddRFIDPoint(mapPoint, RFIDStartNo);
|
|
|
|
// 숫자로 끝나는 RFID 값인 경우 자동 증가
|
|
if (Regex.IsMatch(RFIDStartNo, @"^[A-Za-z]+\d+$"))
|
|
{
|
|
// 마지막 숫자 부분 추출
|
|
Match match = Regex.Match(RFIDStartNo, @"\d+$");
|
|
if (match.Success)
|
|
{
|
|
int currentNumber = int.Parse(match.Value);
|
|
if (currentNumber > this.RFIDLastNumber)
|
|
{
|
|
RFIDLastNumber = currentNumber;
|
|
}
|
|
RFIDLastNumber++;
|
|
|
|
// 숫자 부분을 새로운 번호로 교체
|
|
RFIDStartNo = RFIDStartNo.Substring(0, match.Index) + RFIDLastNumber.ToString("D4");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case eMouseMode.addtext:
|
|
var text = new MapText
|
|
{
|
|
Location = mapPoint,
|
|
Text = "새 텍스트",
|
|
TextColor = Color.Black,
|
|
BackgroundColor = Color.Transparent,
|
|
Font = new Font("Arial", 12)
|
|
};
|
|
mapTexts.Add(text);
|
|
selectedText = text;
|
|
this.Invalidate();
|
|
break;
|
|
case eMouseMode.addcustomline:
|
|
if (previewStartPoint == null)
|
|
{
|
|
previewStartPoint = mapPoint;
|
|
}
|
|
else
|
|
{
|
|
var line = new CustomLine
|
|
{
|
|
StartPoint = previewStartPoint.Value,
|
|
EndPoint = mapPoint,
|
|
LineColor = Color.Red,
|
|
LineWidth = 2
|
|
};
|
|
customLines.Add(line);
|
|
selectedLine = line;
|
|
previewStartPoint = null;
|
|
this.Invalidate();
|
|
}
|
|
break;
|
|
case eMouseMode.addrfidline:
|
|
if (clickedRFID != null)
|
|
{
|
|
if (previewStartPoint == null)
|
|
{
|
|
previewStartPoint = clickedRFID.Location;
|
|
}
|
|
else
|
|
{
|
|
var startRFID = rfidPoints.FirstOrDefault(r => r.Location == previewStartPoint);
|
|
if (startRFID != null)
|
|
{
|
|
var line = new RFIDLine
|
|
{
|
|
StartPoint = previewStartPoint.Value,
|
|
EndPoint = clickedRFID.Location,
|
|
StartRFID = startRFID.RFIDValue,
|
|
EndRFID = clickedRFID.RFIDValue,
|
|
Distance = GetDistance(previewStartPoint.Value, clickedRFID.Location)
|
|
};
|
|
rfidLines.Add(line);
|
|
selectedRFIDLine = line;
|
|
|
|
// 선 근처의 RFID 포인트들을 찾아서 추가
|
|
AddNearbyRFIDPoints(line);
|
|
}
|
|
// 다음 라인을 위해 현재 클릭한 RFID를 시작점으로 설정
|
|
previewStartPoint = clickedRFID.Location;
|
|
}
|
|
this.Invalidate();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void AddNearbyRFIDPoints(RFIDLine line)
|
|
{
|
|
const float NEARBY_DISTANCE = 20.0f; // 근처로 간주하는 거리를 50에서 20으로 줄임
|
|
|
|
// 선 근처의 RFID 포인트들을 찾아서 거리에 따라 정렬
|
|
var nearbyPoints = new List<(RFIDPoint Point, float Distance, float ProjectionRatio)>();
|
|
|
|
foreach (var rfid in rfidPoints)
|
|
{
|
|
if (rfid.Location == line.StartPoint || rfid.Location == line.EndPoint)
|
|
continue;
|
|
|
|
// 선분과 RFID 포인트 사이의 최단 거리 계산
|
|
float distance = GetDistanceToLine(rfid.Location, line.StartPoint, line.EndPoint);
|
|
|
|
if (distance <= NEARBY_DISTANCE)
|
|
{
|
|
// 시작점으로부터의 투영 비율 계산 (0~1 사이 값)
|
|
float projectionRatio = GetProjectionRatio(rfid.Location, line.StartPoint, line.EndPoint);
|
|
if (projectionRatio >= 0 && projectionRatio <= 1) // 선분 위에 있는 점만 포함
|
|
{
|
|
nearbyPoints.Add((rfid, distance, projectionRatio));
|
|
}
|
|
}
|
|
}
|
|
|
|
// 시작점에서 끝점 방향으로 정렬
|
|
nearbyPoints.Sort((a, b) => a.ProjectionRatio.CompareTo(b.ProjectionRatio));
|
|
|
|
// 이전 RFID 값과 위치를 저장
|
|
string prevRFID = line.StartRFID;
|
|
Point prevPoint = line.StartPoint;
|
|
|
|
// 정렬된 포인트들을 순차적으로 연결
|
|
foreach (var item in nearbyPoints)
|
|
{
|
|
var rfidLine = new RFIDLine
|
|
{
|
|
StartPoint = prevPoint,
|
|
EndPoint = item.Point.Location,
|
|
StartRFID = prevRFID,
|
|
EndRFID = item.Point.RFIDValue,
|
|
Distance = GetDistance(prevPoint, item.Point.Location)
|
|
};
|
|
rfidLines.Add(rfidLine);
|
|
|
|
// 현재 포인트를 다음 선분의 시작점으로 설정
|
|
prevPoint = item.Point.Location;
|
|
prevRFID = item.Point.RFIDValue;
|
|
}
|
|
|
|
// 마지막 포인트와 원래 선의 끝점을 연결
|
|
var finalLine = new RFIDLine
|
|
{
|
|
StartPoint = prevPoint,
|
|
EndPoint = line.EndPoint,
|
|
StartRFID = prevRFID,
|
|
EndRFID = line.EndRFID,
|
|
Distance = GetDistance(prevPoint, line.EndPoint)
|
|
};
|
|
rfidLines.Add(finalLine);
|
|
|
|
// 원래 선은 제거
|
|
rfidLines.Remove(line);
|
|
}
|
|
|
|
private float GetProjectionRatio(Point point, Point lineStart, Point lineEnd)
|
|
{
|
|
float lineLength = GetDistance(lineStart, lineEnd);
|
|
if (lineLength == 0) return 0;
|
|
|
|
return ((point.X - lineStart.X) * (lineEnd.X - lineStart.X) +
|
|
(point.Y - lineStart.Y) * (lineEnd.Y - lineStart.Y)) / (lineLength * lineLength);
|
|
}
|
|
|
|
private float GetDistanceToLine(Point point, Point lineStart, Point lineEnd)
|
|
{
|
|
float lineLength = GetDistance(lineStart, lineEnd);
|
|
if (lineLength == 0) return GetDistance(point, lineStart);
|
|
|
|
float t = ((point.X - lineStart.X) * (lineEnd.X - lineStart.X) +
|
|
(point.Y - lineStart.Y) * (lineEnd.Y - lineStart.Y)) / (lineLength * lineLength);
|
|
|
|
t = Math.Max(0, Math.Min(1, t));
|
|
|
|
float projectionX = lineStart.X + t * (lineEnd.X - lineStart.X);
|
|
float projectionY = lineStart.Y + t * (lineEnd.Y - lineStart.Y);
|
|
|
|
return GetDistance(point, new Point((int)projectionX, (int)projectionY));
|
|
}
|
|
|
|
private void MapControl_MouseDoubleClick(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Left)
|
|
{
|
|
// 화면 좌표를 맵 좌표로 변환
|
|
var mapPoint = ScreenToMap(e.Location);
|
|
|
|
// 텍스트 객체 찾기
|
|
foreach (var text in mapTexts)
|
|
{
|
|
var textSize = CreateGraphics().MeasureString(text.Text, text.Font);
|
|
var rect = new RectangleF(
|
|
text.Location.X - SELECTION_DISTANCE,
|
|
text.Location.Y - SELECTION_DISTANCE,
|
|
textSize.Width + SELECTION_DISTANCE * 2,
|
|
textSize.Height + SELECTION_DISTANCE * 2
|
|
);
|
|
|
|
if (rect.Contains(mapPoint))
|
|
{
|
|
var rlt = AR.UTIL.InputBox("input", text.Text);
|
|
if (rlt.Item1 == true)
|
|
{
|
|
text.Text = rlt.Item2;// dialog.InputText;
|
|
this.Invalidate();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public event EventHandler<MouseEventArgs> OnRightClick;
|
|
|
|
public void SetRFIDPoints(List<RFIDPoint> points)
|
|
{
|
|
rfidPoints = points;
|
|
this.Invalidate();
|
|
}
|
|
|
|
public void SetMagnetLines(List<MagnetLine> lines)
|
|
{
|
|
magnetLines = lines;
|
|
this.Invalidate();
|
|
}
|
|
|
|
public void SetAGV(AGV vehicle)
|
|
{
|
|
agv = vehicle;
|
|
this.Invalidate();
|
|
}
|
|
|
|
public void SetMapTexts(List<MapText> texts)
|
|
{
|
|
mapTexts = texts;
|
|
if (mapTexts == null) mapTexts = new List<MapText>();
|
|
this.Invalidate();
|
|
}
|
|
|
|
public void SetCustomLines(List<CustomLine> lines)
|
|
{
|
|
customLines = lines;
|
|
if (customLines == null) customLines = new List<CustomLine>();
|
|
this.Invalidate();
|
|
}
|
|
|
|
public void SetIsAddingText(bool value)
|
|
{
|
|
isAddingText = value;
|
|
isDrawingCustomLine = false;
|
|
this.Cursor = value ? Cursors.IBeam : Cursors.Default;
|
|
}
|
|
|
|
public void SetIsDrawingCustomLine(bool value)
|
|
{
|
|
isDrawingCustomLine = value;
|
|
isAddingText = false;
|
|
this.Cursor = value ? Cursors.Cross : Cursors.Default;
|
|
}
|
|
|
|
public void SetIsDrawingRFIDLine(bool value)
|
|
{
|
|
isDrawingRFIDLine = value;
|
|
isDrawingCustomLine = false;
|
|
isAddingText = false;
|
|
this.Cursor = value ? Cursors.Cross : Cursors.Default;
|
|
}
|
|
|
|
public void SetIsDeletingRFIDLine(bool value)
|
|
{
|
|
isDeletingRFIDLine = value;
|
|
isDrawingCustomLine = false;
|
|
isAddingText = false;
|
|
isDrawingRFIDLine = false;
|
|
this.Cursor = value ? GetScissorsCursor() : Cursors.Default;
|
|
}
|
|
|
|
public void SetIsDrawingLine(bool value)
|
|
{
|
|
isDrawingLine = value;
|
|
isDrawingCustomLine = false;
|
|
isAddingText = false;
|
|
isDrawingRFIDLine = false;
|
|
this.Cursor = value ? Cursors.Cross : Cursors.Default;
|
|
}
|
|
|
|
public enum eMouseMode : byte
|
|
{
|
|
Default = 0,
|
|
pan,
|
|
rfidcut,
|
|
addtext,
|
|
addcustomline,
|
|
addrfidpoint,
|
|
addrfidline,
|
|
}
|
|
|
|
private eMouseMode mousemode = eMouseMode.Default;
|
|
public eMouseMode MouseMode
|
|
{
|
|
get { return mousemode; }
|
|
set
|
|
{
|
|
if (this.mousemode == value) mousemode = eMouseMode.Default;
|
|
else this.mousemode = value;
|
|
switch (this.mousemode)
|
|
{
|
|
case eMouseMode.pan: this.Cursor = Cursors.Hand; break;
|
|
case eMouseMode.addrfidline: this.Cursor = Cursors.Default; break;
|
|
case eMouseMode.addrfidpoint: this.Cursor = Cursors.Default; break;
|
|
case eMouseMode.addtext: this.Cursor = Cursors.Default; break;
|
|
case eMouseMode.addcustomline: this.Cursor = Cursors.Default; break;
|
|
default: this.Cursor = Cursors.Default; break;
|
|
}
|
|
previewStartPoint = null;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
|
|
public void SetIsAddingMagnet(bool value)
|
|
{
|
|
|
|
}
|
|
|
|
public void SetIsAddingPoint(bool value)
|
|
{
|
|
isDrawingCustomLine = false;
|
|
isDrawingLine = false;
|
|
isDrawingMagnetLine = false;
|
|
isDrawingRFIDLine = false;
|
|
isAddingPoint = value;
|
|
this.Cursor = value ? Cursors.Cross : Cursors.Default;
|
|
}
|
|
|
|
private Cursor GetScissorsCursor()
|
|
{
|
|
// 가위 커서 아이콘 생성
|
|
using (var bitmap = new Bitmap(32, 32))
|
|
using (var g = Graphics.FromImage(bitmap))
|
|
{
|
|
g.Clear(Color.Transparent);
|
|
|
|
// 가위 모양 그리기
|
|
using (var pen = new Pen(Color.Black, 2))
|
|
{
|
|
// 가위 손잡이
|
|
g.DrawEllipse(pen, 12, 20, 8, 8);
|
|
g.DrawLine(pen, 16, 20, 16, 16);
|
|
|
|
// 가위 날
|
|
g.DrawLine(pen, 16, 16, 8, 8);
|
|
g.DrawLine(pen, 16, 16, 24, 8);
|
|
}
|
|
|
|
return new Cursor(bitmap.GetHicon());
|
|
}
|
|
}
|
|
|
|
public void DrawPath(List<Point> path)
|
|
{
|
|
currentPath = path;
|
|
this.Invalidate();
|
|
}
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
{
|
|
//base.OnPaint(e);
|
|
|
|
|
|
|
|
e.Graphics.TranslateTransform(offset.X, offset.Y);
|
|
e.Graphics.ScaleTransform(zoom, zoom);
|
|
|
|
DrawRFIDLines(e.Graphics);
|
|
DrawMap(e.Graphics);
|
|
//DrawCustomLines(e.Graphics);
|
|
DrawMapTexts(e.Graphics);
|
|
|
|
DrawPath(e.Graphics);
|
|
DrawAGV(e.Graphics);
|
|
|
|
// 선택된 개체 강조 표시
|
|
if (selectedRFID != null)
|
|
{
|
|
using (Pen pen = new Pen(Color.Magenta, 2))
|
|
{
|
|
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
|
|
e.Graphics.DrawEllipse(pen,
|
|
selectedRFID.Location.X - 10,
|
|
selectedRFID.Location.Y - 10,
|
|
20, 20);
|
|
}
|
|
}
|
|
|
|
if (selectedMagnetLine != null)
|
|
{
|
|
using (Pen pen = new Pen(Color.Magenta, 2))
|
|
{
|
|
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
|
|
e.Graphics.DrawLine(pen, selectedMagnetLine.StartPoint, selectedMagnetLine.EndPoint);
|
|
foreach (var branch in selectedMagnetLine.BranchPoints)
|
|
{
|
|
e.Graphics.DrawEllipse(pen, branch.X - 5, branch.Y - 5, 10, 10);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (selectedLine != null)
|
|
{
|
|
using (Pen pen = new Pen(Color.Magenta, 2))
|
|
{
|
|
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
|
|
e.Graphics.DrawLine(pen, selectedLine.StartPoint, selectedLine.EndPoint);
|
|
}
|
|
}
|
|
|
|
// 미리보기 라인 그리기
|
|
if (previewStartPoint.HasValue)
|
|
{
|
|
using (Pen previewPen = new Pen(Color.FromArgb(180, Color.Yellow), LINE_WIDTH))
|
|
{
|
|
previewPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
|
|
e.Graphics.DrawLine(previewPen, previewStartPoint.Value, currentMousePosition);
|
|
}
|
|
}
|
|
|
|
// 그래픽스 변환 초기화
|
|
e.Graphics.ResetTransform();
|
|
|
|
// 툴바 버튼 그리기
|
|
foreach (var item in this.toolbarRects)
|
|
DrawToolbarButton(e.Graphics, item.Bounds, item.Title, item.isHovering);
|
|
}
|
|
|
|
|
|
private void DrawMap(Graphics g)
|
|
{
|
|
//// 마그넷 라인 그리기
|
|
//foreach (var line in magnetLines)
|
|
//{
|
|
// using (Pen linePen = new Pen(Color.FromArgb(180, Color.Yellow), LINE_WIDTH))
|
|
// {
|
|
// g.DrawLine(linePen, line.StartPoint, line.EndPoint);
|
|
// }
|
|
|
|
// // 분기점 그리기
|
|
// foreach (var branch in line.BranchPoints)
|
|
// {
|
|
// g.FillEllipse(Brushes.Red, branch.X - 3, branch.Y - 3, 6, 6);
|
|
// var direction = line.BranchDirections[branch];
|
|
// g.DrawString(direction.ToString(), Font, Brushes.Black, branch.X + 5, branch.Y - 5);
|
|
// }
|
|
//}
|
|
|
|
// RFID 포인트 그리기
|
|
foreach (var rfid in rfidPoints)
|
|
{
|
|
var MarkerSize = 5;
|
|
g.FillEllipse(Brushes.Green, rfid.Location.X - (MarkerSize/2f), rfid.Location.Y -(MarkerSize/2f), MarkerSize, MarkerSize);
|
|
//g.DrawString(rfid.RFIDValue, Font, Brushes.WhiteSmoke, rfid.Location.X + 5, rfid.Location.Y - 5);
|
|
}
|
|
|
|
// RFID 포인트 그리기
|
|
foreach (var rfid in rfidPoints)
|
|
{
|
|
//g.FillEllipse(Brushes.Green, rfid.Location.X - 3, rfid.Location.Y - 3, 6, 6);
|
|
var fsize = g.MeasureString(rfid.RFIDValue, Font);
|
|
g.DrawString(rfid.RFIDValue, Font, Brushes.WhiteSmoke,
|
|
(rfid.Location.X - fsize.Width / 2f),
|
|
(rfid.Location.Y + 2));
|
|
}
|
|
|
|
//var rfidpts = rfidPoints.Select(t => t.Location).ToArray();
|
|
//g.DrawLines(new Pen(Color.FromArgb(100, Color.Magenta), 10), rfidpts);
|
|
|
|
}
|
|
|
|
private void DrawAGV(Graphics g)
|
|
{
|
|
var agvsize = 30;
|
|
var halfsize = (int)(agvsize / 2);
|
|
var rect = new Rectangle(agv.CurrentPosition.X - halfsize,
|
|
agv.CurrentPosition.Y - halfsize,
|
|
agvsize, agvsize);
|
|
|
|
var recti = new Rectangle(rect.X + 5, rect.Y + 5, rect.Width - 10, rect.Height - 10);
|
|
|
|
using (var sb = new SolidBrush(Color.FromArgb(150, Color.Gold)))
|
|
g.FillEllipse(sb, rect);
|
|
|
|
using (var pen = new Pen(Color.Black))
|
|
g.DrawEllipse(pen, rect);
|
|
|
|
using (var sb = new SolidBrush(Color.FromArgb(150, Color.White)))
|
|
g.FillEllipse(sb, recti);
|
|
|
|
using (var pen = new Pen(Color.Black))
|
|
g.DrawEllipse(pen, recti);
|
|
}
|
|
|
|
private void DrawCustomLines(Graphics g)
|
|
{
|
|
if (customLines == null) return;
|
|
foreach (var line in customLines)
|
|
{
|
|
using (Pen linePen = new Pen(line.LineColor, line.LineWidth))
|
|
{
|
|
g.DrawLine(linePen, line.StartPoint, line.EndPoint);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawMapTexts(Graphics g)
|
|
{
|
|
if (mapTexts == null) return;
|
|
foreach (var text in mapTexts)
|
|
{
|
|
var textSize = g.MeasureString(text.Text, text.Font);
|
|
var rect = new RectangleF(
|
|
text.Location.X,
|
|
text.Location.Y,
|
|
textSize.Width,
|
|
textSize.Height
|
|
);
|
|
|
|
if (text.BackgroundColor != Color.Transparent)
|
|
{
|
|
using (var brush = new SolidBrush(text.BackgroundColor))
|
|
{
|
|
g.FillRectangle(brush, rect);
|
|
}
|
|
}
|
|
|
|
using (var brush = new SolidBrush(text.TextColor))
|
|
{
|
|
g.DrawString(text.Text, text.Font, brush, text.Location);
|
|
}
|
|
|
|
if (text == selectedText)
|
|
{
|
|
using (Pen pen = new Pen(Color.Blue, 1))
|
|
{
|
|
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
|
|
g.DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawRFIDLines(Graphics g)
|
|
{
|
|
foreach (var line in rfidLines)
|
|
{
|
|
using (Pen linePen = new Pen(Color.FromArgb(50, Color.Wheat), 2))
|
|
{
|
|
if (!line.IsBidirectional)
|
|
{
|
|
// 단방향 화살표 그리기
|
|
var arrowSize = 10;
|
|
var angle = Math.Atan2(line.EndPoint.Y - line.StartPoint.Y, line.EndPoint.X - line.StartPoint.X);
|
|
var arrowPoint = new PointF(
|
|
line.EndPoint.X - (float)(arrowSize * Math.Cos(angle)),
|
|
line.EndPoint.Y - (float)(arrowSize * Math.Sin(angle))
|
|
);
|
|
g.DrawLine(linePen, line.StartPoint, arrowPoint);
|
|
|
|
// 화살표 머리 그리기
|
|
var arrowAngle = Math.PI / 6;
|
|
var arrowLength = 15;
|
|
var arrow1 = new PointF(
|
|
arrowPoint.X - (float)(arrowLength * Math.Cos(angle - arrowAngle)),
|
|
arrowPoint.Y - (float)(arrowLength * Math.Sin(angle - arrowAngle))
|
|
);
|
|
var arrow2 = new PointF(
|
|
arrowPoint.X - (float)(arrowLength * Math.Cos(angle + arrowAngle)),
|
|
arrowPoint.Y - (float)(arrowLength * Math.Sin(angle + arrowAngle))
|
|
);
|
|
g.DrawLine(linePen, arrowPoint, arrow1);
|
|
g.DrawLine(linePen, arrowPoint, arrow2);
|
|
}
|
|
else
|
|
{
|
|
g.DrawLine(linePen, line.StartPoint, line.EndPoint);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 미리보기 라인 그리기
|
|
if (previewStartPoint.HasValue && isDrawingRFIDLine)
|
|
{
|
|
using (Pen previewPen = new Pen(Color.FromArgb(180, Color.Green), 2))
|
|
{
|
|
previewPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
|
|
var currentMapPosition = ScreenToMap(currentMousePosition);
|
|
g.DrawLine(previewPen, previewStartPoint.Value, currentMapPosition);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawPath(Graphics g)
|
|
{
|
|
if (currentPath == null || currentPath.Count < 2)
|
|
return;
|
|
|
|
Color pathColor = Color.FromArgb(100, Color.Lime);
|
|
int pathWidth = 10;
|
|
|
|
using (Pen pathPen = new Pen(pathColor, pathWidth))
|
|
{
|
|
pathPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
|
|
for (int i = 0; i < currentPath.Count - 1; i++)
|
|
{
|
|
g.DrawLine(pathPen, currentPath[i], currentPath[i + 1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
public List<RFIDPoint> GetRFIDPoints()
|
|
{
|
|
return rfidPoints;
|
|
}
|
|
|
|
public List<MagnetLine> GetMagnetLines()
|
|
{
|
|
return magnetLines;
|
|
}
|
|
|
|
public List<MapText> GetMapTexts()
|
|
{
|
|
return mapTexts;
|
|
}
|
|
|
|
public List<CustomLine> GetCustomLines()
|
|
{
|
|
return customLines;
|
|
}
|
|
|
|
public List<RFIDLine> GetRFIDLines()
|
|
{
|
|
return rfidLines;
|
|
}
|
|
|
|
public void SetRFIDLines(List<RFIDLine> lines)
|
|
{
|
|
rfidLines = lines;
|
|
this.Invalidate();
|
|
}
|
|
|
|
public void AddRFIDLine(Point startPoint, Point endPoint, string startRFID, string endRFID, bool isBidirectional = true)
|
|
{
|
|
// 시작점과 끝점 사이의 모든 RFID 포인트 찾기
|
|
var allPoints = new List<(RFIDPoint Point, float Distance)>();
|
|
var lineVector = new Point(endPoint.X - startPoint.X, endPoint.Y - startPoint.Y);
|
|
var lineLength = (float)Math.Sqrt(lineVector.X * lineVector.X + lineVector.Y * lineVector.Y);
|
|
|
|
foreach (var rfid in rfidPoints)
|
|
{
|
|
if (rfid.Location == startPoint || rfid.Location == endPoint)
|
|
continue;
|
|
|
|
// RFID 포인트가 선 위에 있는지 확인
|
|
var pointVector = new Point(rfid.Location.X - startPoint.X, rfid.Location.Y - startPoint.Y);
|
|
var dotProduct = pointVector.X * lineVector.X + pointVector.Y * lineVector.Y;
|
|
var projectionLength = dotProduct / (lineLength * lineLength);
|
|
|
|
if (projectionLength >= 0 && projectionLength <= 1)
|
|
{
|
|
// 선과 RFID 포인트 사이의 거리 계산
|
|
var distance = GetDistanceToLine(rfid.Location, startPoint, endPoint);
|
|
if (distance <= SNAP_DISTANCE)
|
|
{
|
|
allPoints.Add((rfid, projectionLength));
|
|
}
|
|
}
|
|
}
|
|
|
|
// 시작점에서 끝점 방향으로 정렬
|
|
allPoints.Sort((a, b) => a.Distance.CompareTo(b.Distance));
|
|
|
|
// 모든 연결 정보를 포함하는 RFID 라인 생성
|
|
var line = new RFIDLine
|
|
{
|
|
StartPoint = startPoint,
|
|
EndPoint = endPoint,
|
|
StartRFID = startRFID,
|
|
EndRFID = endRFID,
|
|
IsBidirectional = isBidirectional,
|
|
Distance = GetDistance(startPoint, endPoint)
|
|
};
|
|
|
|
// 연결된 RFID 목록 생성
|
|
line.ConnectedRFIDs.Add(startRFID);
|
|
foreach (var point in allPoints)
|
|
{
|
|
line.ConnectedRFIDs.Add(point.Point.RFIDValue);
|
|
}
|
|
line.ConnectedRFIDs.Add(endRFID);
|
|
|
|
// 다음/이전 RFID 정보 설정
|
|
for (int i = 0; i < line.ConnectedRFIDs.Count - 1; i++)
|
|
{
|
|
var current = line.ConnectedRFIDs[i];
|
|
var next = line.ConnectedRFIDs[i + 1];
|
|
line.NextRFID[current] = next;
|
|
line.PrevRFID[next] = current;
|
|
|
|
if (isBidirectional)
|
|
{
|
|
line.NextRFID[next] = current;
|
|
line.PrevRFID[current] = next;
|
|
}
|
|
}
|
|
|
|
rfidLines.Add(line);
|
|
this.Invalidate();
|
|
}
|
|
|
|
public void LoadMapData(MapData mapData)
|
|
{
|
|
rfidPoints = mapData.RFIDPoints ?? new List<RFIDPoint>();
|
|
magnetLines = mapData.MagnetLines ?? new List<MagnetLine>();
|
|
mapTexts = mapData.MapTexts ?? new List<MapText>();
|
|
customLines = mapData.CustomLines ?? new List<CustomLine>();
|
|
rfidLines = mapData.RFIDLines ?? new List<RFIDLine>();
|
|
this.Invalidate();
|
|
}
|
|
|
|
public void ClearMap()
|
|
{
|
|
rfidPoints.Clear();
|
|
magnetLines.Clear();
|
|
mapTexts.Clear();
|
|
customLines.Clear();
|
|
rfidLines.Clear();
|
|
|
|
// 선택 상태도 초기화
|
|
selectedText = null;
|
|
selectedLine = null;
|
|
selectedRFID = null;
|
|
selectedMagnetLine = null;
|
|
selectedRFIDLine = null;
|
|
draggingPoint = null;
|
|
|
|
// 미리보기 상태도 초기화
|
|
previewStartPoint = null;
|
|
|
|
// 화면 갱신
|
|
this.Invalidate();
|
|
}
|
|
|
|
public void AddRFIDPoint(Point mapLocation, string rfidValue)
|
|
{
|
|
// 이미 맵 좌표로 변환된 위치를 사용
|
|
var rfidPoint = new RFIDPoint
|
|
{
|
|
Location = mapLocation,
|
|
RFIDValue = rfidValue
|
|
};
|
|
|
|
rfidPoints.Add(rfidPoint);
|
|
this.Invalidate();
|
|
}
|
|
|
|
public void AddMagnetLine(Point startPoint, Point endPoint, Point? branchPoint = null, BranchDirection? branchDirection = null)
|
|
{
|
|
// 이미 맵 좌표로 변환된 위치를 받아서 사용
|
|
var line = new MagnetLine
|
|
{
|
|
StartPoint = startPoint,
|
|
EndPoint = endPoint
|
|
};
|
|
|
|
if (branchPoint.HasValue && branchDirection.HasValue)
|
|
{
|
|
line.BranchPoints.Add(branchPoint.Value);
|
|
line.BranchDirections[branchPoint.Value] = branchDirection.Value;
|
|
}
|
|
|
|
magnetLines.Add(line);
|
|
this.Invalidate();
|
|
}
|
|
|
|
public void SaveToFile(string filename)
|
|
{
|
|
var lines = new List<string>();
|
|
|
|
// RFID 포인트 저장
|
|
lines.Add("[RFID_POINTS]");
|
|
foreach (var point in rfidPoints)
|
|
{
|
|
lines.Add($"{point.Location.X},{point.Location.Y},{point.RFIDValue}");
|
|
}
|
|
|
|
// 자석선 저장
|
|
lines.Add("[MAGNET_LINES]");
|
|
foreach (var line in magnetLines)
|
|
{
|
|
var branchInfo = "";
|
|
foreach (var branch in line.BranchPoints)
|
|
{
|
|
if (line.BranchDirections.ContainsKey(branch))
|
|
{
|
|
branchInfo += $"|{branch.X},{branch.Y},{(int)line.BranchDirections[branch]}";
|
|
}
|
|
}
|
|
lines.Add($"{line.StartPoint.X},{line.StartPoint.Y},{line.EndPoint.X},{line.EndPoint.Y}{branchInfo}");
|
|
}
|
|
|
|
// 텍스트 저장
|
|
lines.Add("[MAP_TEXTS]");
|
|
foreach (var text in mapTexts)
|
|
{
|
|
lines.Add($"{text.Location.X},{text.Location.Y},{text.TextColor.ToArgb()},{text.BackgroundColor.ToArgb()},{text.Font.Name},{text.Font.Size},{text.Text}");
|
|
}
|
|
|
|
// 커스텀 라인 저장
|
|
lines.Add("[CUSTOM_LINES]");
|
|
foreach (var line in customLines)
|
|
{
|
|
lines.Add($"{line.StartPoint.X},{line.StartPoint.Y},{line.EndPoint.X},{line.EndPoint.Y},{line.LineColor.ToArgb()},{line.LineWidth}");
|
|
}
|
|
|
|
// RFID 라인 저장
|
|
lines.Add("[RFID_LINES]");
|
|
foreach (var line in rfidLines)
|
|
{
|
|
lines.Add($"{line.StartPoint.X},{line.StartPoint.Y},{line.EndPoint.X},{line.EndPoint.Y}," +
|
|
$"{line.StartRFID},{line.EndRFID},{line.IsBidirectional},{line.Distance}");
|
|
}
|
|
|
|
File.WriteAllLines(filename, lines);
|
|
}
|
|
|
|
public void LoadFromFile(string filename)
|
|
{
|
|
ClearMap();
|
|
|
|
var lines = File.ReadAllLines(filename);
|
|
var section = "";
|
|
|
|
foreach (var line in lines)
|
|
{
|
|
if (line.StartsWith("[") && line.EndsWith("]"))
|
|
{
|
|
section = line;
|
|
continue;
|
|
}
|
|
|
|
switch (section)
|
|
{
|
|
case "[RFID_POINTS]":
|
|
var rfidParts = line.Split(',');
|
|
if (rfidParts.Length >= 3)
|
|
{
|
|
AddRFIDPoint(
|
|
new Point(int.Parse(rfidParts[0]), int.Parse(rfidParts[1])),
|
|
rfidParts[2]
|
|
);
|
|
}
|
|
break;
|
|
|
|
case "[MAGNET_LINES]":
|
|
var lineParts = line.Split('|');
|
|
var mainParts = lineParts[0].Split(',');
|
|
if (mainParts.Length >= 4)
|
|
{
|
|
var magnetLine = new MagnetLine
|
|
{
|
|
StartPoint = new Point(int.Parse(mainParts[0]), int.Parse(mainParts[1])),
|
|
EndPoint = new Point(int.Parse(mainParts[2]), int.Parse(mainParts[3]))
|
|
};
|
|
|
|
// 분기점 정보 처리
|
|
for (int i = 1; i < lineParts.Length; i++)
|
|
{
|
|
var branchParts = lineParts[i].Split(',');
|
|
if (branchParts.Length >= 3)
|
|
{
|
|
var branchPoint = new Point(
|
|
int.Parse(branchParts[0]),
|
|
int.Parse(branchParts[1])
|
|
);
|
|
magnetLine.BranchPoints.Add(branchPoint);
|
|
magnetLine.BranchDirections[branchPoint] = (BranchDirection)int.Parse(branchParts[2]);
|
|
}
|
|
}
|
|
magnetLines.Add(magnetLine);
|
|
}
|
|
break;
|
|
|
|
case "[MAP_TEXTS]":
|
|
var textParts = line.Split(',');
|
|
if (textParts.Length >= 7)
|
|
{
|
|
var text = new MapText
|
|
{
|
|
Location = new Point(int.Parse(textParts[0]), int.Parse(textParts[1])),
|
|
TextColor = Color.FromArgb(int.Parse(textParts[2])),
|
|
BackgroundColor = Color.FromArgb(int.Parse(textParts[3])),
|
|
Font = new Font(textParts[4], float.Parse(textParts[5])),
|
|
Text = string.Join(",", textParts.Skip(6)) // 텍스트에 쉼표가 포함될 수 있으므로
|
|
};
|
|
mapTexts.Add(text);
|
|
}
|
|
break;
|
|
|
|
case "[CUSTOM_LINES]":
|
|
var customLineParts = line.Split(',');
|
|
if (customLineParts.Length >= 6)
|
|
{
|
|
var customLine = new CustomLine
|
|
{
|
|
StartPoint = new Point(int.Parse(customLineParts[0]), int.Parse(customLineParts[1])),
|
|
EndPoint = new Point(int.Parse(customLineParts[2]), int.Parse(customLineParts[3])),
|
|
LineColor = Color.FromArgb(int.Parse(customLineParts[4])),
|
|
LineWidth = int.Parse(customLineParts[5])
|
|
};
|
|
customLines.Add(customLine);
|
|
}
|
|
break;
|
|
|
|
case "[RFID_LINES]":
|
|
var rfidLineParts = line.Split(',');
|
|
if (rfidLineParts.Length >= 8)
|
|
{
|
|
AddRFIDLine(
|
|
new Point(int.Parse(rfidLineParts[0]), int.Parse(rfidLineParts[1])),
|
|
new Point(int.Parse(rfidLineParts[2]), int.Parse(rfidLineParts[3])),
|
|
rfidLineParts[4],
|
|
rfidLineParts[5],
|
|
bool.Parse(rfidLineParts[6])
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
this.Invalidate();
|
|
}
|
|
|
|
private void DeleteNearbyRFIDLine(Point clickPoint)
|
|
{
|
|
const float DELETE_DISTANCE = 10.0f; // 클릭 지점으로부터의 허용 거리
|
|
RFIDLine lineToDelete = null;
|
|
float minDistance = float.MaxValue;
|
|
|
|
foreach (var line in rfidLines)
|
|
{
|
|
float distance = GetDistanceToLine(clickPoint, line.StartPoint, line.EndPoint);
|
|
if (distance < DELETE_DISTANCE && distance < minDistance)
|
|
{
|
|
minDistance = distance;
|
|
lineToDelete = line;
|
|
}
|
|
}
|
|
|
|
if (lineToDelete != null)
|
|
{
|
|
rfidLines.Remove(lineToDelete);
|
|
this.Invalidate();
|
|
}
|
|
}
|
|
|
|
private void ZoomIn()
|
|
{
|
|
zoom *= 1.2f;
|
|
zoom = Math.Min(10.0f, zoom);
|
|
this.Invalidate();
|
|
}
|
|
|
|
private void ZoomOut()
|
|
{
|
|
zoom /= 1.2f;
|
|
zoom = Math.Max(0.1f, zoom);
|
|
this.Invalidate();
|
|
}
|
|
|
|
private void ResetZoom()
|
|
{
|
|
zoom = 1.0f;
|
|
offset = PointF.Empty;
|
|
this.Invalidate();
|
|
}
|
|
|
|
private void DrawToolbarButton(Graphics g, Rectangle rect, string text, bool isHovering)
|
|
{
|
|
var color1 = isHovering ? Color.LightSkyBlue : Color.White;
|
|
var color2 = isHovering ? Color.DeepSkyBlue : Color.WhiteSmoke;
|
|
using (var brush = new LinearGradientBrush(rect, color1, color2, LinearGradientMode.Vertical))
|
|
using (var pen = new Pen(Color.Gray))
|
|
using (var font = new Font("Tahoma", 9, FontStyle.Bold))
|
|
using (var format = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center })
|
|
{
|
|
g.FillRectangle(Brushes.LightGray, rect.X + 2, rect.Y + 2, rect.Width, rect.Height);
|
|
g.FillRectangle(brush, rect);
|
|
g.DrawRectangle(pen, rect);
|
|
g.DrawString(text, font, Brushes.Black, rect, format);
|
|
}
|
|
}
|
|
}
|
|
}
|