File size: 8,037 Bytes
e198f4f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import gradio as gr
import numpy as np
import matplotlib.pyplot as plt

# --- 核心計算與繪圖函數 (維持不變) ---
def plot_travel_times(v1, v2, h, x_max):
    """
    根據輸入的地層參數,計算並繪製直達波、反射波與折射波的走時曲線。
    圖表內的標籤為英文。
    """
    # 物理條件檢查:折射必須 V2 > V1
    if v2 <= v1:
        fig, ax = plt.subplots(figsize=(10, 6))
        # 即使沒有折射,我們仍然可以顯示直達波和反射波
        x = np.linspace(0, x_max, 500)
        t_direct = x / v1
        t_reflected = np.sqrt(x**2 + (2 * h)**2) / v1
        
        ax.plot(x, t_direct, 'b--', label='Direct Wave')
        ax.plot(x, t_reflected, 'm-.', label='Reflected Wave')
        
        ax.text(0.5, 0.5, 'V2 <= V1: Critical refraction does not occur.\nOnly Direct and Reflected waves are shown.',
                ha='center', va='center', fontsize=12, color='orange')
        ax.set_xlabel("Distance (m)")
        ax.set_ylabel("Travel Time (s)")
        ax.grid(True)
        ax.legend()
        ax.set_title("Travel-Time Curve (No Refraction)")
        results_md = "### ⚠️ 捷運停駛!\n高速層 V2 的速度必須比 V1 快,聰明的折射波選手才會登場喔!"
        return fig, results_md

    # 1. 計算關鍵物理量
    theta_c_rad = np.arcsin(v1 / v2)
    theta_c_deg = np.rad2deg(theta_c_rad)
    ti = (2 * h * np.cos(theta_c_rad)) / v1
    xc = 2 * h * np.sqrt((v2 + v1) / (v2 - v1))
    xcrit = 2 * h * np.tan(theta_c_rad)

    # 2. 準備繪圖數據
    x = np.linspace(0, x_max, 500)
    t_direct = x / v1
    t_refracted = (x / v2) + ti
    t_reflected = np.sqrt(x**2 + (2 * h)**2) / v1
    t_first_arrival = np.minimum(t_direct, t_refracted)
    
    # 3. 使用 Matplotlib 繪圖
    fig, ax = plt.subplots(figsize=(10, 6))
    
    ax.plot(x, t_direct, 'b--', label=f'Direct Wave (Sprinter)')
    ax.plot(x, t_refracted, 'g--', label=f'Refracted Wave (Subway Rider)')
    ax.plot(x, t_reflected, 'm-.', label='Reflected Wave (Bouncy Ball)')
    ax.plot(x, t_first_arrival, 'r-', linewidth=3, label='WINNER (First Arrival)')
    
    if xc < x_max:
        ax.axvline(x=xc, color='k', linestyle=':', label=f'Crossover Point = {xc:.1f} m')
        ax.plot(xc, xc/v1, 'ko') 

    if xcrit < x_max:
        tcrit = (xcrit / v2) + ti
        ax.axvline(x=xcrit, color='c', linestyle=':', label=f'Critical Point = {xcrit:.1f} m')
        ax.plot(xcrit, tcrit, 'co', markersize=8)

    ax.plot(0, ti, 'mo', markersize=8, label=f'Intercept Time = {ti*1000:.1f} ms')

    ax.set_title("The Great Seismic Race: T-X Plot", fontsize=16)
    ax.set_xlabel("Race Distance (m)", fontsize=12)
    ax.set_ylabel("Travel Time (s)", fontsize=12)
    
    ax.legend()
    ax.grid(True)
    ax.set_xlim(0, x_max)
    max_time = np.max([t_direct[-1], t_refracted[-1], t_reflected[-1]])
    ax.set_ylim(0, max_time * 1.1)
    plt.tight_layout()

    # 4. 準備輸出的說明文字 (生動有趣版)
    results_md = f"""
    ### 📣 賽況播報:認識你的選手!

    一場精彩的地底競速正在上演!三位選手的表現完全取決於您設計的賽道:

    - 🏃‍♂️ **直達波 (The Sprinter)**:
      - **策略**: 硬派跑法!只沿著地表(速度V1)直線衝刺。
      - **特性**: 它是最老實的選手,在短距離賽中通常保持領先。但隨著賽道變長,它可能會被下方的捷運選手超越。

    -  bouncing ball
      - **策略**: 垂直彈跳!它會潛入地底,在第一個地層邊界(深度h)觸地反彈,然後返回地表。
      - **特性**: 它的路徑像個 V 字形,因為需要向下再向上,所以它永遠無法贏得比賽(成為初達波),但它忠實地回報了地層的深度資訊!

    - 🚄 **折射波 (The Subway Rider)**:
      - **策略**: 抄捷徑!這位是「機會主義者」。它知道地底下有一條速度為 `V2` 的「高速捷運」。
      - **臨界點 (Critical Point)**: 在 **{xcrit:.1f} m** 處,是它能進入高速捷運的最早入口。它必須以一個完美的**臨界角 ({theta_c_deg:.2f}°)** 切入才能搭上車。
      - **截時 (Intercept Time)**: 搭捷運總要花時間下到月台吧?這 **{ti*1000:.1f} ms** 就是它為了走捷徑付出的「前期時間成本」。這個時間也巧妙地告訴我們月台有多深(`h`)!

    ---
    ### 🏁 勝負的交叉點:交越距離 (Crossover Distance)

    在 **{xc:.1f} m** 這個神奇的位置,短跑選手被捷運選手追上了!
    - **在此之前**: 直達波(短跑選手)最快。
    - **在此之後**: 折射波(捷運選手)才是永遠的贏家!
    這個交叉點是我們解開地層秘密最重要的線索之一。
    """
    return fig, results_md

