【微实验】柏拉图多面体内的波干涉仿真MATLAB

前情提要一:

前一个文章里,扒谱机与AI斗智斗勇,却一直没法绘制出标准的正五边形组成的十二面体。

网传CAD画图都是先绘制一个五边形,然后对应旋转拼接,而剩下的教程基本都是素描的教程。

经过百般搜索,扒谱机我找到了一个网站,然后参考了一下代码——

正十二面体

从它的源代码中找到了准确的点坐标应该是——

 vertices_dodeca = [
      % 第一组顶点
    1, 1, 1;          % 1
    1, 1, -1;         % 2
    1, -1, 1;         % 3
    1, -1, -1;        % 4
    -1, 1, 1;         % 5
    -1, 1, -1;        % 6
    -1, -1, 1;        % 7
    -1, -1, -1;       % 8
    % 第二组顶点
    phi, 1/phi, 0;    % 9
    phi, -1/phi, 0;   % 10
    -phi, 1/phi, 0;   % 11
    -phi, -1/phi, 0;  % 12
    % 第三组顶点
    1/phi, 0, phi;    % 13
    1/phi, 0, -phi;   % 14
    -1/phi, 0, phi;   % 15
    -1/phi, 0, -phi;  % 16
    % 第四组顶点
    0, phi, 1/phi;    % 17
    0, phi, -1/phi;   % 18
    0, -phi, 1/phi;   % 19
    0, -phi, -1/phi   % 20
    ];

经过艰苦卓绝的面对应关系的书写,我也终于画出来了……

附个代码:

% 清空工作区并关闭所有窗口
clear;
close all;

    % 计算黄金比例
    phi = (1 + sqrt(5)) / 2; 
    colors = lines(5);

    % 1. 正四面体
    vertices_tetra = [1 1 1; 1 -1 -1; -1 1 -1; -1 -1 1];
    faces_tetra = [1 2 3; 1 2 4; 1 3 4; 2 3 4];

    % 2. 正六面体(立方体)
    vertices_cube = [-1 -1 -1; 1 -1 -1; 1 1 -1; -1 1 -1; -1 -1 1; 1 -1 1; 1 1 1; -1 1 1];
    faces_cube = [1 2 3 4; 5 6 7 8; 1 2 6 5; 2 3 7 6; 3 4 8 7; 4 1 5 8];

    % 3. 正八面体
    vertices_octa = [1 0 0; -1 0 0; 0 1 0; 0 -1 0; 0 0 1; 0 0 -1];
    faces_octa = [1 3 5; 1 5 4; 1 4 6; 1 6 3; 2 3 6; 2 6 4; 2 4 5; 2 5 3];

    % 4. 正十二面体
    vertices_dodeca = [
      % 第一组顶点
    1, 1, 1;          % 1
    1, 1, -1;         % 2
    1, -1, 1;         % 3
    1, -1, -1;        % 4
    -1, 1, 1;         % 5
    -1, 1, -1;        % 6
    -1, -1, 1;        % 7
    -1, -1, -1;       % 8
    % 第二组顶点
    phi, 1/phi, 0;    % 9
    phi, -1/phi, 0;   % 10
    -phi, 1/phi, 0;   % 11
    -phi, -1/phi, 0;  % 12
    % 第三组顶点
    1/phi, 0, phi;    % 13
    1/phi, 0, -phi;   % 14
    -1/phi, 0, phi;   % 15
    -1/phi, 0, -phi;  % 16
    % 第四组顶点
    0, phi, 1/phi;    % 17
    0, phi, -1/phi;   % 18
    0, -phi, 1/phi;   % 19
    0, -phi, -1/phi   % 20
    ];
    faces_dodeca = [
    5 11 12 7 15;    8 12 7 19 20;    4 20 8 16 14;    2 9 10 4 14;
    6 18 2 14 16;    5 17 1 13 15;    11 12 8 16 6;    7 19 3 13 15;
    4 10 3 19 20;    1 13 3 10 9;    1 9 2 18 17;    11 6 18 17 5;
    ];
  
    % 5. 正二十面体
    
    vertices_icosa =[
        0   1   phi;  0   1  -phi;  0  -1   phi;  0  -1  -phi;
        1   phi   0;  1  -phi   0; -1   phi   0; -1  -phi   0;
        phi   0   1; -phi   0   1;  phi   0  -1; -phi   0  -1
    ];
    faces_icosa = [
        1  3  9;  1  3 10;  1 10  7;  1  5  7;  1  5  9; 
        2  7 12;  2  5  7;  2  4 12;  2  4 11;  2  5 11;
        3  6  8;  3  6  9;  3  8 10;  4  6  8;  4  6 11; 
        4  8 12;  5  9 11;  6  9 11;  7 10 12;  8 10 12;
    ];

    % 存储多面体数据
    polyhedra = {
        {'正四面体',   vertices_tetra, faces_tetra};
        {'正六面体',   vertices_cube,   faces_cube};
        {'正八面体',   vertices_octa,   faces_octa};
        {'正十二面体', vertices_dodeca, faces_dodeca};
        {'正二十面体', vertices_icosa,  faces_icosa}
    };

    % 批量绘制(增强可视化)
    figure('Position', [100 100 1200 800]);
    for i = 1:5
        subplot(2, 3, i);
        data = polyhedra{i};
        name = data{1};
        v = data{2};
        f = data{3};

        % 绘制填充面 + 线框
        patch('Vertices', v, 'Faces', f, ...
            'FaceColor', colors(i,:), 'EdgeColor', 'k', 'FaceAlpha', 0.3);
        hold on;
        patch('Vertices', v, 'Faces', f, ...
            'FaceColor', 'none', 'EdgeColor', 'k', 'LineWidth', 1.2);

        % 标注顶点索引
        for idx = 1:size(v, 1)
            text(v(idx,1), v(idx,2), v(idx,3), num2str(idx), ...
                'HorizontalAlignment', 'center', 'VerticalAlignment', 'bottom', 'FontSize', 8);
        end

        title(name, 'FontSize', 12);
        axis equal; 
        axis off; 
        view(3); 
        light('Position', [2 2 2]); 
        lighting gouraud;
        hold off;
    end

