cwadayi commited on
Commit
835dae2
·
verified ·
1 Parent(s): 5aa86a7

Create script.js

Browse files
Files changed (1) hide show
  1. script.js +194 -0
script.js ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Interactive Travel-Time Curve
2
+ const ttCanvas = document.getElementById('travelTimeCanvas');
3
+ const ttCtx = ttCanvas.getContext('2d');
4
+
5
+ let receiverX = 100;
6
+ let isDragging = false;
7
+
8
+ const Vp = 200; // P-wave velocity m/s
9
+ const Vs = 120; // S-wave velocity m/s
10
+ const Vr = 100; // Rayleigh-wave velocity m/s
11
+
12
+ function drawTravelTime() {
13
+ const w = ttCanvas.width;
14
+ const h = ttCanvas.height;
15
+ ttCtx.clearRect(0, 0, w, h);
16
+
17
+ // Part 1: Survey Diagram (Top half)
18
+ const groundY = h * 0.2;
19
+ ttCtx.fillStyle = '#8b4513';
20
+ ttCtx.fillRect(0, groundY, w, h*0.3);
21
+ ttCtx.fillStyle = '#add8e6';
22
+ ttCtx.fillRect(0, 0, w, groundY);
23
+
24
+ // Source
25
+ ttCtx.fillStyle = 'red';
26
+ ttCtx.beginPath();
27
+ ttCtx.arc(50, groundY, 10, 0, Math.PI * 2);
28
+ ttCtx.fill();
29
+ ttCtx.fillText('Source', 40, groundY - 15);
30
+
31
+ // Receiver
32
+ ttCtx.fillStyle = 'blue';
33
+ ttCtx.fillRect(receiverX - 5, groundY - 20, 10, 20);
34
+ ttCtx.fillText('Receiver', receiverX - 25, groundY - 25);
35
+
36
+ // Part 2: Travel-Time Graph (Bottom half)
37
+ const graphOriginX = 50;
38
+ const graphOriginY = h * 0.5;
39
+ const graphHeight = h * 0.45;
40
+ const graphWidth = w - 60;
41
+
42
+ // Axes
43
+ ttCtx.strokeStyle = 'black';
44
+ ttCtx.beginPath();
45
+ ttCtx.moveTo(graphOriginX, graphOriginY);
46
+ ttCtx.lineTo(graphOriginX, graphOriginY + graphHeight);
47
+ ttCtx.lineTo(graphOriginX + graphWidth, graphOriginY + graphHeight);
48
+ ttCtx.stroke();
49
+ ttCtx.fillText('Time (T)', graphOriginX - 40, graphOriginY + graphHeight / 2);
50
+ ttCtx.fillText('Distance (X)', graphOriginX + graphWidth / 2, graphOriginY + graphHeight + 20);
51
+
52
+ // Plot lines
53
+ const plotX = receiverX - 50;
54
+ const maxDist = w - 100;
55
+
56
+ function plotWave(v, color) {
57
+ ttCtx.strokeStyle = color;
58
+ ttCtx.lineWidth = 2;
59
+ ttCtx.beginPath();
60
+ ttCtx.moveTo(graphOriginX, graphOriginY + graphHeight);
61
+ // Slope = 1/V. T = X/V. Y_pixel = T * scale_Y. X_pixel = X
62
+ // We invert T axis, so T=0 is at the top
63
+ let endT = maxDist / v;
64
+ let scaleY = graphHeight / (maxDist/Vr); // Scale based on slowest wave
65
+ let endY = graphOriginY + graphHeight - (endT * scaleY);
66
+ let endX = graphOriginX + maxDist;
67
+ // The line in the graph is T vs X, so Y axis is T.
68
+ // Y = origin + H - (T*scale)
69
+ // T = X/V
70
+ // Y = origin + H - (X/V * scaleY)
71
+ ttCtx.moveTo(graphOriginX, graphOriginY + graphHeight);
72
+ ttCtx.lineTo(graphOriginX+maxDist, graphOriginY+graphHeight - (maxDist/v * scaleY) );
73
+ ttCtx.stroke();
74
+
75
+ // Plot point for current receiver
76
+ let currentT = plotX / v;
77
+ let pointY = graphOriginY + graphHeight - (currentT * scaleY);
78
+ ttCtx.fillStyle = color;
79
+ ttCtx.beginPath();
80
+ ttCtx.arc(graphOriginX + plotX, pointY, 5, 0, Math.PI * 2);
81
+ ttCtx.fill();
82
+ }
83
+
84
+ plotWave(Vp, '#d90429');
85
+ plotWave(Vs, '#0077b6');
86
+ plotWave(Vr, '#2d6a4f');
87
+
88
+ ttCtx.lineWidth = 1;
89
+ }
90
+
91
+ ttCanvas.addEventListener('mousedown', (e) => {
92
+ isDragging = true;
93
+ });
94
+ ttCanvas.addEventListener('mouseup', () => {
95
+ isDragging = false;
96
+ });
97
+ ttCanvas.addEventListener('mouseleave', () => {
98
+ isDragging = false;
99
+ });
100
+ ttCanvas.addEventListener('mousemove', (e) => {
101
+ if (isDragging) {
102
+ const rect = ttCanvas.getBoundingClientRect();
103
+ let x = e.clientX - rect.left;
104
+ if (x > 50 && x < ttCanvas.width - 50) {
105
+ receiverX = x;
106
+ drawTravelTime();
107
+ }
108
+ }
109
+ });
110
+
111
+ // Interactive Refraction
112
+ const refCanvas = document.getElementById('refractionCanvas');
113
+ const refCtx = refCanvas.getContext('2d');
114
+ const slider = document.getElementById('v2-slider');
115
+ const v2ValueSpan = document.getElementById('v2-value');
116
+
117
+ const V1 = 1000;
118
+
119
+ function drawRefraction() {
120
+ const w = refCanvas.width;
121
+ const h = refCanvas.height;
122
+ refCtx.clearRect(0, 0, w, h);
123
+
124
+ const V2 = slider.value;
125
+ v2ValueSpan.textContent = V2;
126
+
127
+ const interfaceY = h / 2;
128
+
129
+ // Layers
130
+ refCtx.fillStyle = '#f0e68c'; // Layer 1
131
+ refCtx.fillRect(0, 0, w, interfaceY);
132
+ refCtx.fillStyle = '#d2b48c'; // Layer 2
133
+ refCtx.fillRect(0, interfaceY, w, h);
134
+ refCtx.fillText(`V₁ = ${V1} m/s`, 10, 20);
135
+ refCtx.fillText(`V₂ = ${V2} m/s`, 10, interfaceY + 20);
136
+
137
+ // Incident Ray
138
+ const source = { x: 100, y: 0 };
139
+ const incidentPoint = { x: 250, y: interfaceY };
140
+ const angle1 = Math.atan((incidentPoint.x - source.x) / incidentPoint.y);
141
+
142
+ refCtx.strokeStyle = 'black';
143
+ refCtx.lineWidth = 2;
144
+ refCtx.beginPath();
145
+ refCtx.moveTo(source.x, source.y);
146
+ refCtx.lineTo(incidentPoint.x, incidentPoint.y);
147
+ refCtx.stroke();
148
+
149
+ // Refracted Ray (Snell's Law: sin(theta1)/V1 = sin(theta2)/V2)
150
+ const sin_theta2 = (V2 / V1) * Math.sin(angle1);
151
+ if (sin_theta2 < 1) { // No critical refraction yet
152
+ const angle2 = Math.asin(sin_theta2);
153
+ const endX = incidentPoint.x + (h / 2) * Math.tan(angle2);
154
+ const endY = h;
155
+
156
+ refCtx.beginPath();
157
+ refCtx.moveTo(incidentPoint.x, incidentPoint.y);
158
+ refCtx.lineTo(endX, endY);
159
+ refCtx.stroke();
160
+
161
+ // Draw angles
162
+ refCtx.strokeStyle = 'rgba(0,0,0,0.5)';
163
+ refCtx.lineWidth = 1;
164
+ refCtx.beginPath();
165
+ refCtx.moveTo(incidentPoint.x, incidentPoint.y-30);
166
+ refCtx.lineTo(incidentPoint.x, incidentPoint.y+30);
167
+ refCtx.stroke();
168
+ refCtx.beginPath();
169
+ refCtx.arc(incidentPoint.x, incidentPoint.y, 20, Math.PI/2 - angle1, Math.PI/2);
170
+ refCtx.stroke();
171
+ refCtx.fillText('θ₁', incidentPoint.x - 25, incidentPoint.y-5);
172
+
173
+ refCtx.beginPath();
174
+ refCtx.arc(incidentPoint.x, incidentPoint.y, 20, Math.PI/2, Math.PI/2 + angle2);
175
+ refCtx.stroke();
176
+ refCtx.fillText('θ₂', incidentPoint.x - 25, incidentPoint.y+20);
177
+
178
+ } else { // Critical refraction
179
+ refCtx.beginPath();
180
+ refCtx.moveTo(incidentPoint.x, incidentPoint.y);
181
+ refCtx.lineTo(w, incidentPoint.y);
182
+ refCtx.stroke();
183
+ refCtx.fillStyle = 'red';
184
+ refCtx.fillText('Critical Refraction!', incidentPoint.x + 10, incidentPoint.y-10);
185
+ }
186
+ }
187
+
188
+ slider.addEventListener('input', drawRefraction);
189
+
190
+ // Initial draws
191
+ window.onload = () => {
192
+ drawTravelTime();
193
+ drawRefraction();
194
+ };