# --- Gradio 介面設定 (生動有趣版) ---
with gr.Blocks(theme=gr.themes.Soft()) as demo:
    gr.Markdown("# 地心震波奇幻之旅:地底競速大賽 🌍🏆")
    gr.Markdown(
        """
        **準備好了嗎,地球探險家?** 你現在正操控著一台虛擬震測儀,就像是地球的「超音波」掃描機。我們的任務是透過發射震波,並聆聽它們的回音,來揭開地底深處的秘密。
        
        你將扮演三位賽跑選手——**直達波**、**反射波**與**折射波**——的總教練。調整下方的賽道參數,看看誰會在這場地底競速中脫穎而出!
        """
    )
    
    with gr.Row():
        with gr.Column(scale=1):
            gr.Markdown("### 🗺️ 設計你的賽道")
            v1_slider = gr.Slider(label="V1: 地表跑道速度 (m/s)", minimum=300, maximum=3000, value=800, step=50)
            v2_slider = gr.Slider(label="V2: 地底捷運速度 (m/s)", minimum=500, maximum=6000, value=2500, step=50)
            h_slider = gr.Slider(label="h: 捷運月台深度 (m)", minimum=5, maximum=100, value=20, step=1)
            xmax_slider = gr.Slider(label="賽道總長度 (m)", minimum=100, maximum=500, value=250, step=10)
            
            submit_btn = gr.Button("開賽!", variant="primary")
            
        with gr.Column(scale=2):
            gr.Markdown("### 📊 即時賽況 (T-X Plot)")
            plot_output = gr.Plot()
            
    with gr.Row():
        results_output = gr.Markdown("### 🎙️ 賽事分析\n請設計好賽道並點擊「開賽!」按鈕,查看專業分析。")

    submit_btn.click(
        fn=plot_travel_times,
        inputs=[v1_slider, v2_slider, h_slider, xmax_slider],
        outputs=[plot_output, results_output]
    )
    
    gr.Markdown(
        """
        ---
        ### 🚀 探險家任務
        試試看,你能解開這些謎題嗎?

        1.  **斜率的秘密**: 試著把 `V1` 或 `V2` 調快,看看發生了什麼?賽道變平坦了!沒錯,**速度越快,跑完相同距離的時間就越短,所以線條(斜率)就越平緩。**
        2.  **「截時」的詛咒**: 如果你把地層 `h` 加厚,就像把捷運月台蓋得更深。觀察折射波的綠線,是不是整條都往上平移了?這就是增加的「通勤時間」!
        3.  **拉開差距**: 怎麼讓短跑選手領先久一點?你可以把 `h` 加厚(起跑線拉遠),或是讓 `V1` 和 `V2` 速度更接近(捷運沒快多少),看看那個黑色的交叉點是不是就往右邊跑了?
        4.  **尋找捷運入口**: 觀察青色的「臨界點」。它永遠落在彈跳球選手(反射波)的路線上。這是物理定律給我們的一個有趣彩蛋!
        5.  **取消捷運**: 如果你讓地底捷運的速度 `V2` 比地表跑道 `V1` 還要慢呢?你會發現捷運選手直接罷工不出現了!因為這樣抄捷徑根本不划算。
        
        <footer>
            <p style="text-align:center; color:grey;">地球物理小教室 &copy; 2025 - 由 Gemini 根據課程文件生成</p>
        </footer>
        """
    )

if __name__ == "__main__":
    demo.launch()