image thumbnail

XMas Tree

by

 

12 Dec 2005 (Updated )

Draws a xmas tree with decorations

Editor's Notes:

This file was a File Exchange Pick of the Week

ov_xmasTree.m
function handles = ov_xmasTree(varargin);
% ov_xmasTree: draw a xmas tree
% ---------------------------------------------------------------------
% Task   : draw a xmas tree in current axis, (issues a clf command)
%
% Input  : ov_xmasTree(MAXHEIGHT)
%          ov_xmasTree(...,'PropertyName',PropertyValue,...)
%
%          MAXHEIGHT: default (30)
%            the maximum height of the tree
%
%          Possible Property-Value pairs are:
%          'detail': default (8)
%            the number of points definening a circle on a sphere,
%            thus it defines the smoothness of the needles and decorations
%            of the tree.
%
%          'decorationscolorarray': 
%            the decorations on the tree are colored randomly by the
%            colors in this array. The color array is given
%            as a Nx3 matrix of rgb values
%
%          'seperatepatches': boolean value
%            if set true the tree will consist of different patches for the
%            stem, the top, the needles, the branches and the decorations.
%            if set to false (default), the tree is drawn as one patch
%            (The decorations will not look as shiny as they do in seperate
%            mode, as the material properties are applied to the patch and
%            not on the faces.)
%
%          'decorationsProbability': default (0.025)
%            determines the amount of decorations on the tree. 
%
% Output : a xmas tree in the current axis, 
%          the axis is cleared before drawing the tree
%
% ---------------------------------------------------------------------

% Authors  : Dr. Marc Ltzel (L)
%
% Revisions:
%   2005-12-12: (L) initial creation
%
% Copyright: 2005 Marc Ltzel
% email adress: 
% char(cumsum([109 -12 17 -15 -35 41 -6 -2 -48 -3 71 -7 ...
%              -5 -60 70 1 1 -1 0 -13 -6 17 2 -70 54 1]));
% ---------------------------------------------------------------------

% Annotations:
% ---------------------------------------------------------------------
% The file is by no means a good example for matlab programming, its main
% purpos was to draw a xmas tree.
% The povray source is from xtree.inc
% from Remco de Korte 1998 <remcodek@xs4all.nl>
% the decoration part was inspired by Ken Allred's (2002) additions to
% xtree.inc
% <SNIP xtree.inc>
% //The xtree-object is a simple firtree, starting at <0,0,0> up to maxheight
% //You can change the tree by altering the rand-seed or the height, or by changing the
% //tow increment-steps indeicated below. The setting as it is now is working allright for 
% //me, changing it can increase the render-time dramatically.
% <SNAP>
% povray uses a differently oriented coordinate system, therefore rotations
% around y and z axis are interchanged.
%
% The rotation subroutines are copied from CAD2MATDEMO.M 
% by Don Riley from the FEX
%
% Happy Christmas    *
%                    /\
%                   /^^\ 
%    Marc          /^^\
%                    || 
% ---------------------------------------------------------------------



  [reg, properties] = parseparams(varargin);

  if length(reg)==1
    maxHeight = reg{1};
  elseif length(reg)~=0
    maxHeight = 30;
    disp('XMasTree uses input of the form XMasTree([height],''Properties'',...)');
    warning(['input ' mat2str([reg{2:end}]) ' is ignored.']);
  else
    maxHeight = 30;
  end
  
%% setup some constants
  nPoints = 8;
  decorationsColorArray = ...
    [1,0,0;...
     0,1,0;...
     0,0,1;...
     1,1,0;...
     1,0,1;];
% decorationsColorArray = ...
%  [0.85 0.66 0.3;...
%   0.91 0.91 0.91]; 
  decorationsSize = 0.6;
  NeedlePropability = 0.975;
  BRANCHTYPE = 1;
  NEEDLETYPE = 2;
  BALLTYPE   = 3;
  STEMTYPE   = 4;
  TOPTYPE    = 5;
  colorArrayTree = [0.5, 0.4, 0.2;...
                    0.2, 0.5, 0.2;...
                    0.0, 0.0, 0.9;...
                    0.5, 0.4, 0.2;...
                    0.6, 0.2, 0.0];
  seperatePatches = false;                
  
