# Programmatic Map Image Generation

## Overview

Create a tool to programmatically regenerate the Eagle strategic map images with more equalized province sizes while preserving neighbor relationships. Also implement dynamic text rendering for province names that scales with zoom.

## Current Map Assets

| File | Description | Dimensions |
|------|-------------|------------|
| `Assets/Eagle/rawGray.gz.bytes` | Province ID lookup map (gzip compressed) | 3786 x 1834 |
| `Assets/Eagle/map_bw_labels.png` | B&W map with baked-in province labels | 3786 x 1834, RGBA |
| `Assets/Eagle/Materials/1.png` - `43.png` | Individual province mask images | 3786 x 1834, grayscale |

## Province Data

- 43 provinces defined in `src/main/resources/net/eagle0/eagle/province_map.tsv`
- Each has: id, name, neighbors, neighborPositions, hexMapName
- Problem provinces (too small): **Shumal** (id=1), **Kojaria** (id=39), **Usvol** (id=33), **Tumala** (id=35), **Chia** (id=43)

## Goals

1. **Equalize province sizes** - Reduce disparity so small provinces are easier to click
2. **Preserve topology** - Maintain all neighbor relationships
3. **Preserve coastline** - Keep ocean/land boundaries
4. **Dynamic text rendering** - Province names scale with zoom level

---

## Part 1: Province Size Equalization Tool

### Algorithm: Constrained Voronoi Relaxation

1. **Extract current state from rawGray:**
   - Calculate centroid of each province
   - Calculate area (pixel count) of each province
   - Identify ocean pixels (value 0 or 255)

2. **Iterative relaxation:**
   ```
   For each iteration:
     For each province:
       Calculate target area (average of all provinces)
       If province is too small:
         Move centroid away from larger neighbors
       If province is too large:
         Move centroid toward smaller neighbors
       Constraint: Don't break neighbor relationships
   ```

3. **Generate new boundaries:**
   - Use weighted Voronoi diagram from relaxed centroids
   - Weight by target area
   - Mask with original coastline

4. **Output new images:**
   - New rawGray (province ID map)
   - New province masks (1.png - 43.png)
   - New B&W base map (derived from boundaries)

### Implementation Options

**Option A: Python Script (Recommended)**
- Use numpy, scipy, PIL/Pillow
- Can run offline, generate assets once
- Easier to iterate and debug
- Output: New image files to replace existing

**Option B: Unity Editor Script**
- C# with Unity's Texture2D
- Integrated into build process
- Slower, harder to debug

### Tool Location
`tools/map_generator/` or `scripts/generate_map.py`

---

## Part 2: Dynamic Province Name Rendering

### Current State
- Province names are baked into `map_bw_labels.png`
- Not visible when zoomed out, too small when zoomed in
- `SetUpCenterText()` in MapController shows selected/hovered province name in a fixed UI panel

### New Approach

1. **Create province label GameObjects:**
   - One TextMeshPro label per province
   - Position at province centroid
   - Child of map content (moves with zoom/pan)

2. **Dynamic scaling:**
   - Scale inversely with zoom level
   - At zoom 1.0: Show major provinces only (or none)
   - At zoom 2.0+: Show all province names
   - Font size adjusts to fit province area

3. **Implementation:**
   ```csharp
   public class ProvinceLabelsController : MonoBehaviour {
       public MapPinchZoomHandler zoomHandler;
       public TextMeshProUGUI labelPrefab;

       private Dictionary<int, TextMeshProUGUI> _labels;

       void Update() {
           float zoom = zoomHandler.CurrentZoom;
           foreach (var label in _labels.Values) {
               label.transform.localScale = Vector3.one / zoom;
               label.gameObject.SetActive(zoom >= 1.5f);
           }
       }
   }
   ```

4. **Province centroid data:**
   - Add `centroid_x`, `centroid_y` columns to `province_map.tsv`
   - Or calculate at runtime from rawGray

---

## Files to Create/Modify

### New Files
| File | Purpose |
|------|---------|
| `tools/map_generator/generate_map.py` | Python script to generate new map images |
| `tools/map_generator/requirements.txt` | Python dependencies |
| `Assets/Eagle/ProvinceLabelsController.cs` | Dynamic province name rendering |

### Modified Files
| File | Changes |
|------|---------|
| `Assets/Eagle/rawGray.gz.bytes` | Replaced with equalized version |
| `Assets/Eagle/Materials/*.png` | Replaced province masks |
| `Assets/Eagle/map_bw_labels.png` | Either: regenerated, or removed if using dynamic labels |
| `province_map.tsv` | Add centroid columns |
| `Assets/Gameplay.unity` | Add ProvinceLabelsController, label prefab instances |

---

## Verification

1. **Visual inspection:**
   - Compare old vs new province sizes
   - Verify small provinces (Shumal, Kojaria, Usvol) are larger
   - Verify neighbor relationships preserved

2. **Functional testing:**
   - Click on each province at various zoom levels
   - Verify correct province is selected
   - Verify province colors/weather still work

3. **Label testing:**
   - Zoom in/out, verify labels scale appropriately
   - Pan around, verify labels move with map
   - Verify labels don't overlap excessively

---

## Design Decisions

1. **Equalization approach:** Reduce disparity only
   - Make small provinces (Shumal, Kojaria, Usvol, etc.) 2-3x larger
   - Minimal changes to large provinces
   - Not targeting perfectly equal areas

2. **Coastline:** Allow simplification
   - Coastline can be smoothed/simplified as part of generation
   - Voronoi edges are acceptable

3. **Map style:** Solid fills with borders
   - Clean, simple look
   - Easier to generate programmatically
   - Province boundaries rendered as dark lines

---

## Implementation Steps

### Step 1: Python Map Generator Tool

```
tools/map_generator/
├── generate_map.py      # Main script
├── requirements.txt     # numpy, scipy, Pillow
└── README.md           # Usage instructions
```

**Algorithm:**
1. Load current rawGray.gz.bytes, decompress
2. Calculate each province's centroid and area
3. Identify land mask (non-ocean pixels)
4. Run constrained Voronoi relaxation:
   - Small provinces push neighbors away
   - Large provinces pull neighbors in
   - Stop when min province is >= 50% of average
5. Generate weighted Voronoi from relaxed centroids
6. Output:
   - `rawGray_new.bytes` (province ID map)
   - `1.png` - `43.png` (province masks)
   - `map_bw.png` (solid fill + borders)
   - `centroids.json` (for label positioning)

### Step 2: Dynamic Province Labels (Unity)

1. Create `ProvinceLabelsController.cs`
2. Load centroids from JSON or calculate from rawGray
3. Spawn TextMeshPro labels at each centroid
4. Update label scale/visibility based on zoom

### Step 3: Integration

1. Replace asset files with generated versions
2. Update `MapController` to use new assets
3. Add `ProvinceLabelsController` to scene
4. Remove baked label image dependency
