Files
Timeline_Generator/IMPROVEMENTS_v5.md
beabigegg 2d37d23bcf v9.5: 實作標籤完全不重疊算法
- 新增 _calculate_lane_conflicts_v2() 分開返回標籤重疊和線穿框分數
- 修改泳道選擇算法,優先選擇無標籤重疊的泳道
- 兩階段搜尋:優先側別無可用泳道則嘗試另一側
- 增強日誌輸出,顯示標籤範圍和詳細衝突分數

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-06 11:35:29 +08:00

8.6 KiB
Raw Blame History

時間軸標籤避碰改進v5.0 - 真正的碰撞預防系統

新增改進v5.0

核心概念:從靜態分層到動態碰撞檢測

v4.1 的問題

  • 只是根據層級靜態計算路徑高度
  • 沒有真正檢測線條之間的碰撞
  • 沒有檢測線條與文字框的碰撞
  • 仍然會出現嚴重的重疊

v5.0 的解決方案

  • 真正的碰撞檢測算法
  • 動態路徑優化
  • 20個候選高度選擇最佳路徑
  • 實時追蹤已繪製的線條和文字框

技術實現

1. 碰撞檢測算法

線段與線段碰撞檢測

def check_collision(x_start_sec, x_end_sec, y_height, margin=0.05):
    collision_score = 0

    # 檢查與已繪製線段的碰撞
    for seg_start, seg_end, seg_y in drawn_horizontal_segments:
        # Y 座標是否接近(在 margin 範圍內)
        if abs(y_height - seg_y) < margin:
            # X 範圍是否重疊
            if not (x_end_sec < seg_start or x_start_sec > seg_end):
                overlap = min(x_end_sec, seg_end) - max(x_start_sec, seg_start)
                collision_score += overlap / (x_end_sec - x_start_sec + 1)

    return collision_score

邏輯

  • 檢查新線段的水平部分是否與已有線段在同一高度±5%範圍內)
  • 計算 X 軸重疊的比例
  • 重疊越多,碰撞分數越高

線段與文字框碰撞檢測

# 檢查與文字框的碰撞
for box_x, box_y, box_w, box_h in text_boxes:
    # Y 座標是否在文字框範圍內
    if abs(y_height - box_y) < box_h / 2 + margin:
        # X 範圍是否穿過文字框
        box_left = box_x - box_w / 2
        box_right = box_x + box_w / 2
        if not (x_end_sec < box_left or x_start_sec > box_right):
            overlap = min(x_end_sec, box_right) - max(x_start_sec, box_left)
            collision_score += overlap / (x_end_sec - x_start_sec + 1) * 2  # 權重 x2

邏輯

  • 檢查線段是否穿過文字框的垂直範圍
  • 計算與文字框的 X 軸重疊
  • 文字框碰撞的權重是線段碰撞的2倍更嚴重

2. 最佳路徑選擇

def find_best_path_height(event_x_sec, label_x_sec, label_y, layer):
    is_upper = label_y > 0

    # 生成20個候選高度
    candidates = []
    if is_upper:
        # 上方:從 20% 到 90% (每次增加 3.5%)
        for i in range(20):
            ratio = 0.20 + (i * 0.035)
            candidates.append(ratio)
    else:
        # 下方:從 90% 到 20% (每次減少 3.5%)
        for i in range(20):
            ratio = 0.90 - (i * 0.035)
            candidates.append(ratio)

    # 計算每個高度的碰撞分數
    best_ratio = candidates[layer % len(candidates)]  # 默認值
    min_collision = float('inf')

    x_start = min(event_x_sec, label_x_sec)
    x_end = max(event_x_sec, label_x_sec)

    for ratio in candidates:
        test_y = label_y * ratio
        score = check_collision(x_start, x_end, test_y)
        if score < min_collision:
            min_collision = score
            best_ratio = ratio

    return best_ratio