%% read Property-Value pairs
  for ii=1:2:length(properties)
    switch lower(properties{ii})
      case 'detail'
        nPoints = properties{ii+1};
      case 'decorationscolorarray'
        decorationsColorArray = properties{ii+1};
      case 'seperatepatches'
        seperatePatches = properties{ii+1};
      case 'decorationsprobability'
        NeedlePropability = 1-properties{ii+1};
      case 'decorationssize'
        decorationsSize = properties{ii+1};
      otherwise
        warning([properties{ii} ' unrecognized property.']);
    end
  end


%% a template ball
  [x,y,z] = sphere(nPoints);
  tmpBall.Vertices = [x(:),y(:),z(:)]*decorationsSize;
  tmpBall.Faces = convhulln(tmpBall.Vertices);


%% a template branch part
  [x,z,y] = ellipsoid( 0,0,0,  0.2,0.2*2.5,0.2,  nPoints);
  tmpBranch.Vertices = [x(:),y(:),z(:)];
  tmpBranch.Faces = convhulln(tmpBranch.Vertices);


%% a template needle
% #declare needle=sphere{<0,0,0>,1 scale<.75,.1,.1> pigment{rgb<.2,.5,.2>}}
% //here you can change the size of the needles - they're quite large and a
% bit 'fat' now 
  [x,z,y]  = ellipsoid(0,0,0,  0.5,0.1,0.1,  nPoints);
  tmpNeedle.Vertices = [x(:),y(:),z(:)];
  tmpNeedle.Faces = convhulln(tmpNeedle.Vertices);


%% The main XTree
  maxheight = maxHeight;              % #declare maxheight=30;
  rand('state',9);                    % #declare r=seed(777);

%% the stem of the tree, 
  % #declare xtree=union{  
  % width at top and foot depending on the height
  % cone{<0,0,0>,         maxheight/50,...
  %      <0,maxheight,0>, maxheight/200 texture{branch_text}}
  [x,y,z] = cylinder([maxheight/50; maxheight/200], nPoints);
  z = z * maxheight;
  stem.Vertices = [x(:),y(:),z(:)];
  stem.Faces = convhulln(stem.Vertices);
  
%% the top
  %   sphere{<0,maxheight,0>,maxheight/175 texture{branch_text}} 
  [x,y,z] = ellipsoid(0,0,maxheight, maxheight/175,maxheight/175,maxheight/175,nPoints);
  top.Vertices = [x(:),y(:),z(:)];
  top.Faces = convhulln(top.Vertices);
  
% //this starts the branches of at 1/15th of the tree's size off the ground.
%   #declare cc=maxheight/15;
  cc=maxheight/15;
  
  tree = povUnion;
%   #while (cc<maxheight)
  while (cc<maxheight)
    % start a branch:  
    % union{ 
    branch = povUnion;
    % #declare i=0;
    i = 0;
    % #declare dd=(maxheight*.95-cc)/2.5;
    dd = (maxheight*.95-cc)/2.5;
    % //put needles on the branch:  
    % #while (i<dd)
    while (i<dd)
      % union{
      branchPart = povUnion;
      %   sphere{<0,0,0>,.2 scale <1,2.5,1> texture{branch_text}}
      tmp = tmpBranch;
      branchPart = povUnion(branchPart,tmp,BRANCHTYPE);
      % //put the needles all around: 
      % #declare j=60*rand(r);   
      j = 60*rand;
      % #while (j<360)      
      while (j<360)
        % object{needle translate -x rotate z*-15-i*45/dd rotate y*j+30*rand(r)}  
        hasLight = rand;
        if hasLight<=NeedlePropability
          tmp = tmpNeedle;
          hasLight = 0;
        else
          tmp = tmpBall;
          hasLight = 1;
        end
        % translate -x rotate z*-15-i*45/dd rotate y*j+30*rand(r)
        tmp.Vertices(:,1) = tmp.Vertices(:,1)-0.75;
        tmp = c_rotateYAxis(tmp,-(-15-i*45/dd));
        tmp = c_rotateZAxis(tmp,j+30*rand);
      
        if hasLight==0
          branchPart = povUnion(branchPart,tmp,NEEDLETYPE);
        else
          branchPart = povUnion(branchPart,tmp,BALLTYPE);
        end
      
        % #declare j=j+60;
        j = j + 60;
      % #end
      end 

      % scale 1-.25*i/dd
      branchPart.Vertices = branchPart.Vertices * (1-0.25*i/dd);
      % translate y*i 
      branchPart.Vertices(:,3) = branchPart.Vertices(:,3)+i;
    % }

      branch = povUnion(branch,branchPart);

      % the frequency of the needles (~the amount of needles) depends on this step
      % if you make it smaller you have more needles and a much more rendertime
      % #declare i=i+.4;
      i = i + 0.4; 

    % #end
    end %(i<dd)
  
    if ~isempty(branch.Vertices)
      % //this makes the branch point up a bit 
      % (depending on how high it is on the tree)
      % rotate z*(-75+cc/2)
      branch = c_rotateYAxis(branch,-75+cc/2);

      % //this puts it on the tree somewhere around 
      % rotate y*360*rand(r)
      branch = c_rotateZAxis(branch,360*rand);

      % this puts the branch at its proper height
      % translate y*cc    
      branch.Vertices(:,3) = branch.Vertices(:,3) + cc;

      tree = povUnion(tree,branch); 
    end
    % }

    % Not implemented yet
    % //put some needles on the stem:
    %   #declare j=0;
    %   #while (j<360)
    %     object{needle translate -x rotate z*(-45-15*rand(r)) translate -x*maxheight/400 rotate y*j+30*rand(r) translate y*cc}
    %   #declare j=j+60;
    %   #end

    % //this step defines the number of branches. 
    % //There's one branch for each step, but it can be at any side of the tree
    % //Again, if you make this smaller you'll have more branches, but it'll also render much slower
    % //You could probably make the tree more realistic by increasing the step towards the top of the tree 
    % #declare cc=cc+.25;
    cc = cc + 0.25;
  % #end
  end %while (cc<maxheight) 
  % }

