MATLAB | 趣味编程——动态字符串甜甜圈和爱心

hello 大家好久不见,今天来一期趣味编程,代码运行效果:

甜甜圈代码复刻自 Andy Sloane 的博文:

C 语言代码长这样:

但是这段代码需要 ANSI 或 VT100 仿真环境,我尝试将其改成了 MATLAB 代码并且将循环改成了矩阵运算,提高了代码运行效率。

代码原理作者在这篇博文有讲:

就是在圆环表面均匀取散点当作取样点,然后和观察点连线与一个平面相交取交点 (就是把立体图形映射为平面),对其进行取整映射到像素点(字符的行列坐标),然后每一个像素点只记录距离比较近的采样点的亮度,然后将不同亮度转换为不同字符。

MATLAB 甜甜圈

matlab 复制代码
% # MATLAB port of donut.c
% + Original author : Andy Sloane (a1k0n)
%   - Initial code release : https://www.a1k0n.net/2006/09/15/obfuscated-c-donut.html
%   - Detailed explanation : https://www.a1k0n.net/2011/07/20/donut-math.html
%
% + C version without math library :
%   - Author : kjkent (AUR: donut.c)
%   - Principle : https://www.a1k0n.net/2021/01/13/optimizing-donut.html
%   - Reference : https://aur.archlinux.org/packages/donut.c
%
% + MATLAB port : slandarer

% Figure, axes, text handle (界面、坐标区域、文本句柄)
fig = figure('Units','pixels', 'Position',[300,300,600,600]);
ax = axes('Parent',fig, 'Position',[0,0,1,1], 'XLim',[-1,1], 'YLim',[-1,1], ...
    'XColor','none', 'YColor','none', 'Color',[0,0,0]);
txtHdl = text(ax, 0, - .65, 'slandarer', 'Color',[1,1,1], 'FontSize',12, ...
    'FontName','Consolas', 'HorizontalAlignment','center', 'VerticalAlignment','bottom');
text(ax, 0, -.65, {'Original implementation by Andy Sloane;'; 'MATLAB port by slandarer.'}, ...
    'Color',[1,1,1], 'FontSize',17, 'FontName','Consolas', ...
    'HorizontalAlignment','center', 'VerticalAlignment','top')


W = 80; % Characters per row (每行字符数)
H = 22; % Total rows (总行数)
tTxt = repmat(' ', 1, W*H);
tTxt((1:H).*W) = newline;

% Rotation angles (旋转角)
A = 1; B = 1; 

% Sampling grid (采样点集)
theta = 0:0.07:6.28; phi = 0:0.02:6.28;
[ct, cp] = meshgrid(cos(theta), cos(phi)); ct = ct(:); cp = cp(:);
[st, sp] = meshgrid(sin(theta), sin(phi)); st = st(:); sp = sp(:);
h = ct + 2; % R1 + R2*cos(theta)

% Luminance character ramp (亮度对应字符)
lumChar = '.,-~:;=!*#$@';
lumN = length(lumChar);

for i = 1:1e10
    lTxt = tTxt;
    A = A + .07; B = B + .03;
    cA = cos(A); sA = sin(A);
    cB = cos(B); sB = sin(B);
    
    D = 1./(sp.*h.*sA + st.*cA + 5);
    t = sp.*h.*cA - st.*sA;

    % Project to screen coordinates (投影到屏幕坐标)
    x = floor(40 + 30.*D.*(cp.*h.*cB - t.*sB));
    y = floor(12 + 15.*D.*(cp.*h.*sB + t.*cB));
    lum = floor(8.*((st.*sA - sp.*ct.*cA).*cB - sp.*ct.*sA - st.*cA - cp.*ct.*sB));

    bool = y >= 0 & y < H - 1 & x >= 0 & x < W - 1;
    bx = x(bool); by = y(bool); 
    bD = D(bool); bLum = lum(bool);
    label = bx + W.*by + 1;

    % Keep max-D point per cell and map its luminance to char index
    % (每个单元格保留最大 D 的点,并将其亮度映射为字符索引)
    sD = sortrows([label, bD, bLum], [1, 2, 3], 'descend');
    [sL, idx, ~] = unique(sD(:, 1), 'stable');
    idx = min(max(sD(idx, 3), 0), lumN - 1) + 1;

    lTxt(sL) = lumChar(idx);
    txtHdl.String = lTxt;
    pause(.05)