现在绘制柏拉图多面体代码都已经齐全,是时候做波在里面传播的仿真动画了。

前情提要二:

【微实验】新型鬼畜,球面波在柏拉图多面体里疯跑太魔幻!-CSDN博客

这篇文章里,笔者仅仅实现了立方体的部分,接下来,是时候把这两个结合起来了。

另外,参考b站up主帆雨动画,还需要进行的优化就是——

波峰使用蓝色标记,波谷使用橙色标记。为了便于观察,统一为黑白色后,振动幅度大的地方颜色深,基本上没有振幅的颜色浅。

尝试写了一下代码,看看效果:

似乎颜色比较浅?再加深一点?

除此之外,我希望仿真中能够把所有振幅为零的点标记画出(emm结果ai给的方案是标出等振幅面,效果并不是很理想)。

于是设计了一下阈值分段,振幅超过10%最大振幅的就直接使用纯色显示,最后优化的效果是这样子的:

这个代码是可以显示波源的震动频率不断变化的,当然,只要两列波的频率不统一,就不能形成稳定的干涉图样,但是同样可以形成干涉图样。可以发现类似双曲线的零振幅干涉纹样会逐渐向频率低的波源靠拢。

另外,在这个代码里,还可以修改不同波的振幅。通过修改参数,你可以得到很好看的纹样:

代码如下:




% 两个波源的干涉仿真(固定颜色标尺)
% 波峰用蓝色标记,波谷用红色标记,中间区域颜色渐变
% 固定颜色标尺,确保相同振幅对应相同颜色

clear; clc; close all;

