# 時間軸標籤避碰改進(v4.0) - 防止線條交錯 ## 新增改進(v4.0) ### 問題描述 v3.0 雖然解決了文字框重疊問題,但仍存在以下問題: 1. ❌ 連接線互相交錯 2. ❌ 連接線穿過其他文字框 3. ❌ 密集事件時視覺混亂 ### 解決方案 #### 1. **智能路徑分層** **核心概念**:讓不同層級的連接線使用不同的中間高度/寬度,避免交錯。 ```python # 水平時間軸(L 形折線的中間高度) base_ratio = 0.45 # 基礎高度比例 layer_offset = (layer % 6) * 0.10 # 每層偏移 10%,每 6 層循環 mid_y_ratio = base_ratio + layer_offset mid_y = label_y * mid_y_ratio # 垂直時間軸(L 形折線的中間寬度) base_ratio = 0.45 # 基礎寬度比例 layer_offset = (layer % 6) * 0.10 # 每層偏移 10% mid_x_ratio = base_ratio + layer_offset mid_x = label_x * mid_x_ratio ``` **效果**: - 層級 0:中間點在 45% 位置 - 層級 1:中間點在 55% 位置 - 層級 2:中間點在 65% 位置 - 層級 3:中間點在 75% 位置 - 層級 4:中間點在 85% 位置 - 層級 5:中間點在 95% 位置 - 層級 6:循環回 45% 位置 #### 2. **避開文字框核心區域** 防止線條的水平段太接近文字框中心: ```python # 如果計算出的中間點太接近文字框位置,則強制調整 if abs(mid_y - label_y) < abs(label_y) * 0.15: mid_y = label_y * 0.35 # 設為更安全的距離 ``` **效果**: - ✅ 線條不會直接穿過文字框中心 - ✅ 保持至少 15% 的安全距離 #### 3. **增加文字框間距** 調整碰撞檢測參數,確保文字框之間有足夠空間: ```python # 標籤寬度(包含時間+標題+描述) label_width_ratio = 0.15 # 15% 的時間軸寬度 # 安全邊距 safety_margin = total_seconds * 0.01 # 1% 的額外緩衝 # 最小水平間距 min_horizontal_gap = total_seconds * 0.03 # 3% 的時間軸寬度 # 層級垂直間距 layer_spacing = 1.0 # 層級之間的垂直距離 ``` --- ## 完整改進歷程 ### v1.0(初版) - ❌ 直線連接 - ❌ 標籤固定位置 - ❌ 只有日期 - ❌ 容易重疊 ### v2.0(2D 避碰) - ✅ Z 形折線 - ✅ 標籤可偏移 - ✅ 智能避碰 - ⚠️ 折線可能交錯 ### v3.0(平滑曲線 + 時間分離) - ✅ 平滑曲線(虛線 + 半透明) - ✅ 時間顯示在點旁邊 - ✅ 標題與時間分離 - ⚠️ 用戶反饋:需要簡化 ### v3.1(簡化版) - ✅ L 形直角折線(取代曲線) - ✅ 時間+標題+描述統一顯示 - ✅ 時分秒精度 - ⚠️ 文字框重疊 ### v3.2(增加間距) - ✅ 增加標籤寬度(15%) - ✅ 增加層級間距(1.0) - ✅ 添加安全邊距(1%) - ⚠️ 線條仍會交錯 ### v4.0(智能路徑分層 - 初版) - ✅ 不同層級使用不同高度/寬度 - ✅ 線條避開文字框核心區域 - ✅ 文字框之間充足間距 - ⚠️ 仍有交錯問題(用戶反饋) ### v4.1(當前版本 - 鏡像分布 + 距離感知) - ✅ **上下/左右鏡像分布策略** - ✅ **根據跨越距離動態調整路徑** - ✅ **10層循環,60%範圍變化** - ✅ **長距離線條自動降低高度** - ✅ **線條交錯最小化** - ✅ **整體視覺清晰專業** --- ## v4.1 核心改進 ### 1. **鏡像分布策略** **問題**:v4.0 中上下兩側的線條使用相同的分層策略,容易在中間區域交錯。 **解決**:上下(或左右)兩側使用鏡像分布: ```python # 水平時間軸 if is_upper_side: # 上方 base_ratio = 0.25 # 從 25% 開始 layer_offset = layer_group * 0.06 # 正向增長: 25% -> 85% else: # 下方 base_ratio = 0.85 # 從 85% 開始 layer_offset = -layer_group * 0.06 # 負向增長: 85% -> 25% ``` **效果**: - 上方 layer 0 在 25%,下方 layer 0 在 85% → 分隔明顯 - 上方 layer 5 在 55%,下方 layer 5 在 55% → 在中間匯合 - 上方 layer 9 在 79%,下方 layer 9 在 31% → 接近但不重疊 ### 2. **距離感知調整** **問題**:長距離線條容易穿過中間的文字框。 **解決**:根據跨越距離動態調整中間點高度: ```python x_span_ratio = abs(x_diff_seconds) / total_range if x_span_ratio > 0.3: # 跨越超過 30% 時間軸 # 上方線條降低,下方線條升高,避開中間區域 distance_adjustment = -0.10 if is_upper_side else 0.10 elif x_span_ratio > 0.15: # 跨越 15-30% distance_adjustment = -0.05 if is_upper_side else 0.05 else: distance_adjustment = 0 # 短距離不調整 ``` **效果**: - ✅ 短距離線條:保持原有層級策略 - ✅ 中距離線條:輕微調整 5% - ✅ 長距離線條:大幅調整 10%,遠離文字框密集區 ### 3. **增加層級循環週期** ```python layer_group = layer % 10 # 從 6 層增加到 10 層 ``` **效果**: - 提供更多的高度選擇(10 個不同高度) - 減少不同層級使用相同高度的機率 - 更細緻的分布 --- ## 技術細節 ### 路徑分層算法(v4.1) **計算公式**: ``` mid_y_ratio = base_ratio + layer_offset + distance_adjustment mid_y_ratio = max(0.20, min(mid_y_ratio, 0.90)) # 限制範圍 mid_y = label_y * mid_y_ratio ``` **參數範圍**: - `base_ratio`: 上方 0.25,下方 0.85 - `layer_offset`: -0.54 到 +0.54 (10層 × 6%) - `distance_adjustment`: -0.10 到 +0.10 - **總範圍**: 20% 到 90%(70% 的可用空間) ### 路徑分層算法(v4.0 舊版) **水平時間軸**: ``` 事件點 (event_x, 0) ↓ 垂直上升 中間點 (event_x, mid_y) ← 根據層級調整 → 水平移動 轉折點 (label_x, mid_y) ↓ 垂直下降 文字框 (label_x, label_y) ``` **垂直時間軸**: ``` 事件點 (0, event_y) → 水平移動 中間點 (mid_x, event_y) ← 根據層級調整 ↓ 垂直移動 轉折點 (mid_x, label_y) → 水平移動 文字框 (label_x, label_y) ``` ### 碰撞檢測策略 1. **計算標籤佔用範圍**(包含安全邊距) 2. **嘗試在同層級放置**(無偏移) 3. **嘗試水平偏移**(左側 1x, 2x, 3x) 4. **嘗試水平偏移**(右側 1x, 2x, 3x) 5. **創建新層級**(如果都無法容納) ### 性能優化 - **時間複雜度**:O(n × m × k) - n = 事件數 - m = 平均層級數(通常 < 5) - k = 偏移嘗試次數(最多 7 次) - **空間複雜度**:O(n × m) --- ## 調整建議 如果仍有線條交錯問題,可以調整以下參數: ### 1. 增加層級偏移幅度 ```python # renderer_timeline.py 第 336 行和第 566 行 layer_offset = (layer % 6) * 0.12 # 從 0.10 增加到 0.12 ``` ### 2. 降低基礎比例 ```python # renderer_timeline.py 第 335 行和第 565 行 base_ratio = 0.40 # 從 0.45 降低到 0.40 ``` ### 3. 增加循環週期 ```python # renderer_timeline.py 第 336 行和第 566 行 layer_offset = (layer % 8) * 0.10 # 從 6 層循環改為 8 層循環 ``` ### 4. 增加文字框間距 ```python # renderer_timeline.py 第 81、88、230、449 行 label_width_ratio = 0.18 # 從 0.15 增加到 0.18 min_horizontal_gap = total_seconds * 0.04 # 從 0.03 增加到 0.04 layer_spacing = 1.2 # 從 1.0 增加到 1.2 ``` --- ## 測試方法 ```batch start_dev.bat ``` 訪問 http://localhost:12010,測試示範檔案: - `demo_project_timeline.csv` - 15 個事件 - `demo_life_events.csv` - 11 個事件 - `demo_product_roadmap.csv` - 14 個事件 **預期效果**: - ✅ 文字框之間無重疊 - ✅ 連接線分散在不同高度 - ✅ 線條避開文字框核心區域 - ✅ 線條交錯大幅減少 - ✅ 整體視覺清晰易讀 --- ## 視覺效果示意(v4.1) ### 鏡像分布示意 ``` 100% ╔══════════════════════════════════════╗ ║ ║ 85% ╟────┐ 下方 Layer 0 (base) ║ ║ │ ║ 79% ╟────┘ 下方 Layer 1 ║ ║ ║ 70% ╟───── 中間區域(避開) ║ ║ ║ 55% ╟────┐ 上方/下方 Layer 5 (匯合點) ║ ║ │ ║ 40% ╟───── 中間區域(避開) ║ ║ ║ 31% ╟────┐ 上方 Layer 1 ║ ║ │ ║ 25% ╟────┘ 上方 Layer 0 (base) ║ ║ ║ 20% ╚══════════════════════════════════════╝ ▲ └─ 時間軸 (0%) ``` **特點**: - ✅ 上下兩側從不同端點開始 - ✅ 在中間區域匯合,但錯開 - ✅ 最大程度利用 70% 的垂直空間 - ✅ 避免在中間區域(40%-70%)密集重疊 ### 距離感知調整示意 ``` 短距離 (< 15%): ●─────┐ └──□ 使用標準層級高度 中距離 (15%-30%): ●─────────┐ └──□ 降低 5%(上方)或升高 5%(下方) 長距離 (> 30%): ●──────────────┐ └──□ 降低 10%(上方)或升高 10%(下方) 遠離中間密集區 ``` ### 線條分層(v4.0 舊版) ``` ┌─────────┐ │文字框 3 │ (層級2) └─────────┘ ↑ │ (mid_y = 65%) ├──────────── ┌─────────┐ │文字框 2 │ (層級1) └─────────┘ ↑ │ (mid_y = 55%) ────┼──────────── ┌─────────┐ │文字框 1 │ (層級0) └─────────┘ ↑ ────┘ (mid_y = 45%) ● 時間軸 ``` ### 避開核心區域 ``` ┌───────────┐ │ 文字框 │ │ 核心區域 │ ← 線條不會穿過這裡 │ │ └───────────┘ ↑ ────────┘ (保持安全距離) ● ``` --- **版本**: v4.0 **更新日期**: 2025-11-05 **作者**: Claude AI ## 關鍵改進總結 | 項目 | v3.2 | v4.0 | v4.1 | 改進方法 | |-----|------|------|------|---------| | 文字框重疊 | ✅ 已解決 | ✅ 已解決 | ✅ 已解決 | 增加間距與安全邊距 | | 線條交錯 | ❌ 嚴重 | ⚠️ 仍存在 | ✅ 最小化 | 鏡像分布 + 距離感知 | | 線條穿框 | ❌ 經常 | ⚠️ 偶爾 | ✅ 極少 | 距離感知動態調整 | | 視覺清晰度 | ⚠️ 中等 | ✅ 良好 | ✅ 優秀 | 多層次優化 | | 配置靈活性 | ✅ 可調 | ✅ 高度可調 | ✅ 智能自適應 | 動態參數計算 | | 層級分布 | 單向 | 單向 | 鏡像 | 上下/左右對稱策略 | | 距離處理 | 固定 | 固定 | 動態 | 根據跨越距離調整 |