end

含背景 MATLAB 甜甜圈

原作者还发布了带背景版本:

我这里简化了些,直接横纵方向取cos函数相乘然后检测是否大于0来构造网格,同时根据景深不同乘了不同系数来模拟近大远小:

matlab 复制代码
% # MATLAB port of donut.c
% + Original author : Andy Sloane (a1k0n)
%   - Initial code release : https://www.a1k0n.net/2006/09/15/obfuscated-c-donut.html
%   - Detailed explanation : https://www.a1k0n.net/2011/07/20/donut-math.html
%
% + C version without math library :
%   - Author : kjkent (AUR: donut.c)
%   - Principle : https://www.a1k0n.net/2021/01/13/optimizing-donut.html
%   - Reference : https://aur.archlinux.org/packages/donut.c
%
% + MATLAB port : slandarer

% Figure, axes, text handle (界面、坐标区域、文本句柄)
fig = figure('Units','pixels', 'Position',[300,300,600,600]);
ax = axes('Parent',fig, 'Position',[0,0,1,1], 'XLim',[-1,1], 'YLim',[-1,1], ...
    'XColor','none', 'YColor','none', 'Color',[0,0,0]);
txtHdl = text(ax, 0, - .65, 'slandarer', 'Color',[1,1,1], 'FontSize',12, ...
    'FontName','Consolas', 'HorizontalAlignment','center', 'VerticalAlignment','bottom');
text(ax, 0, -.65, {'Original implementation by Andy Sloane;'; 'MATLAB port by slandarer.'}, ...
    'Color',[1,1,1], 'FontSize',17, 'FontName','Consolas', ...
    'HorizontalAlignment','center', 'VerticalAlignment','top')


W = 80; % Characters per row (每行字符数)
H = 22; % Total rows (总行数)

% Rotation angles (旋转角)
A = 1; B = 1; 

% Background translation (背景位移)
T1 = 0; T2 = 0;

% Sampling grid (采样点集)
theta = 0:0.07:6.28; phi = 0:0.02:6.28;
[ct, cp] = meshgrid(cos(theta), cos(phi)); ct = ct(:); cp = cp(:);
[st, sp] = meshgrid(sin(theta), sin(phi)); st = st(:); sp = sp(:);
h = ct + 2; % R1 + R2*cos(theta)

[WM, HM] = meshgrid((1:W) - (1 + W)/2, 1:H);
CH = (HM - 1)./(H - 1);


% Luminance character ramp (亮度对应字符)
lumChar = '.,-~:;=!*#$@';
lumN = length(lumChar);