%% 参数设置
% 空间参数
L = 50;                  % 空间范围
N = 2000;                 % 网格点数
x = linspace(-L, L, N);  % x坐标
y = linspace(-L, L, N);  % y坐标
[X, Y] = meshgrid(x, y); % 创建网格

% 波源参数
A1 = 4.2;                  % 波源1振幅
A2 = 1.9;                  % 波源2振幅
f1_start = 0.33;            % 波源1初始频率
f2 = 1;                  % 波源2频率
f1_end = 0.3304;              % 波源1最终频率
k = 2;                   % 波数

% 波源位置
x1 = -5; y1 = 2;         % 波源1位置
x2 = 21;  y2 = -13.2;         % 波源2位置

% 时间参数
t_total = 10;            % 总时间
dt = 0.05;                % 时间步长
t = 0:dt:t_total;        % 时间向量
n_frames = length(t);    % 帧数
%自动设置固定振幅范围(保持对称)
fixed_max = A1 + A2;
fixed_min = -fixed_max;

% 创建颜色映射(高振幅纯色+低振幅渐变)
cmap = zeros(256, 3);
% 定义基础颜色:高振幅纯色+过渡色
blue_peak = [0 0 1];         % 波峰纯色(超过10%振幅)
red_trough = [1 0 0];        % 波谷纯色(低于-10%振幅)
white_mid = [1 1 1];         % 中间参考色


% 振幅阈值:超过10%最高振幅的部分用纯色
threshold_ratio = 0.1;  % 10%阈值
% 计算阈值对应的索引(256阶中,前128阶对应负振幅,后128阶对应正振幅)
high_amp_idx = round(128 * threshold_ratio);  % 10%对应的索引(约102)
low_amp_idx = 128 - high_amp_idx;             % 渐变区域的索引长度(约26)


% --------------------------
% 波峰区域(正振幅):后128阶
% --------------------------
% 1. 高振幅区域(超过10%):纯蓝色(索引128+high_amp_idx ~ 256)
for i = high_amp_idx:128
    cmap(i+128, :) = blue_peak;  % 直接赋值纯色
end
% 2. 低振幅区域(低于10%):从白色渐变到浅蓝(索引128+1 ~ 128+high_amp_idx-1)
for i = 1:(high_amp_idx-1)
    ratio = i / (high_amp_idx-1);  % 0~1渐变比例
    cmap(i+128, :) = white_mid + (blue_peak - white_mid) * ratio;
end


% --------------------------
% 波谷区域(负振幅):前128阶
% --------------------------
% 1. 高振幅区域(低于-10%):纯红色(索引1 ~ low_amp_idx)
for i = 1:low_amp_idx
    cmap(i, :) = red_trough;  % 直接赋值纯色
end
% 2. 低振幅区域(高于-10%):从浅红渐变到白色(索引low_amp_idx+1 ~ 128)
for i = (low_amp_idx+1):128
    ratio = (i - low_amp_idx) / (128 - low_amp_idx);  % 0~1渐变比例
    cmap(i, :) = red_trough + (white_mid - red_trough) * ratio;
end
%% 动画绘制
figure('Position', [300 0 800 800]);
title('两个波源的干涉图样 (蓝色:波峰, 红色:波谷)');
xlabel('x'); ylabel('y');

for i = 1:n_frames
    % 逐渐改变波源1的频率
    f1 = f1_start + (f1_end - f1_start) * (i / n_frames);
    
    % 计算到两个波源的距离
    r1 = sqrt((X - x1).^2 + (Y - y1).^2);
    r2 = sqrt((X - x2).^2 + (Y - y2).^2);
    
    % 避免除以零
    r1(r1 < 1e-6) = 1e-6;
    r2(r2 < 1e-6) = 1e-6;
    
    % 计算两个波的相位
    phi1 = k * r1 - 2 * pi * f1 * t(i);
    phi2 = k * r2 - 2 * pi * f2 * t(i);
    
    % 计算两个波的振幅 (球面波振幅随1/r衰减)
    wave1 = A1 * cos(phi1) ./ r1;
    wave2 = A2 * cos(phi2) ./ r2;
    
    % 合成波 (干涉)
    total_wave = wave1 + wave2;
    
    % 使用固定范围进行归一化,确保颜色标尺一致
    total_wave_clamped = max(min(total_wave, fixed_max), fixed_min); % 限制范围
    total_wave_norm = (total_wave_clamped - fixed_min) / (fixed_max - fixed_min); % 归一化到[0,1]
    
    % 绘制干涉图样
    imagesc(x, y, total_wave_clamped);
    colormap(cmap);
    colorbar;
    caxis([fixed_min fixed_max]); % 固定颜色轴范围
    axis equal tight;
    title(['两个波源的干涉图样 (f1 = ' num2str(f1, '%.2f') ', f2 = ' num2str(f2) ')']);
    
    drawnow;

