feat: Improve mouse-centered zoom with smooth scaling

- UnifiedAGVCanvas 마우스 휠 줌 로직 개선
- 마우스 커서 위치를 기준점으로 하는 정확한 줌 구현
- 줌 비율 1.2배 → 1.15배로 조정 (더 부드러운 동작)
- 스크린 좌표와 월드 좌표 변환을 명시적으로 처리
- 마우스 위치가 줌 전후 동일한 월드 좌표를 가리키도록 보장

개선 효과:
 마우스 아래의 콘텐츠가 줌 중심
 더 자연스럽고 예측 가능한 줌 동작
 좌표 계산 로직 명확화

추가:
- PROJECT_SUMMARY.md: 3개 프로젝트 상세 요약
  - AGVMapEditor (맵 편집 도구)
  - AGVNavigationCore (경로 계산 엔진)
  - AGVSimulator (시뮬레이터)
  - UnifiedAGVCanvas 기능 설명
  - 현재 미완성 부분 정리

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
backuppc
2025-10-23 09:55:29 +09:00
parent 03760837ab
commit ce78752c2c
2 changed files with 366 additions and 9 deletions

View File

@@ -170,19 +170,23 @@ namespace AGVNavigationCore.Controls
private void UnifiedAGVCanvas_MouseWheel(object sender, MouseEventArgs e) private void UnifiedAGVCanvas_MouseWheel(object sender, MouseEventArgs e)
{ {
// 줌 처리 // 현재 마우스 위치를 월드 좌표로 변환 (줌 전)
var mouseWorldPoint = ScreenToWorld(e.Location); var mouseWorldBefore = ScreenToWorld(e.Location);
var oldZoom = _zoomFactor;
float oldZoom = _zoomFactor;
// 줌 팩터 계산 (휠 델타 기반) - 더 부드러운 줌
if (e.Delta > 0) if (e.Delta > 0)
_zoomFactor = Math.Min(_zoomFactor * 1.2f, 5.0f); _zoomFactor = Math.Min(_zoomFactor * 1.15f, 5.0f); // 확대 (더 부드러움)
else else
_zoomFactor = Math.Max(_zoomFactor / 1.2f, 0.1f); _zoomFactor = Math.Max(_zoomFactor / 1.15f, 0.1f); // 축소 (더 부드러움)
// 마우스 위치를 중심으로 줌 // 줌 후 마우스 위치의 월드 좌표
var zoomRatio = _zoomFactor / oldZoom; var mouseWorldAfter = ScreenToWorld(e.Location);
_panOffset.X = (int)(e.X - (e.X - _panOffset.X) * zoomRatio);
_panOffset.Y = (int)(e.Y - (e.Y - _panOffset.Y) * zoomRatio); // 마우스 위치가 같은 월드 좌표를 가리키도록 팬 오프셋 조정
_panOffset.X += (int)((mouseWorldBefore.X - mouseWorldAfter.X) * _zoomFactor);
_panOffset.Y += (int)((mouseWorldBefore.Y - mouseWorldAfter.Y) * _zoomFactor);
Invalidate(); Invalidate();
} }

353
Cs_HMI/PROJECT_SUMMARY.md Normal file
View File

