파일정리
This commit is contained in:
162
AGVLogic/AGVMapEditor/Models/EditorSettings.cs
Normal file
162
AGVLogic/AGVMapEditor/Models/EditorSettings.cs
Normal 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
|
||||
}
|
||||
}
|
||||
77
AGVLogic/AGVMapEditor/Models/ImagePathEditor.cs
Normal file
77
AGVLogic/AGVMapEditor/Models/ImagePathEditor.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
210
AGVLogic/AGVMapEditor/Models/MapImage.cs
Normal file
210
AGVLogic/AGVMapEditor/Models/MapImage.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
125
AGVLogic/AGVMapEditor/Models/MapLabel.cs
Normal file
125
AGVLogic/AGVMapEditor/Models/MapLabel.cs
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user