for i = 1:1e10
    % Update background translations (更新背景位移)
    T1 = T1 + .05; T2 = T2 + .5; 
    % Generate background pattern (生成背景图案)
    C1 = cos(WM.*pi./(8 + 3.*CH) + 10.*abs(mod(T1, 2) - 1));
    C2 = cos(HM.*pi./(3 + 2.*CH) - T2);
    % Threshold to create '.' and '#' pattern (阈值化为 '.' 和 '#' 图案)
    ZM = ((C1.*C2 > 0).*(lumN - 3) + 1).';
    tTxt = lumChar(ZM(:).');
    tTxt((1:H).*W) = newline;

    lTxt = tTxt;
    A = A + .07; B = B + .03;
    cA = cos(A); sA = sin(A);
    cB = cos(B); sB = sin(B);

    D = 1./(sp.*h.*sA + st.*cA + 5);
    t = sp.*h.*cA - st.*sA;

    % Project to screen coordinates (投影到屏幕坐标)
    x = floor(40 + 30.*D.*(cp.*h.*cB - t.*sB));
    y = floor(12 + 15.*D.*(cp.*h.*sB + t.*cB));
    lum = floor(8.*((st.*sA - sp.*ct.*cA).*cB - sp.*ct.*sA - st.*cA - cp.*ct.*sB));

    bool = y >= 0 & y < H - 1 & x >= 0 & x < W - 1;
    bx = x(bool); by = y(bool); 
    bD = D(bool); bLum = lum(bool);
    label = bx + W.*by + 1;

    % Keep max-D point per cell and map its luminance to char index
    % (每个单元格保留最大 D 的点,并将其亮度映射为字符索引)
    sD = sortrows([label, bD, bLum], [1, 2, 3], 'descend');
    [sL, idx, ~] = unique(sD(:, 1), 'stable');
    idx = min(max(sD(idx, 3), 0), lumN - 1) + 1;

    lTxt(sL) = lumChar(idx);
    txtHdl.String = lTxt;
    pause(.05)
end

MATLAB 爱心

matlab 复制代码
%% by slandarer
clear;clc;

% Figure, axes, text handle (界面、坐标区域、文本句柄)
fig = figure('Units','pixels', 'Position',[300,100,800,800]);
ax = axes('Parent',fig, 'Position',[0,0,1,1], 'XLim',[-1,1], 'YLim',[-1,1], ...
    'XColor','none', 'YColor','none', 'Color',[0,0,0]);
txtHdl = text(ax, 0, - .65, 'slandarer', 'Color',[1,1,1], 'FontSize',8, ...
    'FontName','Consolas', 'HorizontalAlignment','center', 'VerticalAlignment','bottom');
text(ax, 0, -.65, {'char heart                '; '-- made by slandarer.'}, ...
    'Color',[1,1,1], 'FontSize',21, 'FontName','Consolas', ...
    'HorizontalAlignment','center', 'VerticalAlignment','top')

t = linspace(-3, 3, 120); 
[X, Y, Z] = ndgrid(t, t, t);
F = ((-(X.^2).*(Z.^3) - (9/80).*(Y.^2).*(Z.^3)) + ((X.^2) + (9/4).*(Y.^2) + (Z.^2) - 1).^3);
FV = isosurface(F, 0); hPnts = FV.vertices; N = size(hPnts, 1);
hPnts = hPnts - repmat(mean(hPnts, 1), [N, 1]);
hPnts = hPnts./repmat(max(abs(hPnts).*[1.5,1,1], [], 1), [N, 1]);

W = 100; % Characters per row (每行字符数)
H = 50; % Total rows (总行数)
tTxt = repmat(' ', 1, W*H);
tTxt((1:H).*W) = newline;

% Luminance character ramp (亮度对应字符)
lumChar = '.,-~:;=!*#$@';
lumN = length(lumChar);

T = 0;
for i = 1:1e10
    lTxt = tTxt;
    T = T + .1; c = cos(T); s = sin(T);

    % Rotation matrix (旋转矩阵)
    R = [c, -s, 0;
         s,  c, 0;
         0,  0, 1];
    nPnts = hPnts*R;

    % Project to screen coordinates (投影到屏幕坐标)
    x = floor(50 + 50.*4.5.*nPnts(:,1)./(nPnts(:,2) + 5));
    y = floor(25 - 20.*4.5.*nPnts(:,3)./(nPnts(:,2) + 5));
    lum = floor(9 * (nPnts * [-1.1; -2; 3]));
    D = 1./(nPnts(:,2) + 5);

    bool = y >= 0 & y < H - 1 & x >= 0 & x < W - 1;
    bx = x(bool); by = y(bool); 
    bD = D(bool); bLum = lum(bool);
    label = bx + W.*by + 1;

    % Keep max-D point per cell and map its luminance to char index
    % (每个单元格保留最大 D 的点,并将其亮度映射为字符索引)
    sD = sortrows([label, bD, bLum], [1, 2, 3], 'descend');
    [sL, idx, ~] = unique(sD(:, 1), 'stable');
    idx = min(max(sD(idx, 3), 0), lumN - 1) + 1;

    lTxt(sL) = lumChar(idx);
    txtHdl.String = lTxt;
    pause(.05)
end

含背景 MATLAB 爱心

matlab 复制代码
%% by slandarer
clear;clc;

% Figure, axes, text handle (界面、坐标区域、文本句柄)
fig = figure('Units','pixels', 'Position',[300,100,800,800]);
ax = axes('Parent',fig, 'Position',[0,0,1,1], 'XLim',[-1,1], 'YLim',[-1,1], ...
    'XColor','none', 'YColor','none', 'Color',[0,0,0]);
txtHdl = text(ax, 0, - .65, 'slandarer', 'Color',[1,1,1], 'FontSize',7, ...
    'FontName','Consolas', 'HorizontalAlignment','center', 'VerticalAlignment','bottom');
text(ax, 0, -.65, {'char heart                '; '-- made by slandarer.'}, ...
    'Color',[1,1,1], 'FontSize',21, 'FontName','Consolas', ...
    'HorizontalAlignment','center', 'VerticalAlignment','top')

t = linspace(-3, 3, 120); 
[X, Y, Z] = ndgrid(t, t, t);
F = ((-(X.^2).*(Z.^3) - (9/80).*(Y.^2).*(Z.^3)) + ((X.^2) + (9/4).*(Y.^2) + (Z.^2) - 1).^3);
FV = isosurface(F, 0); hPnts = FV.vertices; N = size(hPnts, 1);
hPnts = hPnts - repmat(mean(hPnts, 1), [N, 1]);
hPnts = hPnts./repmat(max(abs(hPnts).*[1.5,1,1], [], 1), [N, 1]);

W = 100; % Characters per row (每行字符数)
H = 50; % Total rows (总行数)

[WM, HM] = meshgrid((1:W) - (1 + W)/2, 1:H);
CH = (HM - 1)./(H - 1);

% Background translation (背景位移)
T1 = 0; T2 = 0;

% Luminance character ramp (亮度对应字符)
lumChar = '.,-~:;=!*#$@';
lumN = length(lumChar);



T = 0;
for i = 1:1e10
    % Update background translations (更新背景位移)
    T1 = T1 + .05; T2 = T2 + .5; 
    % Generate background pattern (生成背景图案)
    C1 = cos(WM.*pi./(15 + 10.*CH) + 10.*abs(mod(T1, 2) - 1));
    C2 = cos(HM.*pi./(5 + 5.*CH) - T2);
    % Threshold to create '.' and '#' pattern (阈值化为 '.' 和 '#' 图案)
    ZM = ((C1.*C2 > 0).*(lumN - 3) + 1).';
    tTxt = lumChar(ZM(:).');
    tTxt((1:H).*W) = newline;

    lTxt = tTxt;
    T = T + .1; c = cos(T); s = sin(T);

    % Rotation matrix (旋转矩阵)
    R = [c, -s, 0;
        s,  c, 0;
        0,  0, 1];
    nPnts = hPnts*R;

    % Project to screen coordinates (投影到屏幕坐标)
    x = floor(50 + 50.*4.5.*nPnts(:,1)./(nPnts(:,2) + 5));
    y = floor(25 - 20.*4.5.*nPnts(:,3)./(nPnts(:,2) + 5));
    lum = floor(9 * (nPnts * [-1.1; -2; 3]));
    D = 1./(nPnts(:,2) + 5);

    bool = y >= 0 & y < H - 1 & x >= 0 & x < W - 1;
    bx = x(bool); by = y(bool); 
    bD = D(bool); bLum = lum(bool);
    label = bx + W.*by + 1;

    % Keep max-D point per cell and map its luminance to char index
    % (每个单元格保留最大 D 的点,并将其亮度映射为字符索引)
    sD = sortrows([label, bD, bLum], [1, 2, 3], 'descend');
    [sL, idx, ~] = unique(sD(:, 1), 'stable');
    idx = min(max(sD(idx, 3), 0), lumN - 1) + 1;

    lTxt(sL) = lumChar(idx);
    txtHdl.String = lTxt;
    pause(.05)
end
相关推荐
UIU1141 小时前
P1003 [NOIP 2011 提高组] 铺地毯
数据结构·c++·算法
爱读书的小胖1 小时前
LeetCode 88. 合并两个有序数组 C 解题分析
c语言·数据结构·算法
Matlab程序猿2 小时前
【MATLAB源码-第461期】基于matlab的高速铁路 双链路 QPSK 误码性能仿真与风险驱动自适应复制控制仿真
matlab·高速铁路通信·双链路通信·截止期约束
inrichtt2 小时前
第七课时:STM32时钟系统揭秘:从零理解时钟树
c语言·数据结构·stm32·单片机·嵌入式硬件
xxwl5853 小时前
区域赛算法模版第四章---数据结构
数据结构·c++·算法
Logic1014 小时前
C语言/数据结构二维数组题解:找出行列O个数均为偶数的格子——奇偶性预处理+二次遍历
c语言·数据结构·算法·二维数组·时间复杂度·动态内存分配·奇偶性
垆边人似月.5 小时前
移动长度 k 子串使字符串变成回文
数据结构·算法
David猪大卫5 小时前
【C++修炼】string类:string类的模拟实现
c语言·开发语言·数据结构·c++·经验分享·笔记·学习
jeo陽7 小时前
05-数组与冒泡排序:第一个数据结构与经典算法
c语言·数据结构·算法