파일정리

This commit is contained in:
ChiKyun Kim
2026-01-29 14:03:17 +09:00
parent 00cc0ef5b7
commit 58ca67150d
440 changed files with 47236 additions and 99165 deletions

View File

@@ -0,0 +1,162 @@
using System;
using System.IO;
using Newtonsoft.Json;
namespace AGVMapEditor.Models
{
/// <summary>
/// AGV 맵 에디터의 환경설정을 관리하는 클래스
/// </summary>
public class EditorSettings
{
#region Properties
/// <summary>
/// 마지막으로 열었던 맵 파일의 경로
/// </summary>
public string LastMapFilePath { get; set; } = string.Empty;
/// <summary>
/// 프로그램 시작시 마지막 맵 파일을 자동으로 로드할지 여부
/// </summary>
public bool AutoLoadLastMapFile { get; set; } = true;
/// <summary>
/// 설정이 마지막으로 저장된 시간
/// </summary>
public DateTime LastSaved { get; set; } = DateTime.Now;
/// <summary>
/// 기본 맵 파일 저장 디렉토리
/// </summary>
public string DefaultMapDirectory { get; set; } = string.Empty;
#endregion
#region Constants
private static readonly string SettingsFileName = "EditorSettings.json";
private static readonly string SettingsDirectory = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"AGVMapEditor");
private static readonly string SettingsFilePath = Path.Combine(SettingsDirectory, SettingsFileName);
#endregion
#region Static Instance
private static EditorSettings _instance;
private static readonly object _lock = new object();
/// <summary>
/// 싱글톤 인스턴스
/// </summary>
public static EditorSettings Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = LoadSettings();
}
}
}
return _instance;
}
}
#endregion
#region Methods
/// <summary>
/// 설정을 파일에서 로드
/// </summary>
private static EditorSettings LoadSettings()
{
try
{
if (File.Exists(SettingsFilePath))
{
string jsonContent = File.ReadAllText(SettingsFilePath);
var settings = JsonConvert.DeserializeObject<EditorSettings>(jsonContent);
return settings ?? new EditorSettings();
}
}
catch (Exception ex)
{
// 설정 로드 실패시 기본 설정 사용
System.Diagnostics.Debug.WriteLine($"설정 로드 실패: {ex.Message}");
}
return new EditorSettings();
}
/// <summary>
/// 설정을 파일에 저장
/// </summary>
public void Save()
{
try
{
// 디렉토리가 없으면 생성
if (!Directory.Exists(SettingsDirectory))
{
Directory.CreateDirectory(SettingsDirectory);
}
LastSaved = DateTime.Now;
string jsonContent = JsonConvert.SerializeObject(this, Formatting.Indented);
File.WriteAllText(SettingsFilePath, jsonContent);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"설정 저장 실패: {ex.Message}");
}
}
/// <summary>
/// 마지막 맵 파일 경로 업데이트
/// </summary>
/// <param name="filePath">맵 파일 경로</param>
public void UpdateLastMapFile(string filePath)
{
if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
{
LastMapFilePath = filePath;
// 기본 디렉토리도 업데이트
DefaultMapDirectory = Path.GetDirectoryName(filePath);
Save();
}
}
/// <summary>
/// 마지막 맵 파일이 존재하는지 확인
/// </summary>
public bool HasValidLastMapFile()
{
return !string.IsNullOrEmpty(LastMapFilePath) && File.Exists(LastMapFilePath);
}
/// <summary>
/// 설정 초기화
/// </summary>
public void Reset()
{
LastMapFilePath = string.Empty;
AutoLoadLastMapFile = true;
DefaultMapDirectory = string.Empty;
LastSaved = DateTime.Now;
Save();
}
#endregion
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace AGVMapEditor.Models
{
/// <summary>
/// PropertyGrid에서 이미지 파일 경로를 선택하기 위한 커스텀 UITypeEditor
/// PropertyGrid에 "..." 버튼을 표시하고, 클릭 시 파일 열기 대화상자를 표시
/// </summary>
public class ImagePathEditor : UITypeEditor
{
/// <summary>
/// PropertyGrid에서 이 에디터의 UI 스타일 반환
/// DropDown 스타일을 사용하여 "..." 버튼을 표시
/// </summary>
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
/// <summary>
/// 사용자가 "..." 버튼을 클릭할 때 호출되는 메서드
/// </summary>
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
// IWindowsFormsEditorService를 얻어서 대화상자를 표시
var editorService = provider?.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
if (editorService == null)
return value;
// 파일 열기 대화상자 생성
using (var ofd = new OpenFileDialog())
{
ofd.Title = "이미지 파일 선택";
ofd.Filter = "이미지 파일|*.jpg;*.jpeg;*.png;*.bmp;*.gif|모든 파일|*.*";
ofd.CheckFileExists = true;
// 현재 경로가 있으면 해당 위치에서 시작
if (!string.IsNullOrEmpty(value?.ToString()))
{
try
{
string currentPath = value.ToString();
if (System.IO.File.Exists(currentPath))
{
ofd.InitialDirectory = System.IO.Path.GetDirectoryName(currentPath);
ofd.FileName = System.IO.Path.GetFileName(currentPath);
}
}
catch { }
}
// 대화상자 표시
if (ofd.ShowDialog() == DialogResult.OK)
{
// 선택된 파일 경로를 Base64로 변환하고 반환
string filePath = ofd.FileName;
return filePath; // MapNode의 ConvertImageToBase64는 setter에서 호출됨
}
}
return value;
}
/// <summary>
/// PropertyGrid에서 이 타입의 값을 표시하는 방법
/// 파일 경로를 파일명만 표시하도록 처리
/// </summary>
public override bool GetPaintValueSupported(ITypeDescriptorContext context)
{
return false;
}
}
}