end

disp('仿真完成!');

除此之外,在三维仿真时,可以掏空波谷部分,这样就能够更加直观地显示,但感觉MATLAB实现起来比较有难度。

(挖个坑,挖个坑……)

正式仿真:

首先,为了让代码看起来简洁高效,对上面两个“前情提要”进行封装:

1. 保存柏拉图多面体基础信息

创建 save_plato_polyhedra.m 脚本并运行:

% 定义柏拉图多面体顶点、面及名称并保存为mat文件
phi = (1 + sqrt(5)) / 2; 

% 正四面体
tetra = struct( ...
    'name', '正四面体', ...
    'vertices', [1 1 1; 1 -1 -1; -1 1 -1; -1 -1 1], ...
    'faces', [1 2 3; 1 2 4; 1 3 4; 2 3 4] ...
);

% 正六面体(立方体)
cube = struct( ...
    'name', '正六面体', ...
    'vertices', [-1 -1 -1; 1 -1 -1; 1 1 -1; -1 1 -1; -1 -1 1; 1 -1 1; 1 1 1; -1 1 1], ...
    'faces', [1 2 3 4; 5 6 7 8; 1 2 6 5; 2 3 7 6; 3 4 8 7; 4 1 5 8] ...
);

% 正八面体
octa = struct( ...
    'name', '正八面体', ...
    'vertices', [1 0 0; -1 0 0; 0 1 0; 0 -1 0; 0 0 1; 0 0 -1], ...
    'faces', [1 3 5; 1 5 4; 1 4 6; 1 6 3; 2 3 6; 2 6 4; 2 4 5; 2 5 3] ...
);

% 正十二面体
dodeca = struct( ...
    'name', '正十二面体', ...
    'vertices', [ ...
        1, 1, 1; 1, 1, -1; 1, -1, 1; 1, -1, -1; -1, 1, 1; -1, 1, -1; ...
        -1, -1, 1; -1, -1, -1; phi, 1/phi, 0; phi, -1/phi, 0; -phi, 1/phi, 0; ...
        -phi, -1/phi, 0; 1/phi, 0, phi; 1/phi, 0, -phi; -1/phi, 0, phi; ...
        -1/phi, 0, -phi; 0, phi, 1/phi; 0, phi, -1/phi; 0, -phi, 1/phi; 0, -phi, -1/phi ...
    ], ...
    'faces', [ ...
        5 11 12 7 15; 8 12 7 19 20; 4 20 8 16 14; 2 9 10 4 14; ...
        6 18 2 14 16; 5 17 1 13 15; 11 12 8 16 6; 7 19 3 13 15; ...
        4 10 3 19 20; 1 13 3 10 9; 1 9 2 18 17; 11 6 18 17 5 ...
    ] ...
);

% 正二十面体
icosa = struct( ...
    'name', '正二十面体', ...
    'vertices', [ ...
        0   1   phi; 0   1  -phi; 0  -1   phi; 0  -1  -phi; ...
        1   phi   0; 1  -phi   0; -1   phi   0; -1  -phi   0; ...
        phi   0   1; -phi   0   1; phi   0  -1; -phi   0  -1 ...
    ], ...
    'faces', [ ...
        1  3  9; 1  3 10; 1 10  7; 1  5  7; 1  5  9; ...
        2  7 12; 2  5  7; 2  4 12; 2  4 11; 2  5 11; ...
        3  6  8; 3  6  9; 3  8 10; 4  6  8; 4  6 11; ...
        4  8 12; 5  9 11; 6  9 11; 7 10 12; 8 10 12 ...
    ] ...
);

