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>
210 lines
6.6 KiB
C#
210 lines
6.6 KiB
C#
using System;
|
|
using System.Drawing;
|
|
|
|
namespace AGVMapEditor.Models
|
|
{
|
|
/// <summary>
|
|
/// 맵 이미지 정보를 관리하는 클래스
|
|
/// 디자인 요소용 이미지/비트맵 요소
|
|
/// </summary>
|
|
public class MapImage
|
|
{
|
|
/// <summary>
|
|
/// 이미지 고유 ID
|
|
/// </summary>
|
|
public string ImageId { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// 이미지 파일 경로
|
|
/// </summary>
|
|
public string ImagePath { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// 맵 상의 위치 좌표 (좌상단 기준)
|
|
/// </summary>
|
|
public Point Position { get; set; } = Point.Empty;
|
|
|
|
/// <summary>
|
|
/// 이미지 크기 (원본 크기 기준 배율)
|
|
/// </summary>
|
|
public SizeF Scale { get; set; } = new SizeF(1.0f, 1.0f);
|
|
|
|
/// <summary>
|
|
/// 이미지 투명도 (0.0 ~ 1.0)
|
|
/// </summary>
|
|
public float Opacity { get; set; } = 1.0f;
|
|
|
|
/// <summary>
|
|
/// 이미지 회전 각도 (도 단위)
|
|
/// </summary>
|
|
public float Rotation { get; set; } = 0.0f;
|
|
|
|
/// <summary>
|
|
/// 이미지 설명
|
|
/// </summary>
|
|
public string Description { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// 이미지 생성 일자
|
|
/// </summary>
|
|
public DateTime CreatedDate { get; set; } = DateTime.Now;
|
|
|
|
/// <summary>
|
|
/// 이미지 수정 일자
|
|
/// </summary>
|
|
public DateTime ModifiedDate { get; set; } = DateTime.Now;
|
|
|
|
/// <summary>
|
|
/// 이미지 활성화 여부
|
|
/// </summary>
|
|
public bool IsActive { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// 로딩된 이미지 (런타임에서만 사용, JSON 직렬화 제외)
|
|
/// </summary>
|
|
[Newtonsoft.Json.JsonIgnore]
|
|
public Image LoadedImage { get; set; }
|
|
|
|
/// <summary>
|
|
/// 기본 생성자
|
|
/// </summary>
|
|
public MapImage()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// 매개변수 생성자
|
|
/// </summary>
|
|
/// <param name="imageId">이미지 ID</param>
|
|
/// <param name="imagePath">이미지 파일 경로</param>
|
|
/// <param name="position">위치</param>
|
|
public MapImage(string imageId, string imagePath, Point position)
|
|
{
|
|
ImageId = imageId;
|
|
ImagePath = imagePath;
|
|
Position = position;
|
|
CreatedDate = DateTime.Now;
|
|
ModifiedDate = DateTime.Now;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 이미지 로드 (256x256 이상일 경우 자동 리사이즈)
|
|
/// </summary>
|
|
/// <returns>로드 성공 여부</returns>
|
|
public bool LoadImage()
|
|
{
|
|
try
|
|
{
|
|
if (!string.IsNullOrEmpty(ImagePath) && System.IO.File.Exists(ImagePath))
|
|
{
|
|
LoadedImage?.Dispose();
|
|
var originalImage = Image.FromFile(ImagePath);
|
|
|
|
// 이미지 크기 체크 및 리사이즈
|
|
if (originalImage.Width > 256 || originalImage.Height > 256)
|
|
{
|
|
LoadedImage = ResizeImage(originalImage, 256, 256);
|
|
originalImage.Dispose();
|
|
}
|
|
else
|
|
{
|
|
LoadedImage = originalImage;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// 이미지 로드 실패
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 이미지 리사이즈 (비율 유지)
|
|
/// </summary>
|
|
/// <param name="image">원본 이미지</param>
|
|
/// <param name="maxWidth">최대 너비</param>
|
|
/// <param name="maxHeight">최대 높이</param>
|
|
/// <returns>리사이즈된 이미지</returns>
|
|
private Image ResizeImage(Image image, int maxWidth, int maxHeight)
|
|
{
|
|
// 비율 계산
|
|
double ratioX = (double)maxWidth / image.Width;
|
|
double ratioY = (double)maxHeight / image.Height;
|
|
double ratio = Math.Min(ratioX, ratioY);
|
|
|
|
// 새로운 크기 계산
|
|
int newWidth = (int)(image.Width * ratio);
|
|
int newHeight = (int)(image.Height * ratio);
|
|
|
|
// 리사이즈된 이미지 생성
|
|
var resizedImage = new Bitmap(newWidth, newHeight);
|
|
using (var graphics = Graphics.FromImage(resizedImage))
|
|
{
|
|
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
|
|
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
|
|
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
|
|
graphics.DrawImage(image, 0, 0, newWidth, newHeight);
|
|
}
|
|
|
|
return resizedImage;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 실제 표시될 크기 계산
|
|
/// </summary>
|
|
/// <returns>실제 크기</returns>
|
|
public Size GetDisplaySize()
|
|
{
|
|
if (LoadedImage == null) return Size.Empty;
|
|
|
|
return new Size(
|
|
(int)(LoadedImage.Width * Scale.Width),
|
|
(int)(LoadedImage.Height * Scale.Height)
|
|
);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 문자열 표현
|
|
/// </summary>
|
|
public override string ToString()
|
|
{
|
|
return $"{ImageId}: {System.IO.Path.GetFileName(ImagePath)} at ({Position.X}, {Position.Y})";
|
|
}
|
|
|
|
/// <summary>
|
|
/// 이미지 복사
|
|
/// </summary>
|
|
/// <returns>복사된 이미지</returns>
|
|
public MapImage Clone()
|
|
{
|
|
var clone = new MapImage
|
|
{
|
|
ImageId = ImageId,
|
|
ImagePath = ImagePath,
|
|
Position = Position,
|
|
Scale = Scale,
|
|
Opacity = Opacity,
|
|
Rotation = Rotation,
|
|
Description = Description,
|
|
CreatedDate = CreatedDate,
|
|
ModifiedDate = ModifiedDate,
|
|
IsActive = IsActive
|
|
};
|
|
|
|
// 이미지는 복사하지 않음 (필요시 LoadImage() 호출)
|
|
return clone;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 리소스 정리
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
LoadedImage?.Dispose();
|
|
LoadedImage = null;
|
|
}
|
|
}
|
|
} |