View File

@@ -0,0 +1,210 @@
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;
}
}
}

View File

@@ -0,0 +1,125 @@
using System;
using System.Drawing;
namespace AGVMapEditor.Models
{
/// <summary>
/// 맵 라벨 정보를 관리하는 클래스
/// 디자인 요소용 텍스트 라벨
/// </summary>
public class MapLabel
{
/// <summary>
/// 라벨 고유 ID
/// </summary>
public string LabelId { get; set; } = string.Empty;
/// <summary>
/// 라벨 텍스트
/// </summary>
public string Text { get; set; } = string.Empty;
/// <summary>
/// 맵 상의 위치 좌표
/// </summary>
public Point Position { get; set; } = Point.Empty;
/// <summary>
/// 폰트 정보
/// </summary>
public string FontFamily { get; set; } = "Arial";
/// <summary>
/// 폰트 크기
/// </summary>
public float FontSize { get; set; } = 12;
/// <summary>
/// 폰트 스타일 (Bold, Italic 등)
/// </summary>
public FontStyle FontStyle { get; set; } = FontStyle.Regular;
/// <summary>
/// 글자 색상
/// </summary>
public Color ForeColor { get; set; } = Color.Black;
/// <summary>
/// 배경 색상
/// </summary>
public Color BackColor { get; set; } = Color.Transparent;
/// <summary>
/// 배경 표시 여부
/// </summary>
public bool ShowBackground { get; set; } = false;
/// <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>
/// 기본 생성자
/// </summary>
public MapLabel()
{
}
/// <summary>
/// 매개변수 생성자
/// </summary>
/// <param name="labelId">라벨 ID</param>
/// <param name="text">라벨 텍스트</param>
/// <param name="position">위치</param>
public MapLabel(string labelId, string text, Point position)
{
LabelId = labelId;
Text = text;
Position = position;
CreatedDate = DateTime.Now;
ModifiedDate = DateTime.Now;
}
/// <summary>
/// 문자열 표현
/// </summary>
public override string ToString()
{
return $"{LabelId}: {Text} at ({Position.X}, {Position.Y})";
}
/// <summary>
/// 라벨 복사
/// </summary>
/// <returns>복사된 라벨</returns>
public MapLabel Clone()
{
return new MapLabel
{
LabelId = LabelId,
Text = Text,
Position = Position,
FontFamily = FontFamily,
FontSize = FontSize,
FontStyle = FontStyle,
ForeColor = ForeColor,
BackColor = BackColor,
ShowBackground = ShowBackground,
CreatedDate = CreatedDate,
ModifiedDate = ModifiedDate,
IsActive = IsActive
};
}
}
}