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