% 整合为结构体数组并保存
plato_polyhedra = [tetra, cube, octa, dodeca, icosa];
save('plato_polyhedra.mat', 'plato_polyhedra');
disp('柏拉图多面体数据已成功保存!');
2. 保存颜色配置信息

创建 save_color_config.m 脚本并运行:

% 定义波干涉颜色映射及参数并保存为mat文件
% 基础颜色定义
blue_peak = [0 0 1];         % 波峰颜色(蓝色)
red_trough = [1 0 0];        % 波谷颜色(红色)
white_mid = [1 1 1];         % 中间过渡色(白色)
threshold_ratio = 0.1;       % 纯色与渐变色阈值比例

% 生成颜色映射(256级)
cmap = zeros(256, 3);
high_amp_idx = round(128 * threshold_ratio);  % 波峰纯色起始索引
low_amp_idx = 128 - high_amp_idx;             % 波谷纯色结束索引

% 波峰区域(正振幅):后128级
for i = high_amp_idx:128
    cmap(i+128, :) = blue_peak;
end
for i = 1:(high_amp_idx-1)
    ratio = i / (high_amp_idx-1);
    cmap(i+128, :) = white_mid + (blue_peak - white_mid) * ratio;
end

% 波谷区域(负振幅):前128级
for i = 1:low_amp_idx
    cmap(i, :) = red_trough;
end
for i = (low_amp_idx+1):128
    ratio = (i - low_amp_idx) / (128 - low_amp_idx);
    cmap(i, :) = red_trough + (white_mid - red_trough) * ratio;
end

% 保存颜色配置
save('wave_color_config.mat', 'cmap', 'blue_peak', 'red_trough', 'threshold_ratio');

第二步:主仿真代码(加载数据并运行仿真)

创建 polyhedron_wave_simulation.m 脚本:

(注:这里是直接使用解析式求解的版本,只展现了柏拉图多面体表面上的振幅)

% 柏拉图多面体表面波源干涉仿真(固定频率版)
% 功能:在多面体表面绘制两固定频率波源的干涉纹样,波峰蓝/波谷红/中间渐变
clear; clc; close all;

%% 加载外部数据
load('plato_polyhedra.mat', 'plato_polyhedra');  % 加载多面体数据
load('wave_color_config.mat', 'cmap');           % 加载颜色配置

%% 核心参数设置
% 波源参数(频率固定)
A1 = 4.2;                  % 波源1振幅
A2 = 1.9;                  % 波源2振幅
f1 = 0.33;                 % 波源1固定频率(不再变化)
f2 = 1.0;                  % 波源2固定频率
k = 2;                     % 波数(2π/λ)

% 三维波源位置(多面体内部坐标)
source1 = [0.5, 0.2, -0.3];   % 波源1空间坐标
source2 = [-0.3, -0.4, 0.6];  % 波源2空间坐标

% 时间参数(确保每个多面体仿真10秒)
t_total = 10;               % 单多面体仿真总时长(秒)
dt = 0.05;                  % 时间步长(减小步长提升流畅度)
t = 0:dt:t_total;           % 时间序列
n_frames = length(t);       % 总帧数
frame_delay = dt;           % 每帧停留时间(控制播放速度)

% 振幅范围(对称固定,确保颜色映射稳定)
fixed_max = A1 + A2;
fixed_min = -fixed_max;

