Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Interactive Drude-Smith Model Visualization</title> | |
| <!-- Tailwind CSS --> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <!-- Plotly.js --> | |
| <script src='https://cdn.plot.ly/plotly-latest.min.js'></script> | |
| <!-- Font Awesome for Icons (Optional, but nice for UI) --> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer" /> | |
| <style> | |
| /* Custom Styles (if needed) */ | |
| .slider-thumb::-webkit-slider-thumb { | |
| appearance: none; /* Override default look */ | |
| width: 16px; /* Set a specific slider handle width */ | |
| height: 16px; /* Set a specific slider handle height */ | |
| background: #3b82f6; /* Blue background */ | |
| border-radius: 50%; /* Circular thumb */ | |
| cursor: pointer; /* Cursor on hover */ | |
| } | |
| .slider-thumb::-moz-range-thumb { | |
| width: 16px; | |
| height: 16px; | |
| background: #3b82f6; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| border: none; /* Remove Firefox border */ | |
| } | |
| /* Style for active state */ | |
| .tab-button.active { | |
| border-color: #3b82f6; | |
| color: #3b82f6; | |
| font-weight: 600; | |
| } | |
| /* Minor layout adjustments */ | |
| #plotDiv { | |
| min-height: 500px; /* Ensure plot has enough vertical space */ | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gradient-to-br from-gray-100 to-blue-100 font-sans antialiased"> | |
| <div class="container mx-auto p-4 md:p-8"> | |
| <header class="text-center mb-8"> | |
| <h1 class="text-3xl md:text-4xl font-bold text-gray-800 mb-2">Drude-Smith Model Visualization</h1> | |
| <p class="text-md md:text-lg text-gray-600">Explore the complex conductivity σ*(ω) = σ'(ω) + iσ''(ω)</p> | |
| </header> | |
| <div class="flex flex-col md:flex-row gap-8"> | |
| <!-- Controls Panel --> | |
| <aside class="md:w-1/3 lg:w-1/4 bg-white p-6 rounded-xl shadow-lg border border-gray-200"> | |
| <h2 class="text-xl font-semibold text-gray-700 mb-6 border-b pb-2">Parameters</h2> | |
| <div class="space-y-5"> | |
| <!-- DC Conductivity (sigma0) --> | |
| <div> | |
| <label for="sigma0" class="flex items-center text-sm font-medium text-gray-700 mb-1"> | |
| <i class="fas fa-plug fa-fw mr-2 text-blue-500"></i> DC Conductivity (σ₀) <span class="ml-auto font-mono text-blue-600" id="sigma0Value">1000</span> S/m | |
| </label> | |
| <input type="range" id="sigma0" min="1" max="10000" value="1000" step="1" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-thumb range-lg dark:bg-gray-700"> | |
| </div> | |
| <!-- Relaxation Time (tau0) --> | |
| <div> | |
| <label for="tau0" class="flex items-center text-sm font-medium text-gray-700 mb-1"> | |
| <i class="fas fa-stopwatch fa-fw mr-2 text-green-500"></i> Relaxation Time (τ₀) <span class="ml-auto font-mono text-green-600" id="tau0Value">20</span> fs | |
| </label> | |
| <input type="range" id="tau0" min="1" max="200" value="20" step="1" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-thumb range-lg dark:bg-gray-700"> | |
| </div> | |
| <!-- Backscattering Parameter (c1) --> | |
| <div> | |
| <label for="c1" class="flex items-center text-sm font-medium text-gray-700 mb-1"> | |
| <i class="fas fa-arrow-left fa-fw mr-2 text-red-500"></i> 1st Order Parameter (c₁) <span class="ml-auto font-mono text-red-600" id="c1Value">-0.5</span> | |
| </label> | |
| <input type="range" id="c1" min="-1" max="0" value="-0.5" step="0.01" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-thumb range-lg dark:bg-gray-700"> | |
| <p class="text-xs text-gray-500 mt-1">Controls first scattering event memory (-1: full backscattering, 0: Drude).</p> | |
| </div> | |
| <!-- Second Order Parameter (c2) --> | |
| <div> | |
| <label for="c2" class="flex items-center text-sm font-medium text-gray-700 mb-1"> | |
| <i class="fas fa-arrows-left-right fa-fw mr-2 text-purple-500"></i> 2nd Order Parameter (c₂) <span class="ml-auto font-mono text-purple-600" id="c2Value">0.1</span> | |
| </label> | |
| <input type="range" id="c2" min="-0.5" max="0.5" value="0.1" step="0.01" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-thumb range-lg dark:bg-gray-700"> | |
| <p class="text-xs text-gray-500 mt-1">Introduces higher-order scattering effects.</p> | |
| </div> | |
| <!-- Model Selection --> | |
| <div class="pt-4 border-t mt-6"> | |
| <h3 class="text-sm font-medium text-gray-700 mb-2">Models to Display:</h3> | |
| <div class="flex flex-wrap gap-2"> | |
| <label class="inline-flex items-center cursor-pointer"> | |
| <input type="checkbox" id="showDrude" class="form-checkbox h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" checked> | |
| <span class="ml-2 text-sm text-gray-600">Drude (c₁=0, c₂=0)</span> | |
| </label> | |
| <label class="inline-flex items-center cursor-pointer"> | |
| <input type="checkbox" id="showDS1" class="form-checkbox h-4 w-4 text-red-600 border-gray-300 rounded focus:ring-red-500" checked> | |
| <span class="ml-2 text-sm text-gray-600">Drude-Smith 1st (c₁)</span> | |
| </label> | |
| <label class="inline-flex items-center cursor-pointer"> | |
| <input type="checkbox" id="showDS2" class="form-checkbox h-4 w-4 text-purple-600 border-gray-300 rounded focus:ring-purple-500"> | |
| <span class="ml-2 text-sm text-gray-600">Drude-Smith 2nd (c₁, c₂)</span> | |
| </label> | |
| </div> | |
| </div> | |
| <!-- Plot Type Selection --> | |
| <div class="pt-4 border-t mt-6"> | |
| <h3 class="text-sm font-medium text-gray-700 mb-2">Plot Type:</h3> | |
| <div class="flex border border-gray-300 rounded-md overflow-hidden"> | |
| <button id="plotConductivityBtn" class="tab-button flex-1 py-1 px-3 text-sm text-gray-600 border-r border-gray-300 focus:outline-none active"> | |
| Conductivity (σ) | |
| </button> | |
| <button id="plotPermittivityBtn" class="tab-button flex-1 py-1 px-3 text-sm text-gray-600 focus:outline-none"> | |
| Permittivity (ε) | |
| </button> | |
| </div> | |
| <div id="epsilonInfControls" class="mt-3 hidden"> | |
| <label for="epsilonInf" class="flex items-center text-sm font-medium text-gray-700 mb-1"> | |
| <i class="fas fa-infinity fa-fw mr-2 text-indigo-500"></i> High-Freq. Permittivity (ε<sub>∞</sub>) <span class="ml-auto font-mono text-indigo-600" id="epsilonInfValue">1.0</span> | |
| </label> | |
| <input type="range" id="epsilonInf" min="0.1" max="20" value="1.0" step="0.1" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-thumb range-lg dark:bg-gray-700"> | |
| </div> | |
| </div> | |
| </div> | |
| </aside> | |
| <!-- Plot Area --> | |
| <main class="md:w-2/3 lg:w-3/4 bg-white p-4 rounded-xl shadow-lg border border-gray-200 flex flex-col"> | |
| <div id="plotDiv" class="flex-grow"> | |
| <!-- Plotly chart will be rendered here --> | |
| </div> | |
| <div class="text-xs text-center text-gray-500 mt-2"> | |
| Frequency range: 1 GHz to 100 THz (log scale) | |
| </div> | |
| </main> | |
| </div> | |
| <!-- Explanation Section (Optional) --> | |
| <section class="mt-10 p-6 bg-white rounded-xl shadow-lg border border-gray-200"> | |
| <h3 class="text-lg font-semibold text-gray-700 mb-3">Model Explanation</h3> | |
| <div class="text-sm text-gray-600 space-y-2"> | |
| <p>The Drude model describes free electron conductivity. The Drude-Smith model extends this by incorporating carrier localization or preferential back-scattering, common in disordered materials or nanostructures.</p> | |
| <p><strong>Complex Conductivity:</strong> σ*(ω) = σ'(ω) + iσ''(ω)</p> | |
| <p><strong>Drude Model:</strong> σ*(ω) = σ₀ / (1 - iωτ₀)</p> | |
| <p><strong>Drude-Smith (1st Order):</strong> σ*(ω) = [σ₀ / (1 - iωτ₀)] * [1 + c₁ / (1 - iωτ₀)]</p> | |
| <p><strong>Drude-Smith (2nd Order):</strong> σ*(ω) = [σ₀ / (1 - iωτ₀)] * [1 + c₁ / (1 - iωτ₀) + c₂ / (1 - iωτ₀)²]</p> | |
| <p>Where: σ₀ is DC conductivity, τ₀ is relaxation time, ω is angular frequency (2πf), c₁ (-1 ≤ c₁ ≤ 0) is the first scattering persistence parameter, and c₂ accounts for subsequent scattering memory.</p> | |
| <p><strong>Complex Permittivity:</strong> ε*(ω) = ε<sub>∞</sub> + i σ*(ω) / (ωε₀), where ε<sub>∞</sub> is the high-frequency permittivity constant and ε₀ is the vacuum permittivity (≈ 8.854 × 10⁻¹² F/m).</p> | |
| </div> | |
| </section> | |
| </div> | |
| <script> | |
| // --- DOM Elements --- | |
| const sigma0Slider = document.getElementById('sigma0'); | |
| const tau0Slider = document.getElementById('tau0'); | |
| const c1Slider = document.getElementById('c1'); | |
| const c2Slider = document.getElementById('c2'); | |
| const epsilonInfSlider = document.getElementById('epsilonInf'); | |
| const sigma0ValueSpan = document.getElementById('sigma0Value'); | |
| const tau0ValueSpan = document.getElementById('tau0Value'); | |
| const c1ValueSpan = document.getElementById('c1Value'); | |
| const c2ValueSpan = document.getElementById('c2Value'); | |
| const epsilonInfValueSpan = document.getElementById('epsilonInfValue'); | |
| const showDrudeCheckbox = document.getElementById('showDrude'); | |
| const showDS1Checkbox = document.getElementById('showDS1'); | |
| const showDS2Checkbox = document.getElementById('showDS2'); | |
| const plotConductivityBtn = document.getElementById('plotConductivityBtn'); | |
| const plotPermittivityBtn = document.getElementById('plotPermittivityBtn'); | |
| const epsilonInfControls = document.getElementById('epsilonInfControls'); | |
| const plotDiv = document.getElementById('plotDiv'); | |
| // --- Constants --- | |
| const EPSILON0 = 8.854187817e-12; // Vacuum permittivity (F/m) | |
| // --- State --- | |
| let currentPlotType = 'conductivity'; // 'conductivity' or 'permittivity' | |
| // --- Complex Number Math --- | |
| // Simple complex number representation: { re: real_part, im: imaginary_part } | |
| function complexAdd(z1, z2) { | |
| return { re: z1.re + z2.re, im: z1.im + z2.im }; | |
| } | |
| function complexSubtract(z1, z2) { | |
| return { re: z1.re - z2.re, im: z1.im - z2.im }; | |
| } | |
| function complexMultiply(z1, z2) { | |
| return { | |
| re: z1.re * z2.re - z1.im * z2.im, | |
| im: z1.re * z2.im + z1.im * z2.re | |
| }; | |
| } | |
| function complexDivide(z1, z2) { | |
| const denominator = z2.re * z2.re + z2.im * z2.im; | |
| if (denominator === 0) return { re: NaN, im: NaN }; // Avoid division by zero | |
| return { | |
| re: (z1.re * z2.re + z1.im * z2.im) / denominator, | |
| im: (z1.im * z2.re - z1.re * z2.im) / denominator | |
| }; | |
| } | |
| function complexScalarMultiply(scalar, z) { | |
| return { re: scalar * z.re, im: scalar * z.im }; | |
| } | |
| // --- Model Calculations --- | |
| function calculateModels(omega, sigma0, tau0, c1, c2) { | |
| const wtau = omega * tau0; | |
| const denominator_term = { re: 1, im: -wtau }; // (1 - iωτ₀) | |
| // Drude Model Calculation | |
| const sigma_drude = complexDivide({ re: sigma0, im: 0 }, denominator_term); | |
| // Drude-Smith 1st Order | |
| // σ_ds1 = σ_drude * (1 + c1 / (1 - iωτ₀)) | |
| // = σ_drude * (1 + c1 * conj(denominator_term) / |denominator_term|^2) | |
| // Let term1 = c1 / (1 - iωτ₀) | |
| const term1_ds1 = complexDivide({ re: c1, im: 0 }, denominator_term); | |
| const bracket_ds1 = complexAdd({ re: 1, im: 0 }, term1_ds1); | |
| const sigma_ds1 = complexMultiply(sigma_drude, bracket_ds1); | |
| // Drude-Smith 2nd Order | |
| // σ_ds2 = σ_ds1 + σ_drude * (c2 / (1 - iωτ₀)^2) | |
| // Let term2 = c2 / (1 - iωτ₀)^2 | |
| const denominator_term_sq = complexMultiply(denominator_term, denominator_term); | |
| const term2_ds2 = complexDivide({ re: c2, im: 0 }, denominator_term_sq); | |
| const addition_ds2 = complexMultiply(sigma_drude, term2_ds2); | |
| const sigma_ds2 = complexAdd(sigma_ds1, addition_ds2); | |
| return { | |
| drude: sigma_drude, | |
| ds1: sigma_ds1, | |
| ds2: sigma_ds2 | |
| }; | |
| } | |
| // --- Plotting --- | |
| function updatePlot() { | |
| // Get current parameter values | |
| const sigma0 = parseFloat(sigma0Slider.value); | |
| const tau0_fs = parseFloat(tau0Slider.value); | |
| const c1 = parseFloat(c1Slider.value); | |
| const c2 = parseFloat(c2Slider.value); | |
| const epsilonInf = parseFloat(epsilonInfSlider.value); | |
| // Convert tau0 from fs to s | |
| const tau0 = tau0_fs * 1e-15; | |
| // Update value displays | |
| sigma0ValueSpan.textContent = sigma0.toFixed(0); | |
| tau0ValueSpan.textContent = tau0_fs.toFixed(0); | |
| c1ValueSpan.textContent = c1.toFixed(2); | |
| c2ValueSpan.textContent = c2.toFixed(2); | |
| epsilonInfValueSpan.textContent = epsilonInf.toFixed(1); | |
| // Frequency range (log scale) | |
| const numPoints = 200; | |
| const freq_min_hz = 1e9; // 1 GHz | |
| const freq_max_hz = 100e12; // 100 THz | |
| const log_freq_min = Math.log10(freq_min_hz); | |
| const log_freq_max = Math.log10(freq_max_hz); | |
| const freq_hz = []; | |
| const omega = []; | |
| for (let i = 0; i < numPoints; i++) { | |
| const log_f = log_freq_min + (log_freq_max - log_freq_min) * i / (numPoints - 1); | |
| const f = Math.pow(10, log_f); | |
| freq_hz.push(f); | |
| omega.push(2 * Math.PI * f); | |
| } | |
| // Calculate model data | |
| const sigma_drude_real = []; const sigma_drude_imag = []; | |
| const sigma_ds1_real = []; const sigma_ds1_imag = []; | |
| const sigma_ds2_real = []; const sigma_ds2_imag = []; | |
| const epsilon_drude_real = []; const epsilon_drude_imag = []; | |
| const epsilon_ds1_real = []; const epsilon_ds1_imag = []; | |
| const epsilon_ds2_real = []; const epsilon_ds2_imag = []; | |
| for (let i = 0; i < numPoints; i++) { | |
| const w = omega[i]; | |
| const models = calculateModels(w, sigma0, tau0, c1, c2); | |
| // Conductivity Data | |
| sigma_drude_real.push(models.drude.re); | |
| sigma_drude_imag.push(models.drude.im); | |
| sigma_ds1_real.push(models.ds1.re); | |
| sigma_ds1_imag.push(models.ds1.im); | |
| sigma_ds2_real.push(models.ds2.re); | |
| sigma_ds2_imag.push(models.ds2.im); | |
| // Permittivity Data: ε*(ω) = ε_inf + i * σ*(ω) / (ω * ε₀) | |
| // i * σ*(ω) = i * (σ' + iσ'') = iσ' - σ'' = -σ'' + iσ' | |
| // So, ε*(ω) = ε_inf + (-σ'' + iσ') / (ω * ε₀) | |
| // ε'(ω) = ε_inf - σ''(ω) / (ω * ε₀) | |
| // ε''(ω) = σ'(ω) / (ω * ε₀) | |
| const factor = w * EPSILON0; | |
| if (factor > 1e-20) { // Avoid division by zero / very small numbers at low freq if w=0 | |
| epsilon_drude_real.push(epsilonInf - models.drude.im / factor); | |
| epsilon_drude_imag.push(models.drude.re / factor); | |
| epsilon_ds1_real.push(epsilonInf - models.ds1.im / factor); | |
| epsilon_ds1_imag.push(models.ds1.re / factor); | |
| epsilon_ds2_real.push(epsilonInf - models.ds2.im / factor); | |
| epsilon_ds2_imag.push(models.ds2.re / factor); | |
| } else { // Handle low frequency limit or w=0 case | |
| // At w=0, sigma'' -> 0, sigma' -> sigma0. Epsilon'' diverges (sigma'/w*eps0) | |
| // Epsilon' should approach some limit related to sigma''/w, which involves derivatives, or simply becomes very large negative if sigma'' starts positive. | |
| // For practical plotting, push NaN or handle appropriately. Let's push NaN for simplicity near zero freq. | |
| epsilon_drude_real.push(NaN); epsilon_drude_imag.push(NaN); | |
| epsilon_ds1_real.push(NaN); epsilon_ds1_imag.push(NaN); | |
| epsilon_ds2_real.push(NaN); epsilon_ds2_imag.push(NaN); | |
| } | |
| } | |
| // Prepare Plotly traces | |
| const traces = []; | |
| const showDrude = showDrudeCheckbox.checked; | |
| const showDS1 = showDS1Checkbox.checked; | |
| const showDS2 = showDS2Checkbox.checked; | |
| const freq_display = freq_hz.map(f => f / 1e9); // Display frequency in GHz | |
| if (currentPlotType === 'conductivity') { | |
| if (showDrude) { | |
| traces.push({ x: freq_display, y: sigma_drude_real, mode: 'lines', name: "σ' (Drude)", line: { color: 'rgb(59, 130, 246)', width: 2 } }); // blue-500 | |
| traces.push({ x: freq_display, y: sigma_drude_imag, mode: 'lines', name: "σ'' (Drude)", line: { color: 'rgb(59, 130, 246)', dash: 'dash', width: 2 } }); | |
| } | |
| if (showDS1) { | |
| traces.push({ x: freq_display, y: sigma_ds1_real, mode: 'lines', name: "σ' (DS1)", line: { color: 'rgb(239, 68, 68)', width: 2 } }); // red-500 | |
| traces.push({ x: freq_display, y: sigma_ds1_imag, mode: 'lines', name: "σ'' (DS1)", line: { color: 'rgb(239, 68, 68)', dash: 'dash', width: 2 } }); | |
| } | |
| if (showDS2) { | |
| traces.push({ x: freq_display, y: sigma_ds2_real, mode: 'lines', name: "σ' (DS2)", line: { color: 'rgb(168, 85, 247)', width: 2 } }); // purple-500 | |
| traces.push({ x: freq_display, y: sigma_ds2_imag, mode: 'lines', name: "σ'' (DS2)", line: { color: 'rgb(168, 85, 247)', dash: 'dash', width: 2 } }); | |
| } | |
| } else { // Permittivity | |
| if (showDrude) { | |
| traces.push({ x: freq_display, y: epsilon_drude_real, mode: 'lines', name: "ε' (Drude)", line: { color: 'rgb(59, 130, 246)', width: 2 } }); | |
| traces.push({ x: freq_display, y: epsilon_drude_imag, mode: 'lines', name: "ε'' (Drude)", line: { color: 'rgb(59, 130, 246)', dash: 'dash', width: 2 } }); | |
| } | |
| if (showDS1) { | |
| traces.push({ x: freq_display, y: epsilon_ds1_real, mode: 'lines', name: "ε' (DS1)", line: { color: 'rgb(239, 68, 68)', width: 2 } }); | |
| traces.push({ x: freq_display, y: epsilon_ds1_imag, mode: 'lines', name: "ε'' (DS1)", line: { color: 'rgb(239, 68, 68)', dash: 'dash', width: 2 } }); | |
| } | |
| if (showDS2) { | |
| traces.push({ x: freq_display, y: epsilon_ds2_real, mode: 'lines', name: "ε' (DS2)", line: { color: 'rgb(168, 85, 247)', width: 2 } }); | |
| traces.push({ x: freq_display, y: epsilon_ds2_imag, mode: 'lines', name: "ε'' (DS2)", line: { color: 'rgb(168, 85, 247)', dash: 'dash', width: 2 } }); | |
| } | |
| } | |
| // Plotly Layout | |
| const yAxisTitle = currentPlotType === 'conductivity' ? 'Conductivity (S/m)' : 'Relative Permittivity'; | |
| const layout = { | |
| title: `Complex ${currentPlotType.charAt(0).toUpperCase() + currentPlotType.slice(1)} vs Frequency`, | |
| xaxis: { | |
| title: 'Frequency (GHz)', | |
| type: 'log', | |
| autorange: true, | |
| // range: [Math.log10(freq_min_hz / 1e9), Math.log10(freq_max_hz / 1e9)], // Use GHz for range | |
| tickformat: '.1s', // Format ticks like 1G, 10G, 100G, 1T, 10T etc. | |
| exponentformat: 'SI' | |
| }, | |
| yaxis: { | |
| title: yAxisTitle, | |
| autorange: true, | |
| exponentformat: 'SI' // Use SI prefix for large/small numbers | |
| // Consider 'type: log' for y-axis if needed, but linear shows negative values better. | |
| }, | |
| margin: { l: 60, r: 30, b: 50, t: 50 }, | |
| hovermode: 'x unified', // Show hover info for all traces at a given x | |
| legend: { | |
| // x: 1.05, | |
| // y: 1, | |
| traceorder: 'normal', // Keep legend order same as trace order | |
| font: { | |
| size: 10 | |
| }, | |
| bgcolor: 'rgba(255,255,255,0.8)' // Semi-transparent background | |
| }, | |
| showlegend: true, | |
| plot_bgcolor: 'rgba(249, 250, 251, 0.8)', // Light background inside plot area | |
| paper_bgcolor: 'rgba(255, 255, 255, 0)', // Transparent background outside plot area | |
| }; | |
| // Use Plotly.react for efficient updates | |
| Plotly.react(plotDiv, traces, layout, {responsive: true}); | |
| } | |
| // --- Event Listeners --- | |
| [sigma0Slider, tau0Slider, c1Slider, c2Slider, epsilonInfSlider].forEach(slider => { | |
| slider.addEventListener('input', updatePlot); | |
| }); | |
| [showDrudeCheckbox, showDS1Checkbox, showDS2Checkbox].forEach(checkbox => { | |
| checkbox.addEventListener('change', updatePlot); | |
| }); | |
| plotConductivityBtn.addEventListener('click', () => { | |
| if (currentPlotType !== 'conductivity') { | |
| currentPlotType = 'conductivity'; | |
| plotConductivityBtn.classList.add('active', 'bg-blue-50'); | |
| plotPermittivityBtn.classList.remove('active', 'bg-blue-50'); | |
| epsilonInfControls.classList.add('hidden'); | |
| updatePlot(); | |
| } | |
| }); | |
| plotPermittivityBtn.addEventListener('click', () => { | |
| if (currentPlotType !== 'permittivity') { | |
| currentPlotType = 'permittivity'; | |
| plotPermittivityBtn.classList.add('active', 'bg-blue-50'); | |
| plotConductivityBtn.classList.remove('active', 'bg-blue-50'); | |
| epsilonInfControls.classList.remove('hidden'); | |
| updatePlot(); | |
| } | |
| }); | |
| // --- Initial Plot --- | |
| // Ensure active class is set correctly on initial load | |
| plotConductivityBtn.classList.add('active', 'bg-blue-50'); | |
| updatePlot(); | |
| // Optional: Resize plot on window resize | |
| window.addEventListener('resize', () => { | |
| Plotly.Plots.resize(plotDiv); | |
| }); | |
| </script> | |
| </body> | |
| </html> |