@@ -0,0 +1,353 @@
# 프로젝트 요약 (AGVMapEditor, AGVNavigationCore, AGVSimulator)
## 📊 프로젝트 개요
3개의 주요 프로젝트가 **AGVNavigationCore** 라이브러리를 공유하며 연동되는 구조입니다.
```
┌─────────────────────────────────────────────────────────────┐
│ AGVNavigationCore (공유 라이브러리) │
├─────────────────────────────────────────────────────────────┤
│ • Models: MapNode, MapLoader, Enums, IMovableAGV, VirtualAGV
│ • Controls: UnifiedAGVCanvas (통합 UI 렌더링)
│ • PathFinding: A*, AGVPathfinder, DirectionChangePlanner
└─────────────────────────────────────────────────────────────┘
▲ ▲ ▲
│ 참조 │ 참조 │ 참조
│ │ │
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ AGVMapEditor │ │ AGVSimulator │ │ AGV4 (미사용)
│ (맵 편집) │ │ (시뮬레이션) │ │ (메인앱)
└──────────────┘ └──────────────┘ └──────────────┘
```
---
## 🎯 각 프로젝트의 기능
### 1⃣ **AGVMapEditor** (맵 편집 도구)
**목적**: AGV 맵 데이터를 시각적으로 생성/편집
#### 제공 기능
| 기능 | 설명 |
|------|------|
| **노드 생성** | 맵 상에 AGV 네비게이션 노드 추가 |
| **노드 편집** | 노드 이름, RFID, 타입, 도킹방향 설정 |
| **노드 이동** | 드래그로 노드 위치 변경 (그리드 스냅 지원) |
| **연결 관리** | 노드 간 경로 연결 생성/삭제 |
| **노드 삭제** | 선택된 노드 제거 |
| **라벨 추가** | 맵에 텍스트 라벨 추가 |
| **이미지 추가** | 맵에 배경 이미지 추가 |
| **맵 저장/로드** | JSON 형식 맵 파일 저장/로드 |
#### 기술 구성
```
MainForm (WinForms)
└─ UnifiedAGVCanvas (UI 렌더링)
├─ MapNode 목록 관리
├─ 편집 모드 (Select, Move, AddNode, Connect, Delete, DeleteConnection, AddLabel, AddImage)
└─ MapLoader 사용 (JSON 저장/로드)
```
#### 주요 UI 요소
- **메뉴바**: File (Open/Save), Edit, View
- **툴바**: 편집 모드 전환 버튼 (선택, 이동, 노드추가, 연결, 삭제, 라벨, 이미지)
- **속성 패널**: 선택된 노드 정보 표시/편집
- **캔버스**: 맵 시각화 및 편집
---
### 2⃣ **AGVNavigationCore** (경로 계산 엔진 라이브러리)
**목적**: AGV 경로 계산 및 네비게이션 핵심 로직 제공
#### 제공 기능
| 영역 | 모듈 | 기능 |
|------|------|------|
| **Models** | MapNode | AGV 네비게이션 노드 데이터 |
| | MapLoader | 맵 파일 로드/저장 |
| | IMovableAGV | AGV 동작 인터페이스 |
| | VirtualAGV | 가상 AGV 시뮬레이션 로직 ⚠️ **미완성** |
| **Controls** | UnifiedAGVCanvas | 맵 렌더링/편집/모니터링 UI |
| **PathFinding** | AStarPathfinder | 기본 A* 경로 탐색 |
| | AGVPathfinder | AGV 제약 고려 경로 계산 ⚠️ **미완성** |
| | DirectionChangePlanner | 방향 전환 경로 계획 ⚠️ **미완성** |
#### 핵심 기능 (구현 상태)
```
✅ 완성
├─ MapNode 데이터 모델
├─ MapLoader (파일 I/O)
├─ UnifiedAGVCanvas (UI 렌더링/편집)
└─ AStarPathfinder (기본 경로 탐색)
❌ 미완성 (개발 대상)
├─ VirtualAGV.ExecutePath() - 경로 실행
├─ VirtualAGV.Update() - 프레임 업데이트
├─ AGVPathfinder 핵심 로직 - 경로 상세화, 마그넷 방향 계산
└─ DirectionChangePlanner - 4단계 방향 전환 알고리즘
```
#### 기술 구성
```
AGVNavigationCore (Class Library)
├── Models/
│ ├── MapNode.cs
│ ├── MapLoader.cs
│ ├── VirtualAGV.cs (← 경로 추적 미완성)
│ ├── IMovableAGV.cs
│ └── Enums.cs
├── Controls/
│ ├── UnifiedAGVCanvas.cs (메인 UI)
│ ├── UnifiedAGVCanvas.Designer.cs
│ ├── UnifiedAGVCanvas.Events.cs
│ ├── UnifiedAGVCanvas.Mouse.cs (← 줌 기능)
│ ├── UnifiedAGVCanvas.Rendering.cs
│ └── UnifiedAGVCanvas.Utilities.cs
├── PathFinding/
│ ├── Core/
│ │ ├── AStarPathfinder.cs ✅
│ │ ├── PathNode.cs
│ │ └── AGVPathResult.cs
│ ├── Planning/
│ │ ├── AGVPathfinder.cs (❌ 미완성)
│ │ ├── DirectionChangePlanner.cs (❌ 미완성)
│ │ └── NodeMotorInfo.cs
│ ├── Analysis/
│ │ └── JunctionAnalyzer.cs
│ └── Validation/
│ ├── PathValidationResult.cs
│ └── DockingValidationResult.cs
└── Utils/
└── LiftCalculator.cs
```
---
### 3⃣ **AGVSimulator** (AGV 시뮬레이터)
**목적**: AGV 경로 계산 및 동작을 시각적으로 검증
#### 제공 기능
| 기능 | 설명 |
|------|------|
| **맵 로드** | 저장된 맵 파일 로드 |
| **AGV 시뮬레이션** | 가상 AGV 생성 및 경로 따라 이동 |
| **경로 계산** | 시작점/목적지 선택 후 경로 자동 계산 |
| **경로 시각화** | 계산된 경로 맵에 표시 |
| **실시간 상태 모니터링** | AGV 위치, 방향, 상태 실시간 표시 |
| **도킹 검증** | AGV 도킹 방향 검증 |
#### 기술 구성
```
SimulatorForm (WinForms)
├─ UnifiedAGVCanvas (맵 렌더링)
├─ VirtualAGV 리스트 (가상 AGV들)
├─ MapLoader (맵 파일 로드)
├─ AGVPathfinder (경로 계산)
└─ 시뮬레이션 타이머 (매 프레임 AGV 업데이트)
```
#### 주요 UI 요소
- **메뉴바**: File (Open Map)
- **경로 제어 그룹**: 시작RFID, 목적지RFID, 타겟계산 버튼
- **시뮬레이션 제어**: 시작, 일시정지, 정지 버튼
- **캔버스**: 맵 + AGV + 경로 시각화
---
## 🎨 UnifiedAGVCanvas (통합 UI 컨트롤)
**위치**: `AGVNavigationCore/Controls/UnifiedAGVCanvas.cs` (4개 파일)
### 핵심 기능
| 기능 | 설명 |
|------|------|
| **맵 렌더링** | 노드, 연결선, 그리드, AGV, 경로 표시 |
| **편집 기능** | 노드 추가/이동/삭제, 연결 관리 (AGVMapEditor) |
| **모니터링** | 실시간 AGV 상태 표시 (AGVSimulator) |
| **줌/팬** | 마우스 휠 줌, 좌클릭 드래그 팬 |
| **선택/호버** | 노드 선택, 호버 표시 |
| **경로 시각화** | 계산된 경로를 색상으로 표시 |
### 모드 및 상태
```csharp
// CanvasMode
Edit // 편집 모드 (맵 에디터)
// EditMode (Edit 모드에서만 적용)
Select // 노드 선택
Move // 노드 이동
AddNode // 노드 추가
Connect // 연결 생성
Delete // 노드/연결 삭제
DeleteConnection // 연결 삭제
AddLabel // 라벨 추가
AddImage // 이미지 추가
```
---
## 🖱️ 줌/팬 기능 (현재 상태)
### 현재 구현
```csharp
// UnifiedAGVCanvas.Mouse.cs : 171-188
private void UnifiedAGVCanvas_MouseWheel(object sender, MouseEventArgs e)
{
// 줌 처리
var mouseWorldPoint = ScreenToWorld(e.Location);
var oldZoom = _zoomFactor;
if (e.Delta > 0)
_zoomFactor = Math.Min(_zoomFactor * 1.2f, 5.0f); // 확대 (1.2배)
else
_zoomFactor = Math.Max(_zoomFactor / 1.2f, 0.1f); // 축소 (1.2배)
// 마우스 위치를 중심으로 줌
var zoomRatio = _zoomFactor / oldZoom;
_panOffset.X = (int)(e.X - (e.X - _panOffset.X) * zoomRatio);
_panOffset.Y = (int)(e.Y - (e.Y - _panOffset.Y) * zoomRatio);
Invalidate();
}
```
### 문제점 및 개선 사항
#### ⚠️ 현재 문제
1. **줌 계산 로직**: 수식이 복잡하고 정확하지 않을 수 있음
2. **좌표계 혼동**: 스크린 좌표와 월드 좌표 변환이 일관성 없음
3. **매끄러움**: 줌 비율 계산에서 부자연스러운 동작 가능
#### ✅ 개선 방안
마우스 커서 위치를 기준점으로 하는 스무스한 줌을 구현:
```csharp
private void UnifiedAGVCanvas_MouseWheel(object sender, MouseEventArgs e)
{
// 현재 마우스 위치를 월드 좌표로 변환
var mouseWorldBefore = ScreenToWorld(e.Location);
float oldZoom = _zoomFactor;
// 줌 팩터 계산 (휠 델타 기반)
if (e.Delta > 0)
_zoomFactor = Math.Min(_zoomFactor * 1.15f, 5.0f); // 확대 (부드러움)
else
_zoomFactor = Math.Max(_zoomFactor / 1.15f, 0.1f); // 축소 (부드러움)
// 마우스 위치가 같은 월드 좌표를 가리키도록 팬 오프셋 조정
var mouseWorldAfter = ScreenToWorld(e.Location);
_panOffset.X += (int)((mouseWorldBefore.X - mouseWorldAfter.X) * _zoomFactor);
_panOffset.Y += (int)((mouseWorldBefore.Y - mouseWorldAfter.Y) * _zoomFactor);
Invalidate();
}
```
#### 주요 개선점
1. **더 부드러운 줌**: 1.2배 → 1.15배로 조정
2. **명확한 로직**: 마우스 위치 기준으로 명시적으로 계산
3. **정확한 좌표 변환**: ScreenToWorld() 사용으로 일관성 보장
4. **자연스러운 동작**: 마우스 아래의 점이 같은 위치를 가리킴
---
## 📂 파일 구조 (48개 C# 파일)
### AGVMapEditor (10개 파일)
```
AGVMapEditor/
├── Forms/
│ ├── MainForm.cs (메인 폼, 편집 로직)
│ └── MainForm.Designer.cs (UI 디자인)
├── Models/
│ ├── EditorSettings.cs (에디터 설정)
│ ├── MapImage.cs (맵 이미지 데이터)
│ ├── MapLabel.cs (맵 라벨 데이터)
│ └── NodePropertyWrapper.cs (노드 속성 래퍼)
├── Program.cs (진입점)
└── Properties/
└── AssemblyInfo.cs (어셈블리 정보)
```
### AGVNavigationCore (30개 파일)
```
AGVNavigationCore/
├── Models/ (8개)
│ ├── MapNode.cs, MapLoader.cs, VirtualAGV.cs, IMovableAGV.cs, etc.
├── Controls/ (6개)
│ ├── UnifiedAGVCanvas.cs, UnifiedAGVCanvas.Designer.cs, etc.
├── PathFinding/ (13개)
│ ├── Core/ (AStarPathfinder, PathNode, AGVPathResult)
│ ├── Planning/ (AGVPathfinder, DirectionChangePlanner, etc.)
│ ├── Analysis/ (JunctionAnalyzer)
│ └── Validation/ (PathValidationResult, DockingValidationResult)
└── Utils/ (3개)
├── LiftCalculator.cs, DockingValidator.cs, etc.
```
### AGVSimulator (8개 파일)
```
AGVSimulator/
├── Forms/
│ ├── SimulatorForm.cs (시뮬레이터 메인 폼)
│ └── SimulatorForm.Designer.cs
├── Models/
│ └── (VirtualAGV는 AGVNavigationCore에서 참조)
├── Program.cs
└── Properties/
└── AssemblyInfo.cs
```
---
## 🔄 데이터 흐름
### 맵 편집 → 저장 흐름
```
AGVMapEditor.MainForm
UnifiedAGVCanvas (편집 모드)
MapLoader.SaveMapToFile()
NewMap.agvmap (JSON 파일)
```
### 맵 로드 → 시뮬레이션 흐름
```
NewMap.agvmap (JSON 파일)
MapLoader.LoadMapFromFile()
AGVSimulator.SimulatorForm
UnifiedAGVCanvas (모니터링 모드)
VirtualAGV (경로 실행) ⚠️ 미완성
```
---
## ⚠️ 현재 미완성 부분
### 🔴 우선순위 높음
1. **VirtualAGV.ExecutePath()** - 경로 실행 로직
2. **VirtualAGV.Update()** - 매 프레임 위치 업데이트
3. **AGVPathfinder 핵심** - 경로 상세화, 마그넷 방향 계산
### 🟡 우선순위 중간
4. **DirectionChangePlanner** - 4단계 방향 전환 알고리즘
5. **UnifiedAGVCanvas 줌 개선** - 마우스 기준 스무스 줌
---
## 📝 참고 문서
- `CLAUDE.md` - 개발 가이드 및 AGV 하드웨어 설명
- `CHANGELOG.md` - 변경 로그
- `Data/NewMap.agvmap` - 실제 맵 데이터 샘플