%% 逐个多面体进行仿真
for poly_idx = 1:length(plato_polyhedra)
    % 获取当前多面体数据
    poly = plato_polyhedra(poly_idx);
    name = poly.name;
    vertices = poly.vertices;  % 体表顶点坐标(用于计算波振幅)
    faces = poly.faces;        % 多面体面定义

    % 创建专属显示窗口
    fig = figure('Position', [300 200 800 800]);
    title([name ' 表面波干涉仿真(f1=' num2str(f1) ', f2=' num2str(f2) ')'], 'FontSize', 14);

    % 预计算顶点到波源的距离(体表点到波源的空间距离)
    r1 = sqrt(sum((vertices - source1).^2, 2));  % 顶点到波源1的距离
    r2 = sqrt(sum((vertices - source2).^2, 2));  % 顶点到波源2的距离
    r1(r1 < 1e-6) = 1e-6;  % 避免距离为0导致除零
    r2(r2 < 1e-6) = 1e-6;

    % 动画循环(逐帧更新干涉纹样)
    for frame = 1:n_frames
        t_current = t(frame);  % 当前时间点

        % 计算两列波在体表顶点的振幅(球面波公式:A*cos(kr-ωt)/r)
        phi1 = k * r1 - 2 * pi * f1 * t_current;  % 波源1相位
        phi2 = k * r2 - 2 * pi * f2 * t_current;  % 波源2相位
        wave1 = A1 * cos(phi1) ./ r1;             % 波源1在体表的振幅
        wave2 = A2 * cos(phi2) ./ r2;             % 波源2在体表的振幅
        total_wave = wave1 + wave2;               % 干涉后总振幅
        total_wave_clamped = max(min(total_wave, fixed_max), fixed_min);  % 限制振幅范围

        % 计算顶点颜色索引(映射到颜色表)
        color_idx = round(((total_wave_clamped - fixed_min) / (fixed_max - fixed_min)) * 255) + 1;
        color_idx = max(1, min(256, color_idx));  % 确保索引合法
        vertex_colors = cmap(color_idx, :);       % 顶点对应的颜色

        % 绘制多面体表面(通过顶点颜色插值显示体表纹样)
        cla;  % 清空当前轴
        patch('Vertices', vertices, 'Faces', faces, ...
            'FaceColor', 'interp', 'FaceVertexCData', vertex_colors, ...
            'EdgeColor', [0.3 0.3 0.3], 'LineWidth', 0.5, 'FaceAlpha', 0.9);
        axis equal;  % 等比例显示
        axis off;    % 隐藏坐标轴
        view(3);     % 三维视角
        camlight('headlight');  % 头部灯光(增强立体感)
        lighting gouraud;       % 平滑着色(提升表面过渡效果)
        drawnow;                % 刷新画面

        % 控制帧间隔,确保总仿真时间为10秒
        if frame < n_frames
            pause(frame_delay);
        end
    end

    % 单个多面体仿真结束后停留1秒再切换
    pause(1);
    close(fig);
end

disp('所有多面体波干涉仿真已完成!');

代码说明

  1. 数据与逻辑分离
    多面体的顶点 / 面 / 名称、颜色映射等静态数据通过独立脚本保存为 mat 文件,主代码仅通过load加载,大幅减少冗余。

  2. 固定频率仿真
    移除了原代码中波源 1 的频率变化逻辑,f1f2均设为固定值,符合 “频率不再变化” 的需求。

  3. 体表纹样绘制
    通过计算多面体顶点(体表点) 的波振幅,结合patch函数的'FaceColor', 'interp'参数,实现表面颜色插值,使干涉纹样均匀分布在多面体表面。

  4. 仿真时间控制
    通过dt(时间步长)和frame_delay(帧间隔)联动控制,确保每个多面体的仿真播放时间严格为 10 秒。

运行步骤:先运行save_plato_polyhedra.msave_color_config.m生成数据文件,再运行polyhedron_wave_simulation.m即可看到仿真效果。

效果比较魔幻,我也不知道是不是真的是这样子的,毕竟咱也没真正做过实验。

感觉应该是不太对劲
​​​​​​红色表示波峰,蓝色表示波谷?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值