%% Now the drawing part
  clf;
  hold on
  % check whether to draw one patch or seperate patches
  if seperatePatches
    handles.Top  = trisurf(top.Faces,top.Vertices(:,1),top.Vertices(:,2),top.Vertices(:,3),1,...
                   'FaceColor',[0.6,0.2,0.0],...
                   'EdgeColor','none','FaceLighting','gouraud');
    handles.Stem = trisurf(stem.Faces,stem.Vertices(:,1),stem.Vertices(:,2),stem.Vertices(:,3),2,...
                   'FaceColor',[0.5, 0.4, 0.2],...
                   'EdgeColor','none','FaceLighting','gouraud');
    [needles.Faces,needles.Vertices,needles.Types] = c_seperateType(tree,NEEDLETYPE);
    handles.Needles = patch('Faces',needles.Faces,...
                    'Vertices',needles.Vertices,...
                    'FaceColor','flat',...
                    'FaceVertexCData',colorArrayTree(needles.Types,:),...
                    'EdgeColor','none','FaceLighting','gouraud');
    [branches.Faces,branches.Vertices,branches.Types] = c_seperateType(tree,BRANCHTYPE);
    handles.Branches = patch('Faces',branches.Faces,...
                    'Vertices',branches.Vertices,...
                    'FaceColor','flat',...
                    'FaceVertexCData',colorArrayTree(branches.Types,:),...
                    'EdgeColor','none','FaceLighting','gouraud');
    [decorations.Faces,decorations.Vertices,decorations.Types] = c_seperateType(tree,BALLTYPE);
    if ~isempty(decorations.Faces)
      handles.Decorations = patch('Faces',decorations.Faces,...
                      'Vertices',decorations.Vertices,...
                      'FaceColor','flat',...
                      'FaceVertexCData',colorArrayTree(decorations.Types,:),...
                      'EdgeColor','none','FaceLighting','gouraud');
      colorDecorations(decorations,handles.Decorations,...
                       BALLTYPE,tmpBall,decorationsColorArray);                  
    end
  else
    tree = povUnion(tree,top,TOPTYPE);
    tree = povUnion(tree,stem,STEMTYPE);
    handles = patch('Faces',tree.Faces,...
                    'Vertices',tree.Vertices,...
                    'FaceColor','flat',...
                    'FaceVertexCData',colorArrayTree(tree.Types,:),...
                    'EdgeColor','none','FaceLighting','gouraud');
    colorDecorations(tree,handles,BALLTYPE,tmpBall,decorationsColorArray);                  
  end                    
  light
  axis equal
  view(0,0);
  set(gca,'Visible','off');
  set(gca,'DataAspectRatio',[1 1 1],'PlotBoxAspectRatio',[1 1 1])
end % of function s_XMasTree
  
