# 時間軸標籤避碰改進(v5.0) - 真正的碰撞預防系統 ## 新增改進(v5.0) ### 核心概念:從靜態分層到動態碰撞檢測 **v4.1 的問題**: - ❌ 只是根據層級靜態計算路徑高度 - ❌ 沒有真正檢測線條之間的碰撞 - ❌ 沒有檢測線條與文字框的碰撞 - ❌ 仍然會出現嚴重的重疊 **v5.0 的解決方案**: - ✅ **真正的碰撞檢測算法** - ✅ **動態路徑優化** - ✅ **20個候選高度,選擇最佳路徑** - ✅ **實時追蹤已繪製的線條和文字框** --- ## 技術實現 ### 1. **碰撞檢測算法** #### 線段與線段碰撞檢測 ```python 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 軸重疊的比例 - 重疊越多,碰撞分數越高 #### 線段與文字框碰撞檢測 ```python # 檢查與文字框的碰撞 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. **最佳路徑選擇** ```python 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. **實時追蹤系統** ```python # 初始化追蹤列表 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(靜態分層) ```python # 只根據層級計算高度 if is_upper_side: base_ratio = 0.25 layer_offset = layer_group * 0.06 mid_y_ratio = base_ratio + layer_offset ❌ 問題:無法知道這個高度是否會碰撞 ``` ### v5.0(動態碰撞檢測) ```python # 測試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) - 可以使用啟發式策略減少檢測次數 --- ## 參數配置 ```python # 碰撞檢測參數 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. **增加候選高度數量** ```python for i in range(30): # 從 20 增加到 30 ratio = 0.20 + (i * 0.024) # 調整步長 ``` 2. **增加碰撞容忍度** ```python margin = 0.08 # 從 0.05 增加到 0.08 ``` 3. **增加文字框尺寸估算** ```python label_width_sec = time_range_seconds * 0.18 # 從 0.15 增加到 0.18 label_height = 0.4 # 從 0.3 增加到 0.4 ``` ### 如果性能太慢 1. **減少候選數量** ```python for i in range(10): # 從 20 減少到 10 ``` 2. **使用啟發式優先級** ```python # 優先測試層級對應的高度附近的候選 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**:測試所有可能路徑 → **保證選擇最佳路徑** 這是從**被動避讓**到**主動檢測**的質的飛躍! 🚀