Tips & Tricks
Follow


Christmas Tree for Matlaber : Rotating With Falling Snowflakes

Zhaoxu Liu / slandarer on 15 Dec 2024 (Edited on 16 Dec 2024)
Latest activity Reply by Vasilis Bellos on 6 Jan 2025

Christmas is coming, here are two dynamic Christmas tree drawing codes:
Crystal XMas Tree
function XmasTree2024_1
fig = figure('Units','normalized', 'Position',[.1,.1,.5,.8],...
'Color',[0,9,33]/255, 'UserData',40 + [60,65,75,72,0,59,64,57,74,0,63,59,57,0,1,6,45,75,61,74,28,57,76,57,1,1]);
axes('Parent',fig, 'Position',[0,-1/6,1,1+1/3], 'UserData',97 + [18,11,0,13,3,0,17,4,17],...
'XLim',[-1.5,1.5], 'YLim',[-1.5,1.5], 'ZLim',[-.2,3.8], 'DataAspectRatio', [1,1,1], 'NextPlot','add',...
'Projection','perspective', 'Color',[0,9,33]/255, 'XColor','none', 'YColor','none', 'ZColor','none')
%% Draw Christmas tree
F = [1,3,4;1,4,5;1,5,6;1,6,3;...
2,3,4;2,4,5;2,5,6;2,6,3];
dP = @(V) patch('Faces',F, 'Vertices',V, 'FaceColor',[0 71 177]./255,...
'FaceAlpha',rand(1).*0.2+0.1, 'EdgeColor',[0 71 177]./255.*0.8,...
'EdgeAlpha',0.6, 'LineWidth',0.5, 'EdgeLighting','gouraud', 'SpecularStrength',0.3);
r = .1; h = .8;
V0 = [0,0,0; 0,0,1; 0,r,h; r,0,h; 0,-r,h; -r,0,h];
% Rotation matrix
Rx = @(V, theta) V*[1 0 0; 0 cos(theta) sin(theta); 0 -sin(theta) cos(theta)];
Rz = @(V, theta) V*[cos(theta) sin(theta) 0;-sin(theta) cos(theta) 0; 0 0 1];
N = 180; Vn = zeros(N, 3); eval(char(fig.UserData))
for i = 1:N
tV = Rz(Rx(V0.*(1.2 - .8.*i./N + rand(1).*.1./i^(1/5)), pi/3.*(1 - .6.*i./N)), i.*pi/8.1 + .001.*i.^2) + [0,0,.016.*i];
dP(tV); Vn(i,:) = tV(2,:);
end
scatter3(Vn(:,1).*1.02,Vn(:,2).*1.02,Vn(:,3).*1.01, 30, 'w', 'Marker','*', 'MarkerEdgeAlpha',.5)
%% Draw Star of Bethlehem
w = .3; R = .62; r = .4; T = (1/8:1/8:(2 - 1/8)).'.*pi;
V8 = [ 0, 0, w; 0, 0,-w;
1, 0, 0; 0, 1, 0; -1, 0, 0; 0,-1,0;
R, R, 0; -R, R, 0; -R,-R, 0; R,-R,0;
cos(T).*r, sin(T).*r, T.*0];
F8 = [1,3,25; 1,3,11; 2,3,25; 2,3,11; 1,7,11; 1,7,13; 2,7,11; 2,7,13;
1,4,13; 1,4,15; 2,4,13; 2,4,15; 1,8,15; 1,8,17; 2,8,15; 2,8,17;
1,5,17; 1,5,19; 2,5,17; 2,5,19; 1,9,19; 1,9,21; 2,9,19; 2,9,21;
1,6,21; 1,6,23; 2,6,21; 2,6,23; 1,10,23; 1,10,25; 2,10,23; 2,10,25];
V8 = Rx(V8.*.3, pi/2) + [0,0,3.5];
patch('Faces',F8, 'Vertices',V8, 'FaceColor',[255,223,153]./255,...
'EdgeColor',[255,223,153]./255, 'FaceAlpha', .2)
%% Draw snow
sXYZ = rand(200,3).*[4,4,5] - [2,2,0];
sHdl1 = plot3(sXYZ(1:90,1),sXYZ(1:90,2),sXYZ(1:90,3), '*', 'Color',[.8,.8,.8]);
sHdl2 = plot3(sXYZ(91:200,1),sXYZ(91:200,2),sXYZ(91:200,3), '.', 'Color',[.6,.6,.6]);
annotation(fig,'textbox',[0,.05,1,.09], 'Color',[1 1 1], 'String','Merry Christmas Matlaber',...
'HorizontalAlignment','center', 'FontWeight','bold', 'FontSize',48,...
'FontName','Times New Roman', 'FontAngle','italic', 'FitBoxToText','off','EdgeColor','none');
% Rotate the Christmas tree and let the snow fall
for i=1:1e8
sXYZ(:,3) = sXYZ(:,3) - [.05.*ones(90,1); .06.*ones(110,1)];
sXYZ(sXYZ(:,3)<0, 3) = sXYZ(sXYZ(:,3) < 0, 3) + 5;
sHdl1.ZData = sXYZ(1:90,3); sHdl2.ZData = sXYZ(91:200,3);
view([i,30]); drawnow; pause(.05)
end
end
Curved XMas Tree
function XmasTree2024_2
fig = figure('Units','normalized', 'Position',[.1,.1,.5,.8],...
'Color',[0,9,33]/255, 'UserData',40 + [60,65,75,72,0,59,64,57,74,0,63,59,57,0,1,6,45,75,61,74,28,57,76,57,1,1]);
axes('Parent',fig, 'Position',[0,-1/6,1,1+1/3], 'UserData',97 + [18,11,0,13,3,0,17,4,17],...
'XLim',[-6,6], 'YLim',[-6,6], 'ZLim',[-16, 1], 'DataAspectRatio', [1,1,1], 'NextPlot','add',...
'Projection','perspective', 'Color',[0,9,33]/255, 'XColor','none', 'YColor','none', 'ZColor','none')
%% Draw Christmas tree
[X,T] = meshgrid(.4:.1:1, 0:pi/50:2*pi);
XM = 1 + sin(8.*T).*.05;
X = X.*XM; R = X.^(3).*(.5 + sin(8.*T).*.02);
dF = @(R, T, X) surf(R.*cos(T), R.*sin(T), -X, 'EdgeColor',[20,107,58]./255,...
'FaceColor', [20,107,58]./255, 'FaceAlpha',.2, 'LineWidth',1);
CList = [254,103,110; 255,191,115; 57,120,164]./255;
for i = 1:5
tR = R.*(2 + i); tT = T+i; tX = X.*(2 + i) + i;
SFHdl = dF(tR, tT, tX);
[~, ind] = sort(SFHdl.ZData(:)); ind = ind(1:8);
C = CList(randi([1,size(CList,1)], [8,1]), :);
scatter3(tR(ind).*cos(tT(ind)), tR(ind).*sin(tT(ind)), -tX(ind), 120, 'filled',...
'CData', C, 'MarkerEdgeColor','none', 'MarkerFaceAlpha',.3)
scatter3(tR(ind).*cos(tT(ind)), tR(ind).*sin(tT(ind)), -tX(ind), 60, 'filled', 'CData', C)
end
%% Draw Star of Bethlehem
Rx = @(V, theta) V*[1 0 0; 0 cos(theta) sin(theta); 0 -sin(theta) cos(theta)];
% Rz = @(V, theta) V*[cos(theta) sin(theta) 0;-sin(theta) cos(theta) 0; 0 0 1];
w = .3; R = .62; r = .4; T = (1/8:1/8:(2 - 1/8)).'.*pi;
V8 = [ 0, 0, w; 0, 0,-w;
1, 0, 0; 0, 1, 0; -1, 0, 0; 0,-1,0;
R, R, 0; -R, R, 0; -R,-R, 0; R,-R,0;
cos(T).*r, sin(T).*r, T.*0];
F8 = [1,3,25; 1,3,11; 2,3,25; 2,3,11; 1,7,11; 1,7,13; 2,7,11; 2,7,13;
1,4,13; 1,4,15; 2,4,13; 2,4,15; 1,8,15; 1,8,17; 2,8,15; 2,8,17;
1,5,17; 1,5,19; 2,5,17; 2,5,19; 1,9,19; 1,9,21; 2,9,19; 2,9,21;
1,6,21; 1,6,23; 2,6,21; 2,6,23; 1,10,23; 1,10,25; 2,10,23; 2,10,25];
V8 = Rx(V8.*.8, pi/2) + [0,0,-1.3];
patch('Faces',F8, 'Vertices',V8, 'FaceColor',[255,223,153]./255,...
'EdgeColor',[255,223,153]./255, 'FaceAlpha', .2)
annotation(fig,'textbox',[0,.05,1,.09], 'Color',[1 1 1], 'String','Merry Christmas Matlaber',...
'HorizontalAlignment','center', 'FontWeight','bold', 'FontSize',48,...
'FontName','Times New Roman', 'FontAngle','italic', 'FitBoxToText','off','EdgeColor','none');
%% Draw snow
sXYZ = rand(200,3).*[12,12,17] - [6,6,16];
sHdl1 = plot3(sXYZ(1:90,1),sXYZ(1:90,2),sXYZ(1:90,3), '*', 'Color',[.8,.8,.8]);
sHdl2 = plot3(sXYZ(91:200,1),sXYZ(91:200,2),sXYZ(91:200,3), '.', 'Color',[.6,.6,.6]);
for i=1:1e8
sXYZ(:,3) = sXYZ(:,3) - [.1.*ones(90,1); .12.*ones(110,1)];
sXYZ(sXYZ(:,3)<-16, 3) = sXYZ(sXYZ(:,3) < -16, 3) + 17.5;
sHdl1.ZData = sXYZ(1:90,3); sHdl2.ZData = sXYZ(91:200,3);
view([i,30]); drawnow; pause(.05)
end
end
I wish all MATLABers a Merry Christmas in advance!
Vasilis Bellos
Vasilis Bellos on 4 Jan 2025 (Edited on 5 Jan 2025)
Impressive as always @Zhaoxu Liu / slandarer! Added some ornaments:
%% MATLAB tree
% Ref: https://uk.mathworks.com/matlabcentral/discussions/tips/878025-christmas-tree-for-matlaber-rotating-with-falling-snowflakes
fig = figure('Units','normalized', 'Position',[.25,0,.5,1],...
'Color',[0,9,33]/255);
axes('Parent',fig, 'Position',[0,-1/6,1,1+1/3],...
'XLim',[-6,6], 'YLim',[-6,6], 'ZLim',[-18, 1], 'DataAspectRatio', [1,1,1], 'NextPlot','add',...
'Projection','perspective', 'Color',[0,9,33]/255, 'XColor','none', 'YColor','none', 'ZColor','none')
% Draw Christmas tree
[X,T] = meshgrid(.4:.15:1, 0:pi/150:2*pi);
XM = 1 + sin(8.*T).*.05;
X = X.*XM; R = X.^(3).*(.5 + sin(8.*T).*.02);
dF = @(R, T, X) surf(R.*cos(T), R.*sin(T), -X, 'EdgeColor','interp',...
'FaceColor', [20,107,58]./255, 'FaceAlpha',.8, 'LineWidth',1,'MeshStyle','Column',...
'facelighting','gouraud','ambientstrength',.3,'diffusestrength',.5,'specularstrength',.5);
cmap = colormap(turbo);
% LED strips
cmap = colormap(repmat([cmap;flip(cmap)],3,1));
% Star light
arrayfun(@(x)light('Style','local','Position',[0 0 -1.3],'color',[255,223,153]./255),1:4)
CList = [255,100,100; 150,255,100; 0,255,255]./255;
rng(1)
for i = 1:6 % Number of tree 'layers'
tR = R.*(2 + i); tT = T+i; tX = X.*(2 + i) + i;
SFHdl = dF(tR, tT, tX);
% LED strip gradient
for j=1:size(SFHdl.CData,2)
SFHdl.CData(:,j)=linspace(1,numel(cmap),size(SFHdl.CData,1));
end
[~, ind] = sort(SFHdl.ZData(:)); ind = ind(1:8);
C = CList(randi([1,size(CList,1)], [8,1]), :);
% Tree lights symmetry
if i==1
C(length(C)/2+1:end,:) = C(1:length(C)/2,:);
end
if mod(i,2)
for j=1:length(ind)/2
P = drawPinecone;
set(P,'Matrix',makehgtform(Translate=[tR(ind(j)).*cos(tT(ind(j))), tR(ind(j)).*sin(tT(ind(j))), -tX(ind(j))],Scale=0.35))
end
scatter3(tR(ind(length(ind)/2+1:end)).*cos(tT(ind(length(ind)/2+1:end)))*1.01, tR(ind(length(ind)/2+1:end)).*sin(tT(ind(length(ind)/2+1:end)))*1.01, -tX(ind(length(ind)/2+1:end)), 500, 'filled',...
'CData', C(length(ind)/2+1:end,:), 'MarkerEdgeColor','none', 'MarkerFaceAlpha',.3)
scatter3(tR(ind(length(ind)/2+1:end)).*cos(tT(ind(length(ind)/2+1:end)))*1.01, tR(ind(length(ind)/2+1:end)).*sin(tT(ind(length(ind)/2+1:end)))*1.01, -tX(ind(length(ind)/2+1:end)), 80, 'filled', 'CData', C(length(ind)/2+1:end,:))
else
% Add a few membrane ornaments
if i==2 || i==4
M = drawMembraneOrnament;
set(M,'Matrix',makehgtform(Translate=[tR(ind(1)).*cos(tT(ind(1)))*.98, tR(ind(1)).*sin(tT(ind(1)))*.98, -tX(ind(1))+0.05*0],ZRotate=rand*pi+pi/4*i,Scale=.9))
M = drawMembraneOrnament;
set(M,'Matrix',makehgtform(Translate=[tR(ind(3)).*cos(tT(ind(3)))*.98, tR(ind(3)).*sin(tT(ind(3)))*.98, -tX(ind(3))+0.05*0],ZRotate=rand*pi+pi/4*i,Scale=.9))
scatter3(tR(ind([2,4:end])).*cos(tT(ind([2,4:end])))*1.01, tR(ind([2,4:end])).*sin(tT(ind([2,4:end])))*1.01, -tX(ind([2,4:end])), 500, 'filled',...
'CData', C([2,4:end],:), 'MarkerEdgeColor','none', 'MarkerFaceAlpha',.3)
scatter3(tR(ind([2,4:end])).*cos(tT(ind([2,4:end])))*1.01, tR(ind([2,4:end])).*sin(tT(ind([2,4:end])))*1.01, -tX(ind([2,4:end])), 80, 'filled', 'CData', C([2,4:end],:))
else
scatter3(tR(ind).*cos(tT(ind))*1.01, tR(ind).*sin(tT(ind))*1.01, -tX(ind), 500, 'filled',...
'CData', C, 'MarkerEdgeColor','none', 'MarkerFaceAlpha',.3)
scatter3(tR(ind).*cos(tT(ind))*1.01, tR(ind).*sin(tT(ind))*1.01, -tX(ind), 80, 'filled', 'CData', C)
end
end
% Tree lights
if i==1
arrayfun(@(h)light('Style','Local','Position',[tR(ind(h)).*cos(tT(ind(h)))*.7, tR(ind(h)).*sin(tT(ind(h)))*.7, -tX(ind(h))+2],'Color',C(h,:)),length(ind)/2+1:length(ind))
end
end
% Draw Star of Bethlehem
Rx = @(V, theta) V*[1 0 0; 0 cos(theta) sin(theta); 0 -sin(theta) cos(theta)];
w = .3; R = .62; r = .4; T = (1/8:1/8:(2 - 1/8)).'.*pi;
V8 = [ 0, 0, w; 0, 0,-w;
1, 0, 0; 0, 1, 0; -1, 0, 0; 0,-1,0;
R, R, 0; -R, R, 0; -R,-R, 0; R,-R,0;
cos(T).*r, sin(T).*r, T.*0];
F8 = [1,3,25; 1,3,11; 2,3,25; 2,3,11; 1,7,11; 1,7,13; 2,7,11; 2,7,13;
1,4,13; 1,4,15; 2,4,13; 2,4,15; 1,8,15; 1,8,17; 2,8,15; 2,8,17;
1,5,17; 1,5,19; 2,5,17; 2,5,19; 1,9,19; 1,9,21; 2,9,19; 2,9,21;
1,6,21; 1,6,23; 2,6,21; 2,6,23; 1,10,23; 1,10,25; 2,10,23; 2,10,25];
V8 = Rx(V8.*.8, pi/2) + [0,0,-1.3];
patch('Faces',F8, 'Vertices',V8, 'FaceColor',[255,223,153]./255,...
'EdgeColor',[255,223,153]./255, 'FaceAlpha', 1,'facelighting','gouraud','ambientstrength',.7,'diffusestrength',.8,'specularstrength',0)
% Draw snow
sXYZ = rand(200,3).*[12,12,19] - [6,6,18];
sHdl1 = plot3(sXYZ(1:90,1),sXYZ(1:90,2),sXYZ(1:90,3), '*','linewidth',1,'markersize',12, 'Color',[1 1 1]*.9);
sHdl2 = plot3(sXYZ(91:200,1),sXYZ(91:200,2),sXYZ(91:200,3), '.','markersize',10, 'Color',[1 1 1]*.9);
for i=1:360 % Seamless loop
sXYZ(:,3) = sXYZ(:,3) - [2*19/360.*ones(90,1); 3*19/360.*ones(110,1)];
sXYZ(sXYZ(:,3)<-18, 3) = sXYZ(sXYZ(:,3) < -18, 3) + 19;
sHdl1.ZData = sXYZ(1:90,3); sHdl2.ZData = sXYZ(91:200,3);
view([i,30]); drawnow;
end
function H=drawPinecone
% Ref: https://blogs.mathworks.com/graphics-and-apps/2024/12/16/pinecode-creating-pinecones-with-fibonacci-spirals/
% Pinecone Attributes
nscales = 140;% Number of scales
height = 2.5; % Height of inner part of pinecone
nn = 18; % Number of needles
vps = 17; % verts per scale : An odd number so 1 vert is in center
vpr = 20; % verts per radius
% Scale locations
FIB = (1+sqrt(5))*(1:nscales); % Fibonacci golden ratio as factor of pi
H = linspace(0,1,nscales).^2.8*height; % Height of the root of each scale
R = 1-linspace(-1,1,nscales).^2; % Radius of pinecone over height
U = 1-abs(linspace(-1,1,nscales).^3); % Thickness of scale U shape by height
% Geometry of the scales at the locations
ST = reshape((linspace(-.5,.5,vps)*.3+FIB')', 1,[]);
SR = reshape(((1-abs(linspace(-1,1,vps).^3)).*R')',1,[]);
SH = reshape((ones(1,vps).*H'+abs(linspace(-1,1,vps).^3.*U')*.2)',1,[]);
MR = linspace(0,1,vpr)'.*SR;
% Compute final geometry of the pinecone
X = cospi(ST).*MR;
Y = sinpi(ST).*MR;
Z = SH.*ones(vpr,1)+MR.*linspace(0,1,vps*nscales).^2.1*1.2;
% C = linspace(0,1,vpr).^2'.*ones(1,vps*nscales);
% Plot pinecone
P(1) = plot3(X(end,:),Y(end,:),-Z(end,:),'-','linewidth',.5,'Color',[120 61 3]/255*.5);
P(2) = surface(X,Y,-Z,'FaceColor',"#783D03",'EdgeColor','none','FaceLighting','gouraud','AmbientStrength',0.5,'SpecularColorReflectance',0.1,'SpecularStrength',0.1,'SpecularExponent',5,'DiffuseStrength',0.15);
% Branch & Needles
P(3) = line('XData',(0:.5:2)/3*cos(rand*2*pi),'YData',zeros(1,5),'ZData',[ 0 .05 .2 .25 .2 ]*2,...
'Color','k', 'LineWidth',1); % Branch
NT = linspace(0,5,nn)';
NR = linspace(.1,.8,nn)';
NV = [ 0 0 0; -ones(nn,1).*randi([-1,1],nn,1)*.5 cospi(NT).*NR sinpi(NT).*NR+.9 ];
NF = [ ones(nn,1) (1:nn)'+1 ];
P(4) = patch('Vertices',NV,'Faces',NF,'EdgeColor','#0F9666','FaceColor','none',...
'LineWidth',1); % Needles
H = hgtransform;
set(P,'Parent',H);
end
function H=drawMembraneOrnament
% Ref: https://uk.mathworks.com/help/matlab/visualize/creating-the-matlab-logo.html
% https://uk.mathworks.com/matlabcentral/discussions/fun/877700-matlab-ornament
% https://uk.mathworks.com/matlabcentral/discussions/fun/878156-tree-topper-l-shaped-membrane
L = membrane(1,100);
L = L-max(L,[],'all');
[x,y] = deal(linspace(-.5,.5,length(L)));
[r,c] = find(L==max(L,[],'all'));
x = x+x(r);
y = y+y(c);
[X,Y] = meshgrid(x,y);
S(1) = surface(X,Y,L,'EdgeColor','none');
view(3)
Z = S.ZData;
% Close the membrane & combine sides
S(2) = surf([X(Y==Y(1))*[1 1];NaN(1,2);X(X==X(1))*[1 1];NaN(1,2);X(Y==Y(end))*[1 1];NaN(1,2);X(X==X(end))*[1 1];NaN(1,2);X([1,end],[1,end])],...
[Y(Y==Y(1))*[1 1];NaN(1,2);Y(X==X(1))*[1 1];NaN(1,2);Y(Y==Y(end))*[1 1];NaN(1,2);Y(X==X(end))*[1 1];NaN(1,2);Y([1,end],[1,end])],...
[[Z(Y==Y(1)),ones(size(Z(Y==Y(1))))*(min(Z,[],'all'))];NaN(1,2);[Z(X==X(1)),ones(size(Z(X==X(1))))*(min(Z,[],'all'))];NaN(1,2);...
[Z(Y==Y(end)),ones(size(Z(Y==Y(end))))*(min(Z,[],'all'))];NaN(1,2);[Z(X==X(end)),ones(size(Z(X==X(end))))*(min(Z,[],'all'))];...
NaN(1,2);ones(2,2)*(min(Z,[],'all'))],'edgecolor','none');
set(S,'FaceColor','#FF4433','FaceLighting','gouraud','AmbientStrength',.3,'DiffuseStrength',.6,'BackFaceLighting','lit','SpecularStrength',1,'SpecularColorReflectance',1,'SpecularExponent',7)
H = hgtransform;
set(S,'Parent',H);
end
Chen Lin
Chen Lin on 6 Jan 2025
Great to see you referenced Blog and Dicussions posts from @Adam Danz, @Image Analyst, and @Peter Fryscak. Learning from each other is the best part.
Vasilis Bellos
Vasilis Bellos on 6 Jan 2025
Indeed, I got the idea for the membrane ornament from @Image Analyst's post. While at it, here's a quick variation with @Peter Fryscak's tree topper (the 3D-printed versions look amazing):
Zhaoxu Liu / slandarer
Zhaoxu Liu / slandarer on 5 Jan 2025
It's amazing, Very cool changes!!!
Chen Lin
Chen Lin on 16 Dec 2024
Merry Christmas and Happy Holidays. Thanks for sharing the code.
Adam Danz
Adam Danz on 16 Dec 2024
Impressive as usual Zhaoxu Liu! Thanks for sharing!

Tags

No tags entered yet.