% ===================== 动态3D散点图(MATLAB 2024b 适配) =...
Создано: 21 января 2026 г.
Отвечено с использованием GPT-5.2 Thinking от Chat01
Создано: 21 января 2026 г.
Отвечено с использованием GPT-5.2 Thinking от Chat01
% ===================== 动态3D散点图(MATLAB 2024b 适配) =====================
close all; clear; clc;
rng(2026); % 固定随机种子,结果可复现
set(0,'DefaultFigurePosition',[100,100,1400,900]); % 全局窗口大小
set(0,'DefaultFigureColor','white'); % 全局背景色
% ---------------------- 1. 核心参数 ----------------------
n_points = 150; % 散点数量
freq = 0.05; % 波动频率
amp = 0.3; % 波动幅度(减小以避免过度扩散)
rotate_speed = 0.6; % 自动旋转速度
scale_freq = 0.02; % 缩放频率
scale_amp = 0.4; % 缩放幅度(0.6到1.4之间变化)
point_rotate_speed = 0.01; % 点云旋转速度(弧度/迭代)
update_fps = 30; % 刷新帧率
max_iter = 10000; % 最大迭代次数
bounds = [0 10]; % 边界范围(用于软约束)
% ---------------------- 2. 创建图形+坐标轴(修复空白核心) ----------------------
fig = figure('Name','动态3D散点图(MATLAB 2024b)','NumberTitle','off');
fig.WindowKeyPressFcn = @keyPressCallback; % 绑定按键回调
% 创建原生Axes,修复标签+交互设置
ax = axes('Parent',fig);
hold(ax,'on'); % 先hold on,避免绘图被覆盖
ax.Projection = 'perspective'; % 透视投影
ax.XLim = bounds; ax.YLim = bounds; ax.ZLim = bounds; % 固定坐标范围
% 修复:原生坐标轴标签(替代错误的text创建方式)
ax.XLabel.String = 'X轴'; ax.XLabel.FontSize = 12;
ax.YLabel.String = 'Y轴'; ax.YLabel.FontSize = 12;
ax.ZLabel.String = 'Z轴'; ax.ZLabel.FontSize = 12;
ax.GridAlpha = 0.3; ax.LineWidth = 0.8;
ax.Box = 'on'; % 显示坐标轴边框
% 修复:2024b正确开启交互的方式(替代无效的EnableRotate3D)
rotate3d(ax,'on'); % 开启鼠标3D旋转
pan(ax,'on'); % 开启鼠标平移
zoom(ax,'on'); % 开启鼠标缩放
% ---------------------- 3. 初始化散点数据 ----------------------
points = 10 * rand(n_points, 3); % [X Y Z]
sizes = linspace(30, 120, n_points); % 散点大小梯度
colors = points(:,3)/max(points(:,3)); % 颜色随Z值变化
center = mean(points); % 初始中心
% 计算Delaunay三角化以获取连接边(固定连接)
DT = delaunayTriangulation(points);
edges_list = edges(DT); % 获取所有独特的边
% ---------------------- 4. 创建散点对象(确保显示) ----------------------
h_scatter = scatter3(ax, points(:,1), points(:,2), points(:,3), sizes, colors, 'filled', 'Marker','o');
h_scatter.MarkerEdgeColor = [0.1,0.1,0.1]; % 散点边框
h_scatter.MarkerFaceAlpha = 0.8; % 半透明填充
% 创建连线对象(使用一个plot3以NaN分隔多段线)
line_x = nan(3size(edges_list,1),1);
line_y = nan(3size(edges_list,1),1);
line_z = nan(3size(edges_list,1),1);
for i = 1:size(edges_list,1)
idx = (i-1)3 + 1;
line_x(idx:idx+1) = points(edges_list(i,:),1);
line_y(idx:idx+1) = points(edges_list(i,:),2);
line_z(idx:idx+1) = points(edges_list(i,:),3);
% NaN分隔
end
h_lines = plot3(ax, line_x, line_y, line_z, 'Color',[0.5 0.5 0.5 0.5], 'LineWidth',0.5);
% ---------------------- 5. 颜色条(修复后) ----------------------
cb = colorbar(ax,'Location','eastoutside');
cb.Label.String = 'Z轴数值(颜色映射)';
cb.FontSize = 10;
colormap(ax, parula);
% ---------------------- 6. 全局状态变量 ----------------------
global is_auto_rotate is_pause azimuth elevation;
is_auto_rotate = true;
is_pause = false;
azimuth = 0;
elevation = 30;
% ---------------------- 7. 操作提示(修复字符串拼接) ----------------------
hint_text = text(ax, 0.02, 0.98, 0, ...
['操作说明:\n', ...
' 🖱️ 鼠标:左键拖动旋转 | 滚轮缩放 | 右键平移\n', ...
' ␣ 空格:切换自动旋转/手动\n', ...
' P 键:暂停/继续更新\n', ...
' ESC键:退出程序'], ...
'Units','normalized', 'VerticalAlignment','top', ...
'FontSize',10, 'Color','#333333', 'BackgroundColor','#F5F5F5', ...
'EdgeColor','#CCCCCC', 'Margin',5, 'Interpreter','none');
% ---------------------- 8. 主循环(动态更新) ----------------------
fprintf('程序启动!按ESC退出,空格切换旋转,P键暂停\n');
for iter = 1:max_iter
if ~ishandle(fig), break; end % 窗口关闭则退出
% 暂停逻辑
if is_pause
drawnow limitrate;
pause(1/update_fps);
continue;
end
% 自动旋转视图
if is_auto_rotate
azimuth = azimuth + rotate_speed;
if azimuth >= 360, azimuth = 0; end
view(ax, azimuth, elevation);
end
% 点云动态更新:旋转 + 缩放 + 小波动(避免收敛)
% 1. 小波动(正弦扰动,避免纯随机扩散)
delta = amp * sin(iter * freq + points);
points = points + delta * 0.1; % 小步扰动
% 软约束:如果超出边界,向中心拉回(不硬clip,避免收敛)
dist_to_center = points - center;
over_bound = max(abs(dist_to_center), [], 2) > (bounds(2)-bounds(1))/2 * 1.5;
points(over_bound,:) = points(over_bound,:) - 0.05 * dist_to_center(over_bound,:);
% 2. 整体缩放(从中心)
scale_factor = 1 + scale_amp * sin(iter * scale_freq);
points = center + scale_factor * (points - center);
% 3. 整体旋转(围绕中心,Y轴旋转示例)
theta = point_rotate_speed;
rot_matrix = [cos(theta) 0 sin(theta); 0 1 0; -sin(theta) 0 cos(theta)]; % Y轴旋转
points = center + (points - center) * rot_matrix;
% 更新颜色
colors = points(:,3) / max(points(:,3));
% 刷新散点属性
h_scatter.XData = points(:,1);
h_scatter.YData = points(:,2);
h_scatter.ZData = points(:,3);
h_scatter.CData = colors;
% 更新连线
line_x = nan(3size(edges_list,1),1);
line_y = nan(3size(edges_list,1),1);
line_z = nan(3*size(edges_list,1),1);
for i = 1:size(edges_list,1)
idx = (i-1)*3 + 1;
line_x(idx:idx+1) = points(edges_list(i,:),1);
line_y(idx:idx+1) = points(edges_list(i,:),2);
line_z(idx:idx+1) = points(edges_list(i,:),3);
end
h_lines.XData = line_x;
h_lines.YData = line_y;
h_lines.ZData = line_z;
% 更新标题(无三元运算符)
if is_auto_rotate
rotate_status = '开启';
else
rotate_status = '关闭';
end
if is_pause
pause_status = '暂停';
else
pause_status = '运行';
end
ax.Title.String = sprintf('动态3D散点图 | 迭代:%d/%d | 自动旋转:%s | 状态:%s | 缩放因子:%.2f', ...
iter, max_iter, rotate_status, pause_status, scale_factor);
ax.Title.FontSize = 12;
% 高效刷新
drawnow limitrate;
pause(1/update_fps);
end
% ---------------------- 9. 按键回调函数 ----------------------
function keyPressCallback(src, event)
global is_auto_rotate is_pause;
key = event.Key;
switch key
case 'escape' % ESC退出
fprintf('检测到ESC键,退出程序\n');
close(src);
case 'space' % 空格切换自动旋转
is_auto_rotate = ~is_auto_rotate;
if is_auto_rotate
status = '开启';
else
status = '关闭';
end
fprintf('自动旋转已%s\n', status);
case 'p' % P键暂停/继续
is_pause = ~is_pause;
if is_pause
status = '暂停';
else
status = '继续';
end
fprintf('数据更新已%s\n', status);
end
end
% ---------------------- 10. 结束提示 ----------------------
fprintf('程序正常结束!\n');
我要实现三维散点连线后视觉缩放旋转的动态效果,不管怎么迭代都会在可见的框图范围内,不会超出范围或者迭代成一个点,请帮我修正代码
下面给你一版“永远在可见立方体范围内、不会越界、也不会迭代塌缩成一个点”的修正版。核心思路是:
points = points + ... 会长期漂移 + 被拉回,最后容易“塌缩/贴边/越界”)。[0,10]。matlab% ===================== 动态3D散点图(MATLAB 2024b 适配|不越界不塌缩) ===================== close all; clear; clc; rng(2026); set(0,'DefaultFigurePosition',[100,100,1400,900]); set(0,'DefaultFigureColor','white'); % ---------------------- 1. 核心参数 ---------------------- n_points = 150; freq = 0.05; % 波动频率 amp = 0.30; % 波动幅度(在归一化空间中使用,会更稳定) rotate_speed = 0.6; % 视角自动旋转速度(度/帧) scale_freq = 0.02; % 呼吸缩放频率 scale_amp = 0.40; % 呼吸缩放幅度(0.6~1.4) point_rotate_speed = 0.01; % 点云自转速度(弧度/帧) update_fps = 30; max_iter = 10000; bounds = [0 10]; % 盒子内边距:避免贴边(你可以调大/调小) margin = 0.35; % 数据保证落在 [bounds(1)+margin, bounds(2)-margin] % ---------------------- 2. 创建图形+坐标轴 ---------------------- fig = figure('Name','动态3D散点图(MATLAB 2024b|不越界不塌缩)','NumberTitle','off'); fig.WindowKeyPressFcn = @keyPressCallback; ax = axes('Parent',fig); hold(ax,'on'); ax.Projection = 'perspective'; ax.XLim = bounds; ax.YLim = bounds; ax.ZLim = bounds; ax.XLabel.String = 'X轴'; ax.XLabel.FontSize = 12; ax.YLabel.String = 'Y轴'; ax.YLabel.FontSize = 12; ax.ZLabel.String = 'Z轴'; ax.ZLabel.FontSize = 12; ax.GridAlpha = 0.3; ax.LineWidth = 0.8; ax.Box = 'on'; rotate3d(ax,'on'); pan(ax,'on'); zoom(ax,'on'); % ---------------------- 3. 归一化初始化(关键:不累积漂移) ---------------------- L = bounds(2) - bounds(1); center = [mean(bounds), mean(bounds), mean(bounds)]; half_usable = (L/2 - margin); % 可用半径(映射后永远在盒子内) % base_u:固定的“基准点云”(归一化空间里,范围约 [-1,1]) base_u = (rand(n_points,3) - 0.5) * 2; % 给每个点一个相位,让波动更丰富但可控 phase = 2*pi*rand(n_points,3); % 初始映射到数据空间(在盒子内) u0 = base_u; points = center + half_usable * u0; % Delaunay 三角化(固定连线拓扑) DT = delaunayTriangulation(points); edges_list = edges(DT); ne = size(edges_list,1); % ---------------------- 4. 绘图对象 ---------------------- sizes = linspace(30, 120, n_points); colors = (points(:,3) - bounds(1)) / (bounds(2)-bounds(1)); h_scatter = scatter3(ax, points(:,1), points(:,2), points(:,3), ... sizes, colors, 'filled', 'Marker','o'); h_scatter.MarkerEdgeColor = [0.1,0.1,0.1]; h_scatter.MarkerFaceAlpha = 0.8; % 连线:NaN 分段(向量化更新,别每帧for循环重建) line_x = nan(3*ne,1); line_y = nan(3*ne,1); line_z = nan(3*ne,1); idx1 = 1:3:(3*ne); % 每段线起点 idx2 = 2:3:(3*ne); % 每段线终点 line_x(idx1) = points(edges_list(:,1),1); line_x(idx2) = points(edges_list(:,2),1); line_y(idx1) = points(edges_list(:,1),2); line_y(idx2) = points(edges_list(:,2),2); line_z(idx1) = points(edges_list(:,1),3); line_z(idx2) = points(edges_list(:,2),3); h_lines = plot3(ax, line_x, line_y, line_z, 'Color',[0.55 0.55 0.55], 'LineWidth',0.5); % ---------------------- 5. 颜色条 ---------------------- cb = colorbar(ax,'Location','eastoutside'); cb.Label.String = 'Z轴数值(颜色映射)'; cb.FontSize = 10; colormap(ax, parula); % ---------------------- 6. 全局状态变量 ---------------------- global is_auto_rotate is_pause azimuth elevation; is_auto_rotate = true; is_pause = false; azimuth = 0; elevation = 30; % ---------------------- 7. 操作提示 ---------------------- hint_text = text(ax, 0.02, 0.98, 0, ... ['操作说明:\n', ... ' 🖱️ 鼠标:左键拖动旋转 | 滚轮缩放 | 右键平移\n', ... ' ␣ 空格:切换自动旋转/手动\n', ... ' P 键:暂停/继续更新\n', ... ' ESC键:退出程序'], ... 'Units','normalized', 'VerticalAlignment','top', ... 'FontSize',10, 'Color','#333333', 'BackgroundColor','#F5F5F5', ... 'EdgeColor','#CCCCCC', 'Margin',5, 'Interpreter','none'); % ---------------------- 8. 主循环 ---------------------- fprintf('程序启动!按ESC退出,空格切换旋转,P键暂停\n'); cloud_angle = 0; % 点云自转累计角度(弧度) for iter = 1:max_iter if ~ishandle(fig), break; end if is_pause drawnow limitrate; pause(1/update_fps); continue; end % 视角自动旋转 if is_auto_rotate azimuth = azimuth + rotate_speed; if azimuth >= 360, azimuth = azimuth - 360; end view(ax, azimuth, elevation); end % --------- A) 在归一化空间 u 里生成“永不发散”的动态点云 --------- % 1) 波动(不累积漂移:每帧都从 base_u 生成) wobble = amp * sin(iter*freq + phase); u = base_u + 0.18 * wobble; % 0.18是步幅系数,可调(越大越“活”) % 2) 呼吸缩放(周期性,不会越来越大/越来越小) scale_factor = 1 + scale_amp * sin(iter * scale_freq); u = u * scale_factor; % 3) 点云自转(绕Y轴) cloud_angle = cloud_angle + point_rotate_speed; theta = cloud_angle; rotY = [cos(theta) 0 sin(theta); 0 1 0; -sin(theta) 0 cos(theta)]; u = u * rotY; % --------- B) 自适应“塞回盒子里”:只缩放,不裁剪 --------- % 让 u 的每个维度都落在 [-1,1](若超出则整体缩小) maxabs = max(abs(u), [], 1); % 1x3 fit = min([1, 1./maxabs]); % 标量:<=1 u = u * fit; % --------- C) 映射回数据空间(保证在 bounds 内 + 留margin) --------- points = center + half_usable * u; % 颜色(按Z映射到0~1,稳) colors = (points(:,3) - bounds(1)) / (bounds(2)-bounds(1)); colors = min(max(colors,0),1); % 更新散点 h_scatter.XData = points(:,1); h_scatter.YData = points(:,2); h_scatter.ZData = points(:,3); h_scatter.CData = colors; % 更新连线(向量化) line_x(idx1) = points(edges_list(:,1),1); line_x(idx2) = points(edges_list(:,2),1); line_y(idx1) = points(edges_list(:,1),2); line_y(idx2) = points(edges_list(:,2),2); line_z(idx1) = points(edges_list(:,1),3); line_z(idx2) = points(edges_list(:,2),3); h_lines.XData = line_x; h_lines.YData = line_y; h_lines.ZData = line_z; % 标题 if is_auto_rotate, rotate_status = '开启'; else, rotate_status = '关闭'; end if is_pause, pause_status = '暂停'; else, pause_status = '运行'; end ax.Title.String = sprintf(['动态3D散点图 | 迭代:%d/%d | 自动旋转:%s | 状态:%s | ', ... '缩放因子:%.2f | fit:%.2f'], ... iter, max_iter, rotate_status, pause_status, scale_factor, fit); ax.Title.FontSize = 12; drawnow limitrate; pause(1/update_fps); end fprintf('程序正常结束!\n'); % ---------------------- 9. 按键回调函数 ---------------------- function keyPressCallback(src, event) global is_auto_rotate is_pause; switch event.Key case 'escape' fprintf('检测到ESC键,退出程序\n'); close(src); case 'space' is_auto_rotate = ~is_auto_rotate; if is_auto_rotate, status = '开启'; else, status = '关闭'; end fprintf('自动旋转已%s\n', status); case 'p' is_pause = ~is_pause; if is_pause, status = '暂停'; else, status = '继续'; end fprintf('数据更新已%s\n', status); end end
base_u 生成 u,而不是不断 points = points + ...。如果你还想要更“真实的漂移感”(但仍然不越界),我也可以给你加一个**受限的低频噪声(bounded random walk)**版本:看起来像“乱中有序”飘动,但永远不会跑飞、也不会塌缩。你更喜欢“偏规则呼吸旋转”还是“偏随机漂移但稳定”?