UISlider点击轴改变滑块位置

本文介绍了一个自定义UISlider类MySlider的实现方法。该类重写了触摸开始的方法,使得可以通过触摸屏幕来改变滑块的位置。代码展示了如何计算触摸位置对应的值并设置滑块的新值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



#import <Foundation/Foundation.h>

@interface MySlider : UISlider {
}

@end



#import "MySlider.h"

@implementation MySlider

- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];

return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGRect t = [self trackRectForBounds: [self bounds]];
float v = [self minimumValue] + ([[touches anyObject] locationInView: self].x - t.origin.x - 4.0) * (([self maximumValue]-[self minimumValue]) / (t.size.width - 8.0));
[self setValue: v];
[super touchesBegan: touches withEvent: event];
}


@end

classdef AudioProcessor < matlab.apps.AppBase % 属性定义(存储音频数据和参数) properties (Access = public) UIFigure matlab.ui.Figure ImportButton matlab.ui.control.Button ProcessButton matlab.ui.control.Button SaveButton matlab.ui.control.Button GainSlider matlab.ui.control.Slider GainLabel matlab.ui.control.Label DelaySlider matlab.ui.control.Slider DelayLabel matlab.ui.control.Label EchoAmpSlider matlab.ui.control.Slider EchoAmpLabel matlab.ui.control.Label NoiseSlider matlab.ui.control.Slider NoiseLabel matlab.ui.control.Label WaveformAxes1 matlab.ui.control.UIAxes WaveformAxes2 matlab.ui.control.UIAxes SpectrumAxes1 matlab.ui.control.UIAxes SpectrumAxes2 matlab.ui.control.UIAxes StatusLabel matlab.ui.control.Label OriginalAudio % 原始音频数据 ProcessedAudio % 处理后音频数据 SampleRate % 采样率 IsMono % 是否单声道 end % 构造函数(初始化界面) methods (Access = private) function createComponents(app) % 创建主窗口 app.UIFigure = uifigure('Visible', 'off'); app.UIFigure.Position = [100, 100, 800, 600]; app.UIFigure.Name = '音频处理系统'; % 导入按钮 app.ImportButton = uibutton(app.UIFigure, 'push'); app.ImportButton.Position = [20, 540, 100, 30]; app.ImportButton.Text = '导入音频'; app.ImportButton.ButtonPushedFcn = @(src, event) app.ImportButtonPushed(src, event); % 处理按钮 app.ProcessButton = uibutton(app.UIFigure, 'push'); app.ProcessButton.Position = [140, 540, 100, 30]; app.ProcessButton.Text = '开始处理'; app.ProcessButton.ButtonPushedFcn = @(src, event) app.ProcessButtonPushed(src, event); app.ProcessButton.Enable = 'off'; % 保存按钮 app.SaveButton = uibutton(app.UIFigure, 'push'); app.SaveButton.Position = [260, 540, 100, 30]; app.SaveButton.Text = '保存结果'; app.SaveButton.ButtonPushedFcn = @(src, event) app.SaveButtonPushed(src, event); app.SaveButton.Enable = 'off'; % 增益滑块 app.GainLabel = uilabel(app.UIFigure); app.GainLabel.Position = [400, 545, 50, 22]; app.GainLabel.Text = '增益(dB)'; app.GainSlider = uislider(app.UIFigure); app.GainSlider.Position = [460, 540, 200, 30]; app.GainSlider.Limits = [0, 12]; app.GainSlider.Value = 6; app.GainSlider.MajorTicks = [0, 6, 12]; app.GainSlider.MajorTickLabels = {'0', '6', '12'}; % 延迟滑块 app.DelayLabel = uilabel(app.UIFigure); app.DelayLabel.Position = [400, 515, 50, 22]; app.DelayLabel.Text = '延迟(ms)'; app.DelaySlider = uislider(app.UIFigure); app.DelaySlider.Position = [460, 510, 200, 30]; app.DelaySlider.Limits = [0, 500]; app.DelaySlider.Value = 300; app.DelaySlider.MajorTicks = [0, 250, 500]; app.DelaySlider.MajorTickLabels = {'0', '250', '500'}; % 回音衰减滑块 app.EchoAmpLabel = uilabel(app.UIFigure); app.EchoAmpLabel.Position = [400, 485, 80, 22]; app.EchoAmpLabel.Text = '回音衰减系数'; app.EchoAmpSlider = uislider(app.UIFigure); app.EchoAmpSlider.Position = [460, 480, 200, 30]; app.EchoAmpSlider.Limits = [0, 1]; app.EchoAmpSlider.Value = 0.5; app.EchoAmpSlider.MajorTicks = [0, 0.5, 1]; app.EchoAmpSlider.MajorTickLabels = {'0', '0.5', '1'}; % 噪声幅度滑块 app.NoiseLabel = uilabel(app.UIFigure); app.NoiseLabel.Position = [400, 455, 80, 22]; app.NoiseLabel.Text = '噪声幅度系数'; app.NoiseSlider = uislider(app.UIFigure); app.NoiseSlider.Position = [460, 450, 200, 30]; app.NoiseSlider.Limits = [0, 0.2]; app.NoiseSlider.Value = 0.1; app.NoiseSlider.MajorTicks = [0, 0.1, 0.2]; app.NoiseSlider.MajorTickLabels = {'0', '0.1', '0.2'}; % 波形图坐标1(原始立体声) app.WaveformAxes1 = uiaxes(app.UIFigure); app.WaveformAxes1.Position = [20, 320, 760, 180]; app.WaveformAxes1.Title = '原始音频(立体声)'; app.WaveformAxes1.XLabel = '时间(s)'; app.WaveformAxes1.YLabel = '振幅'; % 波形图坐标2(单声道) app.WaveformAxes2 = uiaxes(app.UIFigure); app.WaveformAxes2.Position = [20, 120, 760, 180]; app.WaveformAxes2.Title = '单声道音频'; app.WaveformAxes2.XLabel = '时间(s)'; app.WaveformAxes2.YLabel = '振幅'; % 频谱图坐标(可扩展) app.SpectrumAxes1 = uiaxes(app.UIFigure); app.SpectrumAxes1.Position = [20, 320, 760, 180]; app.SpectrumAxes1.Visible = 'off'; % 初始隐藏,处理后显示 app.SpectrumAxes2 = uiaxes(app.UIFigure); app.SpectrumAxes2.Position = [20, 120, 760, 180]; app.SpectrumAxes2.Visible = 'off'; % 状态标签 app.StatusLabel = uilabel(app.UIFigure); app.StatusLabel.Position = [20, 50, 760, 22]; app.StatusLabel.Text = '请点击"导入音频"开始'; app.StatusLabel.HorizontalAlignment = 'center'; % 显示窗口 app.UIFigure.Visible = 'on'; end end % 构造函数 methods (Access = public) function app = AudioProcessor % 创建组件 createComponents(app); end end % 导入音频按钮回调 methods (Access = private) function ImportButtonPushed(app, ~) % 打开文件选择对话框 [filePath, fileName] = uigetfile({'*.wav', 'WAV音频文件 (*.wav)'}); if filePath == 0 return; % 未选择文件 end try % 读取音频文件 [y, Fs] = audioread(fullfile(filePath, fileName)); app.OriginalAudio = y; app.SampleRate = Fs; app.IsMono = size(y, 2) == 1; % 显示原始波形 plotOriginalWaveforms(app, y, Fs); % 启用处理按钮 app.ProcessButton.Enable = 'on'; app.StatusLabel.Text = ['已导入: ', fileName]; catch e uialert(app.UIFigure, ['读取失败: ', e.message], '错误'); end end % 开始处理按钮回调 function ProcessButtonPushed(app, ~) if isempty(app.OriginalAudio) uialert(app.UIFigure, '请先导入音频', '提示'); return; end y = app.OriginalAudio; Fs = app.SampleRate; % 1. 双声道转单声道 if ~app.IsMono y_mono = mean(y, 2); else y_mono = y; end % 2. 增益调节(dB转线性因子) gain_db = app.GainSlider.Value; gain_factor = 10^(gain_db/20); % +6dB = 2倍,+12dB=4倍 y_gain = y_mono * gain_factor; % 3. 混音处理(添加白噪声) noise_amp = app.NoiseSlider.Value; noise = noise_amp * randn(size(y_gain)); y_mix = y_gain + noise; % 4. 回音效果 echo_delay_ms = app.DelaySlider.Value; echo_delay = echo_delay_ms / 1000; % 转换为秒 echo_amp = app.EchoAmpSlider.Value; y_echo = addEcho(y_mix, Fs, echo_delay, echo_amp); % 5. 减抽样处理 new_Fs = Fs/2; % 半抽样率 y_down = resample(y_echo, new_Fs, Fs); % 防混叠重采样 % 6. IIR低通滤波器设计 fc = 4000; % 截止频率4kHz [b, a] = butter(4, fc/(new_Fs/2), 'low'); % 4阶巴特沃斯滤波器 y_filtered = filter(b, a, y_down); % 保存处理结果 app.ProcessedAudio = y_filtered; % 显示处理后的波形和频谱 plotProcessedResults(app, y_mono, y_filtered, Fs, new_Fs); % 启用保存按钮 app.SaveButton.Enable = 'on'; app.StatusLabel.Text = '处理完成!'; end % 保存结果按钮回调 function SaveButtonPushed(app, ~) if isempty(app.ProcessedAudio) uialert(app.UIFigure, '无处理结果可保存', '提示'); return; end [filePath, fileName] = uiputfile({'*.wav', 'WAV文件 (*.wav)'}, ... '保存处理后的音频', 'processed_audio.wav'); if filePath == 0 return; % 未选择保存 end try audiowrite(fullfile(filePath, fileName), app.ProcessedAudio, app.SampleRate/2); uialert(app.UIFigure, ['已保存至: ', fileName], '成功'); catch e uialert(app.UIFigure, ['保存失败: ', e.message], '错误'); end end % 绘制原始波形 function plotOriginalWaveforms(app, y_stereo, Fs) t = (0:length(y_stereo)-1)/Fs; % 显示原始立体声波形 cla(app.WaveformAxes1); if size(y_stereo, 2) == 2 plot(app.WaveformAxes1, t, y_stereo(:,1), 'b', t, y_stereo(:,2), 'r'); legend(app.WaveformAxes1, '左声道', '右声道'); else plot(app.WaveformAxes1, t, y_stereo); legend(app.WaveformAxes1, '单声道'); end title(app.WaveformAxes1, '原始音频波形'); xlabel(app.WaveformAxes1, '时间(s)'); ylabel(app.WaveformAxes1, '振幅'); grid(app.WaveformAxes1, 'on'); % 显示单声道波形(若为立体声) if size(y_stereo, 2) == 2 y_mono = mean(y_stereo, 2); cla(app.WaveformAxes2); plot(app.WaveformAxes2, t, y_mono); title(app.WaveformAxes2, '单声道转换后波形'); xlabel(app.WaveformAxes2, '时间(s)'); ylabel(app.WaveformAxes2, '振幅'); grid(app.WaveformAxes2, 'on'); else app.WaveformAxes2.Visible = 'off'; end end % 绘制处理结果(波形和频谱) function plotProcessedResults(app, y_orig, y_proc, Fs, new_Fs) % 隐藏原始波形,显示频谱 app.WaveformAxes1.Visible = 'off'; app.WaveformAxes2.Visible = 'off'; app.SpectrumAxes1.Visible = 'on'; app.SpectrumAxes2.Visible = 'on'; % 绘制原始音频频谱 plotSpectrum(app, y_orig, Fs, app.SpectrumAxes1); title(app.SpectrumAxes1, '原始音频频谱'); % 绘制处理后音频频谱 plotSpectrum(app, y_proc, new_Fs, app.SpectrumAxes2); title(app.SpectrumAxes2, '处理后音频频谱'); end % 频谱分析函数 function plotSpectrum(app, y, Fs, axesHandle) N = 2^nextpow2(length(y)); f = Fs*(0:(N/2))/N; Y = fft(y, N); P = abs(Y/N).^2; % 功率谱密度 cla(axesHandle); plot(axesHandle, f, 10*log10(P(1:N/2+1))); % 转换为dB xlabel(axesHandle, '频率(Hz)'); ylabel(axesHandle, '功率谱密度(dB/Hz)'); xlim(axesHandle, [0, Fs/2]); grid(axesHandle, 'on'); end end % 核心音频处理函数(与原逻辑一致) methods (Access = private) function y_out = addEcho(y_in, Fs, delay, amp) delay_samples = round(delay * Fs); y_out = y_in; % 避免数组越界 max_idx = length(y_in) - delay_samples; if max_idx > 0 y_out(delay_samples+1:end) = y_out(delay_samples+1:end) + amp * y_in(1:max_idx); end end end end
06-24
请帮忙编写一个matlab app,包含交互界面以及显示界面,实现如下代码段的功能: clc; clear; load pitch_mea_pre.mat; measured_data = YPred_denorm'; predicted_data = YTest_denorm'; Fs = 10; plot_(measured_data, predicted_data, Fs); function plot_(measured_data, predicted_data, Fs) % 输入参数: % measured_data: 测量姿态数据 (Nx1向量) % predicted_data: 预测姿态数据 (Mx1向量, M>=N+100) % Fs: 采样频率 (10 Hz) % 检查数据长度 if length(predicted_data) < length(measured_data) + 100 error('预测数据长度不足!需要至少比测量数据长10秒(100个点)'); end % 计算时间向量 t_meas = (0:length(measured_data)-1)/Fs; % 测量数据时间 t_pred = (0:length(predicted_data)-1)/Fs; % 预测数据时间 % 创建图形窗口和子图 figure('Position', [100, 100, 800, 900], 'Color', 'w'); % 子图1: 测量数据 (20秒窗口,但只显示10秒数据) subplot(3, 1, 1); h_meas = plot(t_meas(1:100), measured_data(1:100), 'b', 'LineWidth', 2); title('测量姿态数据 '); xlabel('时间 (秒)'); ylabel('姿态值'); grid on; xlim([0, 20]); % 20秒窗口 ax1 = gca; % 子图2: 预测数据 (20秒窗口) subplot(3, 1, 2); h_pred = plot(t_pred(1:200), predicted_data(1:200), 'r', 'LineWidth', 2); title('预测姿态数据 '); xlabel('时间 (秒)'); ylabel('姿态值'); grid on; xlim([0, 20]); % 20秒窗口 ax2 = gca; % 子图3: 误差数据 (20秒窗口,但只显示10秒数据) subplot(3, 1, 3); error_data = measured_data(1:100) - predicted_data(1:100); h_err = plot(t_meas(1:100), error_data, 'g', 'LineWidth', 2); title('测量-预测误差'); xlabel('时间 (秒)'); ylabel('误差值'); grid on; xlim([0, 20]); % 20秒窗口 ax3 = gca; % 添加当前时间线 hold(ax1, 'on'); tline1 = plot(ax1, [0, 0], ylim(ax1), 'k--', 'LineWidth', 1.5); hold(ax2, 'on'); tline2 = plot(ax2, [0, 0], ylim(ax2), 'k--', 'LineWidth', 1.5); hold(ax3, 'on'); tline3 = plot(ax3, [0, 0], ylim(ax3), 'k--', 'LineWidth', 1.5); % 设置统一的y范围 (基于所有数据) all_data = [measured_data; predicted_data]; y_range = [min(all_data), max(all_data)]; ylim(ax1, y_range); ylim(ax2, y_range); % 设置误差图的y范围 err_range = [min(measured_data - predicted_data(1:length(measured_data))), ... max(measured_data - predicted_data(1:length(measured_data)))]; ylim(ax3, err_range); % 动画参数设置 window_size = 10 * Fs; % 10秒窗口 (100个点) step_size = 1; % 每次前进1个采样点 start_idx = 1; % 起始索引 end_idx = length(measured_data) - window_size; % 结束索引 % 动画循环 for idx = start_idx:step_size:end_idx % 当前时间点 (秒) current_time = idx / Fs; %% 更新测量数据图 (子图1) - 20秒窗口但只显示10秒数据 % 计算数据索引范围 (当前10秒数据) data_start = idx; data_end = idx + window_size - 1; % 更新数据曲线 (只显示10秒数据) set(h_meas, 'XData', t_meas(data_start:data_end), ... 'YData', measured_data(data_start:data_end)); % 更新当前时间线 set(tline1, 'XData', [current_time, current_time]); % 移动x窗口使当前数据位于左侧10秒区域 xlim(ax1, [current_time, current_time + 20]); %% 更新预测数据图 (子图2) - 完整20秒窗口 % 计算数据索引范围 (当前10秒+未来10秒) pred_start = idx; pred_end = idx + 2*window_size - 1; % 更新数据曲线 set(h_pred, 'XData', t_pred(pred_start:pred_end), ... 'YData', predicted_data(pred_start:pred_end)); % 更新当前时间线 set(tline2, 'XData', [current_time, current_time]); % 移动x窗口 xlim(ax2, [current_time, current_time + 20]); %% 更新误差数据图 (子图3) - 20秒窗口但只显示10秒数据 % 计算误差数据 (当前10秒) err_data = measured_data(data_start:data_end) - ... predicted_data(data_start:data_end); % 更新数据曲线 set(h_err, 'XData', t_meas(data_start:data_end), ... 'YData', err_data); % 更新当前时间线 set(tline3, 'XData', [current_time, current_time]); % 移动x窗口使当前数据位于左侧10秒区域 xlim(ax3, [current_time, current_time + 20]); %% 更新图形 drawnow limitrate; % 控制播放速度 (10Hz采样对应实时播放) pause(0.1); % 10Hz = 0.1秒/点 end end
06-17
classdef HeatEquationSolver < matlab.apps.AppBase % 属性 properties (Access = public) UIFigure matlab.ui.Figure TabGroup matlab.ui.container.TabGroup % 设置选项卡组件 SettingsTab matlab.ui.container.Tab SolveButton matlab.ui.control.Button StatusLabel matlab.ui.control.Label % 空间区域设置 DomainLabel matlab.ui.control.Label LLabel matlab.ui.control.Label LEditField matlab.ui.control.NumericEditField NLabel matlab.ui.control.Label NEditField matlab.ui.control.NumericEditField % 时间设置 TimeLabel matlab.ui.control.Label TfinalLabel matlab.ui.control.Label TfinalEditField matlab.ui.control.NumericEditField dtLabel matlab.ui.control.Label dtEditField matlab.ui.control.NumericEditField % 物理参数设置 PhysicalParamsLabel matlab.ui.control.Label AlphaLabel matlab.ui.control.Label AlphaEditField matlab.ui.control.NumericEditField % 初始条件和边界条件设置 ICLabel matlab.ui.control.Label ICDropDown matlab.ui.control.DropDown ICPreviewPanel matlab.ui.container.Panel ICPreviewAxes % 修改为通用类型,兼容axes和uiaxes BCLabel matlab.ui.control.Label BCDropDown matlab.ui.control.DropDown % 数值方法设置 MethodLabel matlab.ui.control.Label SpectralMethodDropDown matlab.ui.control.DropDown CompareWithFDMCheckBox matlab.ui.control.CheckBox % 可视化选项卡组件 VisualizationTab matlab.ui.container.Tab SolutionPlot % 修改为通用类型,兼容axes和uiaxes TimeSlider matlab.ui.control.Slider CurrentTimeLabel matlab.ui.control.Label PlayButton matlab.ui.control.Button AnimationSpeedLabel matlab.ui.control.Label AnimationSpeedDropDown matlab.ui.control.DropDown % 误差分析选项卡组件 ErrorAnalysisTab matlab.ui.container.Tab ErrorPlot % 修改为通用类型,兼容axes和uiaxes PerformanceMetricsTextArea matlab.ui.control.TextArea PerfBarAxes % 修改为通用类型,兼容axes和uiaxes % 帮助选项卡 HelpTab matlab.ui.container.Tab end % 内部属性 - 存储计算结果 properties (Access = private) x % 空间网格点 t % 时间点 u_spectral % 谱方法解 u_fdm % 有限差分法解(如果选择比较) u_exact % 精确解(如果有) errorData % 误差数据 solveTime % 求解时间 fdmSolveTime % 有限差分法求解时间 AnimationTimer % 动画定时器 % 计算参数 L % 计算域长度 N % 空间离散点数 Tfinal % 最终时间 dt % 时间步长 alpha % 热扩散系数 % 状态标志 isSolved % 是否已求解 end methods (Access = private) % 初始条件函数 function u0 = getInitialCondition(app, x) switch app.ICDropDown.Value case '高斯脉冲' % 高斯脉冲 %单个高斯脉冲重复排列,形成周期性的信号 %将脉冲置于计算域的中心,可以保证在周期性边界条件下,脉冲的左右部分对称,避免因边界不连续导致的数值误差。 %L/20意味着脉冲的宽度相对于整个计算域L来说较小,但又不至于太小导致数值离散化困难。 %在显式时间积分方法中,时间步长dt通常受CFL(Courant-Friedrichs-Lewy)条件限制。 % 较小的σ可能导致空间梯度更大,需要更小的时间步长来保持稳定性。选择σ=L/20可能在保证一定精度的同时,允许使用较大的dt,提高计算效率。 sigma = app.L/20; u0 = exp(-(x-app.L/2).^2/(2*sigma^2)); case '阶跃函数' % 阶跃函数 u0 = zeros(size(x)); u0(x >= app.L/4 & x <= 3*app.L/4) = 1; % case '正弦函数' % 正弦函数 u0 = sin(pi*x/app.L); case '自定义' % 这里可以添加用户输入自定义初始条件的功能 u0 = sin(pi*x/app.L); % 默认为正弦函数 end %高斯脉冲:在计算域中心 (L/2) 生成高斯分布,标准差 σ = L/20。 %阶跃函数:在区间 [L/4, 3L/4] 内设为1,其他区域为0。 %正弦函数:生成波长与计算域长度匹配的正弦波,生成正弦波:`u0 = sin(πx/L)`,波长为`2L`。 end % 更新初始条件预览图 function updateICPreview(app) % 获取当前计算域参数 L = app.LEditField.Value; N = 100; % 预览用的固定点数 % 创建均匀网格 dx = L/N; x = (0:N-1)'*dx; % 临时设置app.L以便于getInitialCondition使用 app.L = L; % 获取初始条件 u0 = app.getInitialCondition(x); % 绘制初始条件预览 cla(app.ICPreviewAxes); plot(app.ICPreviewAxes, x, u0, 'b-', 'LineWidth', 1.5); xlabel(app.ICPreviewAxes, 'x'); ylabel(app.ICPreviewAxes, 'u₀'); title(app.ICPreviewAxes, '初始条件'); grid(app.ICPreviewAxes, 'on'); app.ICPreviewAxes.Box = 'on'; end % 使用傅里叶谱方法求解函数 function solve_fourier_spectral(app) tic; % 开始计时 % 获取计算参数 app.L = app.LEditField.Value; app.N = app.NEditField.Value; app.Tfinal = app.TfinalEditField.Value; app.dt = app.dtEditField.Value; app.alpha = app.AlphaEditField.Value; % 创建空间网格 dx = app.L/app.N; app.x = (0:app.N-1)'*dx; % 创建时间点 Nt = ceil(app.Tfinal/app.dt); app.dt = app.Tfinal/Nt; % 调整dt以匹配Tfinal app.t = (0:Nt)'*app.dt; % 初始条件 u0 = app.getInitialCondition(app.x); % 初始化解 app.u_spectral = zeros(app.N, Nt+1); app.u_spectral(:,1) = u0; % 计算波数 if mod(app.N, 2) == 0 k = 2*pi/app.L * [0:app.N/2-1, 0, -app.N/2+1:-1]'; else k = 2*pi/app.L * [0:(app.N-1)/2, -(app.N-1)/2:-1]'; end % 傅里叶变换初始条件 u_hat = fft(u0); % 时间推进 for n = 1:Nt % 热方程的谱解:du_hat/dt = -alpha*k^2*u_hat % 使用精确积分因子求解 u_hat = u_hat .* exp(-app.alpha*k.^2*app.dt); % 反变换回物理空间 app.u_spectral(:,n+1) = real(ifft(u_hat)); end % 记录求解时间 app.solveTime = toc; % 设置状态标志 app.isSolved = true; % 如果选择同时使用有限差分法,则计算FDM解 if app.CompareWithFDMCheckBox.Value app.solve_fdm(); end % 更新界面 app.updateVisualization(); app.updateErrorAnalysis(); end % 使用有限差分法求解函数 function solve_fdm(app) tic; % 开始计时 % 获取计算参数 app.L = app.LEditField.Value; app.N = app.NEditField.Value; app.Tfinal = app.TfinalEditField.Value; app.dt = app.dtEditField.Value; app.alpha = app.AlphaEditField.Value; % 创建均匀网格 dx = app.L/app.N; app.x = (0:app.N-1)'*dx; % 创建时间点 Nt = ceil(app.Tfinal/app.dt); app.dt = app.Tfinal/Nt; % 调整dt以匹配Tfinal app.t = (0:Nt)'*app.dt; % 初始条件 u0 = app.getInitialCondition(app.x); % 初始化解 if app.CompareWithFDMCheckBox.Value && strcmp(app.SpectralMethodDropDown.Value, '有限差分法') % 如果当前是FDM作为主方法,且需要比较,那么存储在u_fdm中 app.u_fdm = zeros(app.N, length(app.t)); app.u_fdm(:,1) = u0; % 并且调用傅里叶谱方法作为比较 app.solve_fourier_for_comparison(); % 最后将FDM结果复制到u_spectral以便统一显示 app.u_spectral = app.u_fdm; else % 如果是独立求解或者作为比较方法,那么存储在u_fdm中 app.u_spectral = zeros(app.N, length(app.t)); app.u_spectral(:,1) = u0; if app.CompareWithFDMCheckBox.Value % 如果是作为比较方法,也初始化u_fdm app.u_fdm = zeros(app.N, length(app.t)); app.u_fdm(:,1) = u0; end end % 计算稳定时间步长 dt_stable = 0.5*dx^2/app.alpha; % 检查时间步长是否稳定 dt_fdm = min(app.dt, dt_stable); r = app.alpha * dt_fdm / dx^2; % 扩散数 % 构造有限差分矩阵(使用中心差分) e = ones(app.N, 1); A = spdiags([e -2*e e], [-1 0 1], app.N, app.N); % 根据边界条件调整矩阵 switch app.BCDropDown.Value case '周期性边界' A(1, app.N) = 1; A(app.N, 1) = 1; case '狄利克雷边界' A(1, :) = 0; A(app.N, :) = 0; end % 显式时间推进(前向Euler) u_current = u0; % 确定要存储结果的数组 if app.CompareWithFDMCheckBox.Value && strcmp(app.SpectralMethodDropDown.Value, '傅里叶谱方法') % 如果作为比较方法,存储在u_fdm中 result_array = app.u_fdm; else % 否则存储在u_spectral中 result_array = app.u_spectral; end % 时间推进循环 for n = 1:length(app.t)-1 u_next = u_current + r * (A * u_current); % 应用边界条件 switch app.BCDropDown.Value case '狄利克雷边界' u_next(1) = 0; u_next(app.N) = 0; end if app.CompareWithFDMCheckBox.Value && strcmp(app.SpectralMethodDropDown.Value, '傅里叶谱方法') app.u_fdm(:,n+1) = u_next; else app.u_spectral(:,n+1) = u_next; end u_current = u_next; end % 记录求解时间 if app.CompareWithFDMCheckBox.Value && strcmp(app.SpectralMethodDropDown.Value, '傅里叶谱方法') % 如果作为比较方法,记录到fdmSolveTime app.fdmSolveTime = toc; else % 否则记录到主求解时间 app.solveTime = toc; % 设置状态标志 app.isSolved = true; end % 如果使用FDM作为主方法,且不需要比较,则不需要更新界面 % 因为调用者会负责更新 if ~(app.CompareWithFDMCheckBox.Value && strcmp(app.SpectralMethodDropDown.Value, '傅里叶谱方法')) % 更新界面 app.updateVisualization(); app.updateErrorAnalysis(); end end % 使用傅里叶谱方法作为比较方法 function solve_fourier_for_comparison(app) tic; % 开始计时 % 获取计算参数(共用主方法的参数) % 空间和时间网格已经在主方法中设置 % 初始条件 u0 = app.getInitialCondition(app.x); % 初始化解 app.u_spectral = zeros(app.N, length(app.t)); app.u_spectral(:,1) = u0; % 计算波数 if mod(app.N, 2) == 0 k = 2*pi/app.L * [0:app.N/2-1, 0, -app.N/2+1:-1]'; else k = 2*pi/app.L * [0:(app.N-1)/2, -(app.N-1)/2:-1]'; end % 傅里叶变换初始条件 u_hat = fft(u0); % 时间推进 for n = 1:length(app.t)-1 % 热方程的谱解:du_hat/dt = -alpha*k^2*u_hat % 使用精确积分因子求解 u_hat = u_hat .* exp(-app.alpha*k.^2*app.dt); % 反变换回物理空间 app.u_spectral(:,n+1) = real(ifft(u_hat)); end % 记录求解时间 app.solveTime = toc; end % 更新可视化函数 function updateVisualization(app) if ~app.isSolved return; end % 获取时间滑块的当前值 timeIndex = round(app.TimeSlider.Value); % 获取当前选择的方法 currentMethod = app.SpectralMethodDropDown.Value; % 绘制解 cla(app.SolutionPlot); hold(app.SolutionPlot, 'on'); % 绘制主方法解 plot(app.SolutionPlot, app.x, app.u_spectral(:,timeIndex), 'b-', 'LineWidth', 2); % 如果有比较方法,也绘制 if app.CompareWithFDMCheckBox.Value plot(app.SolutionPlot, app.x, app.u_fdm(:,timeIndex), 'r--', 'LineWidth', 1.5); if strcmp(currentMethod, '傅里叶谱方法') legend(app.SolutionPlot, '傅里叶谱方法', '有限差分法'); else legend(app.SolutionPlot, '有限差分法', '傅里叶谱方法'); end else legend(app.SolutionPlot, currentMethod); end % 设置标题和标签 title(app.SolutionPlot, sprintf('t = %.4f', app.t(timeIndex))); xlabel(app.SolutionPlot, 'x'); ylabel(app.SolutionPlot, 'u(x,t)'); grid(app.SolutionPlot, 'on'); % 更新当前时间标签 app.CurrentTimeLabel.Text = sprintf('当前时间: %.4f / %.4f', app.t(timeIndex), app.Tfinal); hold(app.SolutionPlot, 'off'); end % 更新误差分析函数 function updateErrorAnalysis(app) if ~app.isSolved || ~app.CompareWithFDMCheckBox.Value return; end % 获取当前选择的方法 currentMethod = app.SpectralMethodDropDown.Value; % 计算两种方法的L2误差 error = zeros(size(app.t)); for i = 1:length(app.t) error(i) = sqrt(mean((app.u_spectral(:,i) - app.u_fdm(:,i)).^2)); end % 绘制误差随时间的变化 cla(app.ErrorPlot); semilogy(app.ErrorPlot, app.t, error, 'k-', 'LineWidth', 1.5); % 设置标题和标签 if strcmp(currentMethod, '傅里叶谱方法') title(app.ErrorPlot, '傅里叶谱方法与有限差分法的L2误差'); else title(app.ErrorPlot, '有限差分法与傅里叶谱方法的L2误差'); end xlabel(app.ErrorPlot, '时间 t'); ylabel(app.ErrorPlot, 'L2误差'); grid(app.ErrorPlot, 'on'); % 计算速度对比 if strcmp(currentMethod, '傅里叶谱方法') method1 = '傅里叶谱方法'; method2 = '有限差分法'; time1 = app.solveTime; time2 = app.fdmSolveTime; else method1 = '有限差分法'; method2 = '傅里叶谱方法'; time1 = app.solveTime; time2 = app.fdmSolveTime; end % 更新性能指标文本 app.PerformanceMetricsTextArea.Value = { sprintf('%s计算时间: %.4f秒', method1, time1), sprintf('%s计算时间: %.4f秒', method2, time2), sprintf('加速比: %.2f倍', time2/time1), sprintf('最大误差: %.6e', max(error)), sprintf('平均误差: %.6e', mean(error)), sprintf('空间点数: %d', app.N), sprintf('时间步数: %d', length(app.t)), sprintf('CFL数: %.4f', app.alpha*app.dt/(app.L/app.N)^2) }; % 绘制性能对比条形图 cla(app.PerfBarAxes); methods = {method1, method2}; times = [time1, time2]; bar(app.PerfBarAxes, times); app.PerfBarAxes.XTickLabel = methods; ylabel(app.PerfBarAxes, '计算时间 (秒)'); title(app.PerfBarAxes, '计算时间对比'); % 添加数值标签 for i = 1:length(times) text(app.PerfBarAxes, i, times(i)*1.05, sprintf('%.4f秒', times(i)), ... 'HorizontalAlignment', 'center', 'FontSize', 9); end % 添加加速比标签 speedup = time2/time1; text(app.PerfBarAxes, 1.5, min(times)*0.5, sprintf('加速比: %.2f倍', speedup), ... 'HorizontalAlignment', 'center', 'FontWeight', 'bold', 'FontSize', 11); end end % 回调函数和界面事件处理 methods (Access = private) % 点击求解按钮的回调函数 function SolveButtonPushed(app, ~) % 更新状态 app.StatusLabel.Text = '计算中...'; try app.StatusLabel.FontColor = [0.8 0.4 0]; catch % 忽略错误,保持默认颜色 end drawnow; % 根据选择的方法进行求解 switch app.SpectralMethodDropDown.Value case '傅里叶谱方法' app.solve_fourier_spectral(); case '有限差分法' app.solve_fdm(); end % 设置时间滑块的范围 app.TimeSlider.Limits = [1, length(app.t)]; app.TimeSlider.Value = 1; % 更新状态 app.StatusLabel.Text = '计算完成!'; try app.StatusLabel.FontColor = [0 0.6 0]; catch % 忽略错误,保持默认颜色 end % 切换到可视化选项卡 app.TabGroup.SelectedTab = app.VisualizationTab; end % 时间滑块改变的回调函数 function TimeSliderValueChanged(app, ~) app.updateVisualization(); end % 初始条件下拉列表值改变的回调函数 function ICDropDownValueChanged(app, ~) app.updateICPreview(); end % L值改变时的回调函数 function LEditFieldValueChanged(app, ~) app.updateICPreview(); end % 选择方法下拉列表值改变的回调函数 function SpectralMethodDropDownValueChanged(app, ~) % 如果选择有限差分法,默认使用狄利克雷边界 if strcmp(app.SpectralMethodDropDown.Value, '有限差分法') app.BCDropDown.Value = '狄利克雷边界'; else app.BCDropDown.Value = '周期性边界'; end end % 播放/暂停按钮的回调函数 function PlayButtonPushed(app, ~) % 如果正在播放,则暂停 if strcmp(app.PlayButton.Text, '暂停') app.PlayButton.Text = '播放'; try app.PlayButton.BackgroundColor = [0.3 0.8 0.3]; catch % 忽略错误,保持默认颜色 end stop(app.AnimationTimer); return; end % 否则开始播放 app.PlayButton.Text = '暂停'; try app.PlayButton.BackgroundColor = [0.8 0.3 0.3]; catch % 忽略错误,保持默认颜色 end % 获取动画速度 speed = 0.1; % 默认中速 switch app.AnimationSpeedDropDown.Value case '慢速' speed = 0.2; case '中速' speed = 0.1; case '快速' speed = 0.05; end % 创建并启动定时器 app.AnimationTimer = timer('ExecutionMode', 'fixedRate', ... 'Period', speed, ... 'TimerFcn', @(~,~) app.animationStep()); start(app.AnimationTimer); end % 动画步进函数 function animationStep(app) % 获取当前滑块值 currentIndex = app.TimeSlider.Value; % 如果已经到达最后一个时间点,则重置到开始 if currentIndex >= app.TimeSlider.Limits(2) currentIndex = app.TimeSlider.Limits(1); else % 否则前进到下一个时间点 currentIndex = currentIndex + 1; end % 更新滑块值 app.TimeSlider.Value = currentIndex; % 更新可视化 app.updateVisualization(); end end % 组件初始化和创建函数 methods (Access = private) % 创建UI组件 function createComponents(app) % 检查MATLAB版本,R2019b及以上支持uiaxes hasUIAxes = ~verLessThan('matlab', '9.7'); % R2019b版本号是9.7 % 创建主窗口 app.UIFigure = uifigure('Visible', 'off'); app.UIFigure.Position = [100, 100, 900, 700]; app.UIFigure.Name = '一维热传导方程谱方法求解器'; app.UIFigure.Color = [0.94 0.94 0.94]; % 创建选项卡组 app.TabGroup = uitabgroup(app.UIFigure); app.TabGroup.Position = [1, 1, 900, 700]; app.TabGroup.TabLocation = 'left'; % 创建设置选项卡 app.SettingsTab = uitab(app.TabGroup); app.SettingsTab.Title = '参数设置'; app.SettingsTab.BackgroundColor = [0.94 0.94 0.94]; % 创建面板来组织控件 paramPanel = uipanel(app.SettingsTab); paramPanel.Title = '计算参数'; paramPanel.FontWeight = 'bold'; paramPanel.FontSize = 14; paramPanel.Position = [30, 400, 400, 300]; paramPanel.BackgroundColor = [0.97 0.97 0.97]; bcPanel = uipanel(app.SettingsTab); bcPanel.Title = '条件设置'; bcPanel.FontWeight = 'bold'; bcPanel.FontSize = 14; bcPanel.Position = [460, 400, 400, 250]; bcPanel.BackgroundColor = [0.97 0.97 0.97]; methodPanel = uipanel(app.SettingsTab); methodPanel.Title = '数值方法设置'; methodPanel.FontWeight = 'bold'; methodPanel.FontSize = 14; methodPanel.Position = [30, 150, 830, 220]; methodPanel.BackgroundColor = [0.97 0.97 0.97]; % 创建空间区域设置组件 app.DomainLabel = uilabel(paramPanel); app.DomainLabel.Position = [20, 260, 200, 22]; app.DomainLabel.Text = '计算域设置'; app.DomainLabel.FontWeight = 'bold'; app.DomainLabel.FontSize = 12; app.LLabel = uilabel(paramPanel); app.LLabel.Position = [20, 240, 150, 22]; app.LLabel.Text = '域长度 L:'; app.LEditField = uieditfield(paramPanel, 'numeric'); app.LEditField.Position = [180, 240, 100, 22]; app.LEditField.Value = 1; app.LEditField.Limits = [0.1, 10]; app.LEditField.ValueDisplayFormat = '%.3f'; app.NLabel = uilabel(paramPanel); app.NLabel.Position = [20, 210, 150, 22]; app.NLabel.Text = '空间点数 N:'; app.NEditField = uieditfield(paramPanel, 'numeric'); app.NEditField.Position = [180, 210, 100, 22]; app.NEditField.Value = 128; app.NEditField.Limits = [16, 1024]; % 创建时间设置组件 app.TimeLabel = uilabel(paramPanel); app.TimeLabel.Position = [20, 180, 200, 22]; app.TimeLabel.Text = '时间设置'; app.TimeLabel.FontWeight = 'bold'; app.TimeLabel.FontSize = 12; app.TfinalLabel = uilabel(paramPanel); app.TfinalLabel.Position = [20, 150, 150, 22]; app.TfinalLabel.Text = '终止时间:'; app.TfinalEditField = uieditfield(paramPanel, 'numeric'); app.TfinalEditField.Position = [180, 150, 100, 22]; app.TfinalEditField.Value = 0.1; app.TfinalEditField.Limits = [0.001, 10]; app.TfinalEditField.ValueDisplayFormat = '%.4f'; app.dtLabel = uilabel(paramPanel); app.dtLabel.Position = [20, 120, 150, 22]; app.dtLabel.Text = '时间步长:'; app.dtEditField = uieditfield(paramPanel, 'numeric'); app.dtEditField.Position = [180, 120, 100, 22]; app.dtEditField.Value = 0.001; app.dtEditField.Limits = [1e-6, 1]; app.dtEditField.ValueDisplayFormat = '%.6f'; % 创建物理参数设置组件 app.PhysicalParamsLabel = uilabel(paramPanel); app.PhysicalParamsLabel.Position = [20, 90, 200, 22]; app.PhysicalParamsLabel.Text = '物理参数'; app.PhysicalParamsLabel.FontWeight = 'bold'; app.PhysicalParamsLabel.FontSize = 12; app.AlphaLabel = uilabel(paramPanel); app.AlphaLabel.Position = [20, 60, 150, 22]; app.AlphaLabel.Text = '热扩散系数 α:'; app.AlphaEditField = uieditfield(paramPanel, 'numeric'); app.AlphaEditField.Position = [180, 60, 100, 22]; app.AlphaEditField.Value = 0.01; app.AlphaEditField.Limits = [0.0001, 1]; app.AlphaEditField.ValueDisplayFormat = '%.5f'; % 创建初始条件和边界条件设置组件 app.ICLabel = uilabel(bcPanel); app.ICLabel.Position = [20, 180, 150, 22]; app.ICLabel.Text = '初始条件:'; app.ICLabel.FontWeight = 'bold'; app.ICDropDown = uidropdown(bcPanel); app.ICDropDown.Items = {'高斯脉冲', '阶跃函数', '正弦函数', '自定义'}; app.ICDropDown.Value = '高斯脉冲'; app.ICDropDown.Position = [180, 180, 180, 22]; app.ICDropDown.BackgroundColor = [1 1 1]; % 添加初始条件预览图 app.ICPreviewPanel = uipanel(bcPanel); app.ICPreviewPanel.Title = '初始条件预览'; app.ICPreviewPanel.Position = [20, 70, 340, 100]; % 根据MATLAB版本选择绘图函数 if hasUIAxes % 使用新版的uiaxes app.ICPreviewAxes = uiaxes(app.ICPreviewPanel); else % 使用传统的axes app.ICPreviewAxes = axes('Parent', app.ICPreviewPanel); end app.ICPreviewAxes.Position = [10, 10, 320, 70]; app.ICPreviewAxes.XGrid = 'on'; app.ICPreviewAxes.YGrid = 'on'; app.ICPreviewAxes.Box = 'on'; app.BCLabel = uilabel(bcPanel); app.BCLabel.Position = [20, 40, 150, 22]; app.BCLabel.Text = '边界条件:'; app.BCLabel.FontWeight = 'bold'; app.BCDropDown = uidropdown(bcPanel); app.BCDropDown.Items = {'周期性边界', '狄利克雷边界'}; app.BCDropDown.Value = '周期性边界'; app.BCDropDown.Position = [180, 40, 180, 22]; app.BCDropDown.BackgroundColor = [1 1 1]; % 在方法面板中创建数值方法设置组件 app.MethodLabel = uilabel(methodPanel); app.MethodLabel.Position = [20, 170, 150, 22]; app.MethodLabel.Text = '求解方法:'; app.MethodLabel.FontWeight = 'bold'; app.SpectralMethodDropDown = uidropdown(methodPanel); app.SpectralMethodDropDown.Items = {'傅里叶谱方法', '有限差分法'}; app.SpectralMethodDropDown.Value = '傅里叶谱方法'; app.SpectralMethodDropDown.Position = [180, 170, 180, 22]; app.SpectralMethodDropDown.BackgroundColor = [1 1 1]; app.CompareWithFDMCheckBox = uicheckbox(methodPanel); app.CompareWithFDMCheckBox.Text = '与另一种方法比较'; app.CompareWithFDMCheckBox.Position = [400, 170, 200, 22]; app.CompareWithFDMCheckBox.Value = true; app.CompareWithFDMCheckBox.FontWeight = 'bold'; % 添加方法说明文本 methodInfoText = uitextarea(methodPanel); methodInfoText.Position = [20, 50, 790, 100]; methodInfoText.Value = { '傅里叶谱方法: 适用于周期性边界条件,使用FFT进行加速计算,对光滑解具有指数收敛特性。', '有限差分法: 经典的数值方法,使用网格点上的差分近似导数,精度取决于网格密度。', '', '对于周期性边界条件,傅里叶谱方法通常更高效、更精确。', '对于狄利克雷边界条件,有限差分法实现简单,适用性广泛。' }; methodInfoText.FontSize = 11; methodInfoText.Editable = 'off'; % 创建求解按钮 - 使其更突出 app.SolveButton = uibutton(app.SettingsTab, 'push'); app.SolveButton.ButtonPushedFcn = createCallbackFcn(app, @SolveButtonPushed, true); app.SolveButton.Position = [375, 60, 150, 50]; app.SolveButton.Text = '开始求解'; app.SolveButton.FontSize = 16; app.SolveButton.FontWeight = 'bold'; try % 尝试设置按钮颜色,如果不支持则忽略 app.SolveButton.BackgroundColor = [0.3 0.6 0.9]; app.SolveButton.FontColor = [1 1 1]; catch % 忽略错误,保持默认颜色 end % 创建状态标签 app.StatusLabel = uilabel(app.SettingsTab); app.StatusLabel.Position = [350, 30, 200, 22]; app.StatusLabel.Text = '准备就绪'; app.StatusLabel.HorizontalAlignment = 'center'; % 创建可视化选项卡 app.VisualizationTab = uitab(app.TabGroup); app.VisualizationTab.Title = '可视化'; app.VisualizationTab.BackgroundColor = [0.94 0.94 0.94]; % 创建可视化控制面板 visControlPanel = uipanel(app.VisualizationTab); visControlPanel.Title = '可视化控制'; visControlPanel.FontWeight = 'bold'; visControlPanel.FontSize = 14; visControlPanel.Position = [30, 30, 830, 100]; visControlPanel.BackgroundColor = [0.97 0.97 0.97]; % 创建解的图形面板 visSolutionPanel = uipanel(app.VisualizationTab); visSolutionPanel.Title = '温度场分布'; visSolutionPanel.FontWeight = 'bold'; visSolutionPanel.FontSize = 14; visSolutionPanel.Position = [30, 150, 830, 500]; visSolutionPanel.BackgroundColor = [0.97 0.97 0.97]; % 创建解的图形 if hasUIAxes % 使用新版的uiaxes app.SolutionPlot = uiaxes(visSolutionPanel); else % 使用传统的axes app.SolutionPlot = axes('Parent', visSolutionPanel); end app.SolutionPlot.Position = [20, 20, 790, 460]; title(app.SolutionPlot, '温度场分布'); xlabel(app.SolutionPlot, '空间坐标 x'); ylabel(app.SolutionPlot, '温度 u(x,t)'); grid(app.SolutionPlot, 'on'); app.SolutionPlot.Box = 'on'; app.SolutionPlot.FontSize = 12; % 创建时间滑块 app.TimeSlider = uislider(visControlPanel); app.TimeSlider.Position = [150, 50, 500, 3]; app.TimeSlider.Limits = [1, 100]; app.TimeSlider.Value = 1; app.TimeSlider.ValueChangedFcn = createCallbackFcn(app, @TimeSliderValueChanged, true); % 添加播放/暂停按钮 app.PlayButton = uibutton(visControlPanel, 'push'); app.PlayButton.Position = [50, 45, 80, 30]; app.PlayButton.Text = '播放'; try % 尝试设置按钮颜色,如果不支持则忽略 app.PlayButton.BackgroundColor = [0.3 0.8 0.3]; app.PlayButton.FontColor = [1 1 1]; catch % 忽略错误,保持默认颜色 end app.PlayButton.ButtonPushedFcn = createCallbackFcn(app, @PlayButtonPushed, true); % 添加时间步长控制 app.AnimationSpeedLabel = uilabel(visControlPanel); app.AnimationSpeedLabel.Position = [670, 50, 80, 22]; app.AnimationSpeedLabel.Text = '动画速度:'; app.AnimationSpeedDropDown = uidropdown(visControlPanel); app.AnimationSpeedDropDown.Items = {'慢速', '中速', '快速'}; app.AnimationSpeedDropDown.Value = '中速'; app.AnimationSpeedDropDown.Position = [750, 50, 60, 22]; app.CurrentTimeLabel = uilabel(visControlPanel); app.CurrentTimeLabel.Position = [330, 15, 200, 22]; app.CurrentTimeLabel.Text = '当前时间: 0.0000 / 0.1000'; app.CurrentTimeLabel.HorizontalAlignment = 'center'; % 创建误差分析选项卡 app.ErrorAnalysisTab = uitab(app.TabGroup); app.ErrorAnalysisTab.Title = '误差分析'; app.ErrorAnalysisTab.BackgroundColor = [0.94 0.94 0.94]; % 创建误差图形面板 errorPanel = uipanel(app.ErrorAnalysisTab); errorPanel.Title = '误差分析'; errorPanel.FontWeight = 'bold'; errorPanel.FontSize = 14; errorPanel.Position = [30, 350, 830, 300]; errorPanel.BackgroundColor = [0.97 0.97 0.97]; % 创建性能指标面板 perfPanel = uipanel(app.ErrorAnalysisTab); perfPanel.Title = '性能指标'; perfPanel.FontWeight = 'bold'; perfPanel.FontSize = 14; perfPanel.Position = [30, 30, 830, 300]; perfPanel.BackgroundColor = [0.97 0.97 0.97]; % 创建误差图形 if hasUIAxes % 使用新版的uiaxes app.ErrorPlot = uiaxes(errorPanel); else % 使用传统的axes app.ErrorPlot = axes('Parent', errorPanel); end app.ErrorPlot.Position = [20, 20, 790, 260]; title(app.ErrorPlot, '谱方法与有限差分法的误差对比'); xlabel(app.ErrorPlot, '时间 t'); ylabel(app.ErrorPlot, 'L2误差'); grid(app.ErrorPlot, 'on'); app.ErrorPlot.Box = 'on'; app.ErrorPlot.YScale = 'log'; app.ErrorPlot.FontSize = 12; % 创建性能指标文本区域 app.PerformanceMetricsTextArea = uitextarea(perfPanel); app.PerformanceMetricsTextArea.Position = [20, 100, 400, 180]; app.PerformanceMetricsTextArea.Value = {'性能指标将在求解后显示'}; app.PerformanceMetricsTextArea.FontSize = 12; app.PerformanceMetricsTextArea.Editable = 'off'; % 创建性能指标可视化 if hasUIAxes % 使用新版的uiaxes app.PerfBarAxes = uiaxes(perfPanel); else % 使用传统的axes app.PerfBarAxes = axes('Parent', perfPanel); end app.PerfBarAxes.Position = [450, 100, 360, 180]; title(app.PerfBarAxes, '计算时间对比'); app.PerfBarAxes.XTickLabelRotation = 45; app.PerfBarAxes.Box = 'on'; app.PerfBarAxes.FontSize = 11; % 创建帮助选项卡 app.HelpTab = uitab(app.TabGroup); app.HelpTab.Title = '帮助'; app.HelpTab.BackgroundColor = [0.94 0.94 0.94]; % 添加帮助信息 helpTextArea = uitextarea(app.HelpTab); helpTextArea.Position = [30, 30, 830, 620]; helpTextArea.Value = { '一维热传导方程谱方法求解器使用说明', '==========================================', '', '1. 参数设置选项卡:', ' - 计算域参数: 设置空间域长度和离散点数', ' - 时间参数: 设置仿真终止时间和时间步长', ' - 物理参数: 设置热扩散系数', ' - 初始条件: 选择初始温度分布', ' - 边界条件: 选择边界类型', ' - 求解方法: 选择傅里叶谱方法或有限差分法', '', '2. 可视化选项卡:', ' - 使用时间滑块观察不同时刻的温度分布', ' - 使用播放按钮自动播放温度场演化过程', ' - 调整动画速度控制播放速率', '', '3. 误差分析选项卡:', ' - 查看谱方法与有限差分法的误差对比', ' - 查看性能指标,包括计算时间和加速比', '', '算法说明:', '=========', '热传导方程: ∂u/∂t = α ∂²u/∂x²', '', '傅里叶谱方法原理:', '1. 将解展开为傅里叶级数', '2. 在频域中计算空间导数', '3. 使用FFT进行物理空间和频域之间的变换', '4. 采用时间积分方法求解', '', '有限差分法: 经典的数值方法,使用网格点上的差分近似导数,精度取决于网格密度。', '', '更多信息请参考文档。' }; helpTextArea.FontSize = 12; helpTextArea.Editable = 'off'; % 选择第一个选项卡作为默认选项卡 app.TabGroup.SelectedTab = app.SettingsTab; end end % 公共方法 methods (Access = public) % 构造函数 function app = HeatEquationSolver % 创建UI组件 createComponents(app) % 初始化属性 app.isSolved = false; % 设置回调函数 app.ICDropDown.ValueChangedFcn = createCallbackFcn(app, @ICDropDownValueChanged, true); app.LEditField.ValueChangedFcn = createCallbackFcn(app, @LEditFieldValueChanged, true); app.SpectralMethodDropDown.ValueChangedFcn = createCallbackFcn(app, @SpectralMethodDropDownValueChanged, true); % 初始化初始条件预览 app.updateICPreview(); % 显示UI app.UIFigure.Visible = 'on'; end % 析构函数 - 清理定时器 function delete(app) % 停止并删除动画定时器(如果存在) if ~isempty(app.AnimationTimer) && isvalid(app.AnimationTimer) stop(app.AnimationTimer); delete(app.AnimationTimer); end % 删除组件 delete(app.UIFigure); end end end代码实现后,选用傅里叶解为主方法同时勾选与另一种方法对比时,主方法的可视化动画无法实现怎么修改
05-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值