邏輯

  1. 根據標籤位置(上方/下方生成20個候選高度
  2. 對每個候選高度計算碰撞分數
  3. 選擇碰撞分數最低的高度
  4. 如果所有高度碰撞分數相同都是0使用層級對應的默認高度

3. 實時追蹤系統

# 初始化追蹤列表
drawn_horizontal_segments = []  # [(x_start, x_end, y), ...]
text_boxes = []  # [(x_center, y_center, width, height), ...]

# 繪製後記錄
if not is_directly_above:
    drawn_horizontal_segments.append((x_start_sec, x_end_sec, mid_y))

text_boxes.append((label_x_sec, label_y, label_width_sec, label_height))

效果

  • 每繪製一條線段,立即記錄其位置
  • 每繪製一個文字框,立即記錄其範圍
  • 後續線條會避開已記錄的所有障礙物

效果對比

v4.1(靜態分層)

# 只根據層級計算高度
if is_upper_side:
    base_ratio = 0.25
    layer_offset = layer_group * 0.06
mid_y_ratio = base_ratio + layer_offset

 問題無法知道這個高度是否會碰撞

v5.0(動態碰撞檢測)

# 測試20個候選高度
for ratio in candidates:
    test_y = label_y * ratio
    score = check_collision(x_start, x_end, test_y)
    if score < min_collision:
        best_ratio = ratio

 優勢保證選擇碰撞最少的路徑

性能分析

時間複雜度

  • 單條線路徑選擇O(候選數 × (已繪線段數 + 文字框數))
  • 全部線條O(事件數 × 候選數 × 事件數) = O(20n²)
  • 實際情況:因為是按順序繪製,平均複雜度約為 O(10n²)

空間複雜度

  • 線段追蹤O(事件數)
  • 文字框追蹤O(事件數)
  • 總計O(事件數)

性能表現

  • 10 個事件:~2000 次碰撞檢測
  • 50 個事件:~50000 次碰撞檢測
  • 100 個事件:~200000 次碰撞檢測

優化空間

  • 可以使用空間索引R-tree降低到 O(n log n)
  • 可以減少候選數量從20降到10
  • 可以使用啟發式策略減少檢測次數

參數配置

# 碰撞檢測參數
margin = 0.05              # Y 軸碰撞容忍度5%
text_box_weight = 2.0      # 文字框碰撞權重x2

# 候選高度參數
candidates_count = 20      # 候選高度數量
upper_range = (0.20, 0.90) # 上方高度範圍 20%-90%
lower_range = (0.90, 0.20) # 下方高度範圍 90%-20%
step = 0.035               # 每次增減 3.5%

# 文字框估算參數
label_width_ratio = 0.15   # 文字框寬度 = 15% 時間軸
label_height = 0.3         # 文字框高度 = 0.3 單位

調整建議

如果仍有碰撞

  1. 增加候選高度數量
for i in range(30):  # 從 20 增加到 30
    ratio = 0.20 + (i * 0.024)  # 調整步長
  1. 增加碰撞容忍度
margin = 0.08  # 從 0.05 增加到 0.08
  1. 增加文字框尺寸估算
label_width_sec = time_range_seconds * 0.18  # 從 0.15 增加到 0.18
label_height = 0.4  # 從 0.3 增加到 0.4

如果性能太慢

  1. 減少候選數量
for i in range(10):  # 從 20 減少到 10
  1. 使用啟發式優先級
# 優先測試層級對應的高度附近的候選
priority_candidates = [
    candidates[layer % len(candidates)],  # 優先級1層級對應
    candidates[(layer-1) % len(candidates)],  # 優先級2相鄰
    candidates[(layer+1) % len(candidates)],  # 優先級3相鄰
    # ... 然後測試其他候選
]

視覺效果

碰撞檢測過程示意

測試候選高度 ratio=0.20 (20%):
  ████████████ 線段1 (已存在)
  ────────────────── 測試線段 ← 碰撞! score=0.8

測試候選高度 ratio=0.35 (35%):

  ────────────────── 測試線段 ← 無碰撞! score=0.0 ✓

  ████████████ 線段1 (已存在)

選擇 ratio=0.35,碰撞分數最低

文字框避讓示意

  ┌──────────┐
  │ 文字框A  │ (已存在)
  └──────────┘
      ↓
  ────────── 測試路徑1 ← 穿過文字框! score=1.5
      ↓

─────────── 測試路徑2 ← 避開文字框! score=0.0 ✓

      ●
   時間軸

版本改進總結

版本 方法 線條交錯 線條穿框 性能
v3.2 增加間距 嚴重 嚴重
v4.0 層級偏移 ⚠️ 存在 ⚠️ 偶爾
v4.1 鏡像分布 ⚠️ 仍有 ⚠️ 仍有
v5.0 碰撞檢測 最小 極少 中等

未來改進方向

1. 空間索引優化

使用 R-tree 或 KD-tree 加速碰撞檢測:

  • 當前O(n) 檢測每個障礙物
  • 優化後O(log n) 查詢相關障礙物

2. 貝茲曲線

使用平滑曲線代替直角折線:

  • 更自然的視覺效果
  • 更容易避開障礙物

3. A 路徑規劃*

使用圖搜索算法找到最優路徑:

  • 可以繞過複雜的障礙物布局
  • 保證找到全局最優解

4. 分組優化

對事件進行分組,組內使用相似的路徑高度:

  • 減少視覺混亂
  • 突出事件的邏輯關係

版本: v5.0 更新日期: 2025-11-05 作者: Claude AI

關鍵突破

靜態規則動態智能

  • v1-v4根據規則計算路徑 → 希望不會碰撞
  • v5:測試所有可能路徑 → 保證選擇最佳路徑

這是從被動避讓主動檢測的質的飛躍! 🚀