%% --- Definition of some local functions ------------------
 function unionObject = povUnion(unionObject, newObject, Type)
   if nargin==0
     unionObject.Faces = [];
     unionObject.Vertices = [];
     unionObject.Types = [];
     return
   end

   if nargin<3
     Type = NaN;
   end
   
   unionObject.Faces    = [unionObject.Faces; ...
                           newObject.Faces+length(unionObject.Vertices)];  
   unionObject.Vertices = [unionObject.Vertices; newObject.Vertices];
   if isnan(Type)
     unionObject.Types  = [unionObject.Types; newObject.Types];
   else
     unionObject.Types  = [unionObject.Types; ...
                           ones(length(newObject.Faces),1)*Type];
   end
 end % of function povUnion
 % ---------------------------------------------------------------------------
  
 function object = c_rotateYAxis(object,THETA)
 % ROTATION ABOUT THE Y-AXIS
 %
 % This is the homogeneous transformation for
 % rotation about the Y-axis.
 %
 %		NOTE: The angel THETA must be in DEGREES.
 %
   THETA = THETA*pi/180;  %Note: THETA is in radians.
   c = cos(THETA);
   s = sin(THETA);
   Ry = [c 0 s 0; 0 1 0 0; -s 0 c 0; 0 0 0 1];
   tmp = Ry*[object.Vertices ones(length(object.Vertices),1)]';
   object.Vertices = tmp(1:3,:)';
 end % of function c_rotateYAxis
 % ---------------------------------------------------------------------------

 function object = c_rotateZAxis(object,THETA)
 % ROTATION ABOUT THE Z-AXIS
 %
 % This is the homogeneous transformation for
 % rotation about the Z-axis.
 %
 %		NOTE:  The angle THETA must be in DEGREES.
 %
   THETA = THETA*pi/180;  %Note: THETA is in radians.
   c = cos(THETA);
   s = sin(THETA);
   Rz = [c -s 0 0; s c 0 0; 0 0 1 0; 0 0 0 1];
   tmp = Rz*[object.Vertices ones(length(object.Vertices),1)]';
   object.Vertices = tmp(1:3,:)';
 end % of function c_rotateZAxis
 % ---------------------------------------------------------------------------
 
 function [newFaces,newVertices,newTypes] = c_seperateType(varargin)
 % if 2 inputs those are: object,selectType
 % if 4 inputs those are: faces,vertices,types,selcetType
   if nargin==2
     faces      = varargin{1}.Faces;
     vertices   = varargin{1}.Vertices;
     types      = varargin{1}.Types;
     selectType = varargin{2};
   elseif nargin==4
     faces      = varargin{1};
     vertices   = varargin{2};
     types      = varargin{3};
     selectType = varargin{4};    
   else
     error('c_seperateType: wrong number of parameters.');
   end

   tmp = unique(faces(types==selectType,:));
   tmpMap = [tmp [1:length(tmp)]'];
   map = ones(length(vertices),1)*NaN;
   map(tmpMap(:,1)) = tmpMap(:,2);

   newVertices = vertices(tmp,:);
   newFaces    = map(faces(types==selectType,:));
   newTypes    = ones(size(newFaces,1),1)*selectType;
 end % of function c_seperateType
 % ---------------------------------------------------------------------------
 
 function colorDecorations(object,handle,BALLTYPE,tmpBall,decorationsColorArray)
   if ~isempty(object.Types==BALLTYPE)    
     decoIdx = find(object.Types==BALLTYPE);
     numBalls = length(decoIdx)/length(tmpBall.Faces);
     numFaces = length(decoIdx);
     tmpFaceVertexCData = repmat([0.0, 0.9, 0.0],numFaces,1);
     for iBalls=1:numBalls 
       rColor = rand*length(decorationsColorArray); 
       rColor = min(ceil(rColor),size(decorationsColorArray,1));
       color = repmat(decorationsColorArray(rColor,:),length(tmpBall.Faces),1);
       tmpFaceVertexCData(1+(iBalls-1)*length(tmpBall.Faces):...
                          iBalls*length(tmpBall.Faces),:) = color;
     end
     faceVertexCData=get(handle,'FaceVertexCData');
     faceVertexCData(decoIdx,:) = tmpFaceVertexCData;
     set(handle,'FaceVertexCData',faceVertexCData);
     % use metalic looking balls if    
     % all faces belong to one decoration patch
     if size(object.Faces,1)==numFaces 
       set(handle,...
           'AmbientStrength',0.4,...
           'DiffuseStrength',0.7,...
           'SpecularStrength',8.0,...
           'SpecularExponent',5,...
           'SpecularColorReflectance',0.6);
     end
   end
 end % of function colorDecorations
 % --------------------------------------------------------------------------- 

Contact us