function digital_rain_animation% Programmed in Matlab R2017a.% You can have a glimpse of the original digital rain in the % Matrix Movie at 1:04 (or youtube: /watch?v=3vAnuBtyEYE ).% The original is far messier. Drops stop, start and shift rows% additionally there are stuck letters, gaps and so on.% This is not replicated because it makes the short loop time very obvious.% Those features could be added in a longer video.% The font is not correct.% The code could be radically optimized, but hence it is intended for % one-time use, there is no real need.latinList = ['-><=*' char(124) '":АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ0123456789']; % the vertical bar is encrypted as char(124) to avoid template-errors on wikimedia commonsasianList = char(65393:65437); % Asian symbols; Half-width kana;% Matlab can work with UTF-8 but M-Files are saved using ANSI. If letters are typed directly, they are lost while saving the filecharList = [latinList asianList asianList]; % aisan symbols twice as likelynChar = numel(charList); [pathstr,fname] = fileparts(which(mfilename)); % save files under the same name and at file location for gifVersion = [1 2 3 4 5 6]% switch gifVersion case 1 rng(1) % seed random generator to make results reproducible name = 'small_letters'; nFrames = 250; sizeLetters = [25 42]; % [nLetterRows nLetterColumns] dropLength = 35; % should be adjusted with regard to nFrames and sizeLetters(1) (must be smaller as nFrames) doubleDropRattio = 0.5; % ratios of columns with 2 drops per loop instad of just 1 DelayTime = 1/10; % in sec xLimits = [0 500]; % in pixel yLimits = [0 400]; % in pixel snapshotAtFrame = 50; case 2 rng(1) % seed random generator to make results reproducible name = 'medium_letters'; nFrames = 150; sizeLetters = [12 21]; % [nLetterRows nLetterColumns] dropLength = 25; % should be adjusted with regard to nFrames and sizeLetters(1) (must be smaller as nFrames) doubleDropRattio = 1; % ratios of columns with 2 drops per loop instad of just 1 DelayTime = 1/10; % in sec xLimits = [0 500]; % in pixel yLimits = [0 400]; % in pixel snapshotAtFrame = 50; case 3 rng(2) % seed random generator to make results reproducible name = 'medium_letters_2'; nFrames = 191; % hight Least common multiple with 150; so combinations do no allign sizeLetters = [12 21]; % [nLetterRows nLetterColumns] dropLength = 25; % should be adjusted with regard to nFrames and sizeLetters(1) (must be smaller as nFrames) doubleDropRattio = 1; % ratios of columns with 2 drops per loop instad of just 1 DelayTime = 1/10; % in sec xLimits = [0 500]; % in pixel yLimits = [0 400]; % in pixel snapshotAtFrame = 50; case 4 rng(3) % seed random generator to make results reproducible name = 'medium_letters_3'; nFrames = 229; sizeLetters = [12 21]; % [nLetterRows nLetterColumns] dropLength = 25; % should be adjusted with regard to nFrames and sizeLetters(1) (must be smaller as nFrames) doubleDropRattio = 1; % ratios of columns with 2 drops per loop instad of just 1 DelayTime = 1/10; % in sec xLimits = [0 500]; % in pixel yLimits = [0 400]; % in pixel snapshotAtFrame = 50; case 5 rng(4) % seed random generator to make results reproducible name = 'medium_letters_4'; nFrames = 259; sizeLetters = [12 21]; % [nLetterRows nLetterColumns] dropLength = 25; % should be adjusted with regard to nFrames and sizeLetters(1) (must be smaller as nFrames) doubleDropRattio = 1; % ratios of columns with 2 drops per loop instad of just 1 DelayTime = 1/10; % in sec xLimits = [0 500]; % in pixel yLimits = [0 400]; % in pixel snapshotAtFrame = 50; case 6 rng(1) % seed random generator to make results reproducible name = 'big_letters'; nFrames = 60; sizeLetters = [6 9]; % [nLetterRows nLetterColumns] dropLength = 15; % should be adjusted with regard to nFrames and sizeLetters(1) (must be smaller as nFrames) doubleDropRattio = 1; % ratios of columns with 2 drops per loop instad of just 1 DelayTime = 1/5; % in sec xLimits = [0 400]; % in pixel yLimits = [0 400]; % in pixel snapshotAtFrame = 50; otherwise error('not defined') end figHandle = figure(1562134155); clf axesHandle = axes; hold(axesHandle,'on') set(figHandle, 'Units','pixel'); set(figHandle, 'Position',[1 1 xLimits(2) yLimits(2)]); % big start image for antialiasing later [x y width height] set(axesHandle,'Color',[0 0 0]); % black background set(axesHandle,'Position',[0 0 1 1]); % stretch axis as big as figure, [x y width height] set(axesHandle,'XTick',NaN) % get rid of ticks set(axesHandle,'YTick',NaN) % get rid of ticks set(axesHandle,'TickLength',[0 0]) % get rid of ticks set(axesHandle,'YColor',[0 0 0]) % remove outline set(axesHandle,'XColor',[0 0 0]) % remove outline xlim(xLimits);ylim(yLimits); letterCube = repmat(randi(nChar,sizeLetters),1,1,nFrames); for iRand = 1:numel(letterCube)*0.02 % small proportion of flipping symbols letterRow = randi(sizeLetters(1)); % select random position letterCol = randi(sizeLetters(2)); % select random position letterThroughTime = letterCube(letterRow,letterCol,:); % extract letterThroughTime = letterThroughTime(:)'; letterThroughTime(1:randi(round(nFrames/3)+3,1,2)) = randi(nChar); % add a random section shift = randi(nFrames-1); letterThroughTime = [letterThroughTime(shift+1:end) letterThroughTime(1:shift)]; % time offset letterCube(letterRow,letterCol,:) = letterThroughTime; % write back end dropColMap = zeros(nFrames,3); % default Black dropColMap(1:dropLength,:) = letterColMap(dropLength); % drop colors with black to fill the list up glareColMap = zeros(nFrames,3); % default Black glareColMap(1,:) = 1; % drop tip white % for i=1:xLimits(2) % plot(i,100,'.','Color',map(i,:)) % end xDelta = xLimits(2)/sizeLetters(2); xCoordinates = (xDelta/2:xDelta:xLimits(2)); yDelta = yLimits(2)/sizeLetters(1); yCoordinates = (yDelta/2:yDelta:yLimits(2))+yDelta/10; handMat = NaN(sizeLetters); for iRow = 1:sizeLetters(1) for iCol = 1:sizeLetters(2) handMat(iRow,iCol) = text(xCoordinates(iCol),yCoordinates(iRow),... charList(letterCube(iRow,iCol,1)),... 'FontWeight','bold',... 'FontName','monospaced',... 'Color',[1 1 1],... 'FontUnits','pixel',... 'FontSize',yDelta*1.08,... 'HorizontalAlignment','center',... 'VerticalAlignment','middle'); end end stateMat = repmat((1:sizeLetters(1))' ,1, sizeLetters(2)); % initiate the basic rain drops running down the screen stateOffset = randi(nFrames,1,sizeLetters(2)); % create a basic offsets for different delays stateMat = stateMat + ones(sizeLetters(1),1) * stateOffset; stateOffset = randi(round(nFrames/3),1,sizeLetters(2))+randi(round(nFrames/5),1,sizeLetters(2)); % create secondary offsets for different delays stateOffset(rand(1,sizeLetters(2))>doubleDropRattio) = 0; % only a portion of columns get 2 drops stateMat2 = stateMat + ones(sizeLetters(1),1) * stateOffset; clearRGBimage = uint8(ones(yLimits(2),xLimits(2),3,nFrames)); % allocate shineRGBimage = clearRGBimage; % allocate for iFrame = 1:nFrames stateMat = rem(stateMat ,nFrames); % bring back into range [0 nFrames-1] stateMat2 = rem(stateMat2,nFrames); % bring back into range [0 nFrames-1] for iRow = 1:sizeLetters(1) for iCol = 1:sizeLetters(2) if stateMat(iRow,iCol)>stateMat2(iRow,iCol) % use the second drop set(handMat(iRow,iCol),'Color',dropColMap(stateMat2(iRow,iCol)+1,:),... 'string',charList(nChar-letterCube(iRow,iCol,iFrame)+1)); % use a mirrored charList to get different characters else % use the first drop set(handMat(iRow,iCol),'Color',dropColMap(stateMat(iRow,iCol)+1,:),... 'string',charList(letterCube(iRow,iCol,iFrame))); end end end drawnow fClear = getframe(figHandle); % secondary image using only the white letters to generate extra strong glare for iRow = 1:sizeLetters(1) for iCol = 1:sizeLetters(2) if stateMat(iRow,iCol)>stateMat2(iRow,iCol) % use the second drop set(handMat(iRow,iCol),'Color',glareColMap(stateMat2(iRow,iCol)+1,:),... 'string',charList(nChar-letterCube(iRow,iCol,iFrame)+1)); % use a mirrored charList to get different characters else % use the first drop set(handMat(iRow,iCol),'Color',glareColMap(stateMat(iRow,iCol)+1,:),... 'string',charList(letterCube(iRow,iCol,iFrame))); end end end drawnow fGlare = getframe(figHandle); stateMat = stateMat +1; % move drops "one position down" stateMat2 = stateMat2+1; % move drops "one position down" %% save animation fClear.cdata = fliplr(fClear.cdata); % view from "behind the code" fGlare.cdata = fliplr(fGlare.cdata); % view from "behind the code" weightFiddleMode = false; % for debugging ad adjusting if weightFiddleMode; figure(110); clf; imshow(fClear.cdata); end fShine.cdata = imgaussfilt(fClear.cdata,yDelta/3); % far reaching shine of all letters if weightFiddleMode; figure(111); clf; imshow(fShine.cdata); end fShine.cdata = fShine.cdata*1.5; % make brighter (automatically limited to range by unit8 format) if weightFiddleMode; figure(112); clf; imshow(fShine.cdata); end fEdge.cdata = imgaussfilt(fClear.cdata,yDelta/12); % stronger glow close to the letter edges if weightFiddleMode; figure(113); clf; imshow(fEdge.cdata); end fEdge.cdata = fEdge.cdata*0.7; % make darker if weightFiddleMode; figure(114); clf; imshow(fEdge.cdata); end fShine.cdata = imadd(fShine.cdata,fEdge.cdata); % combine shine and edge glow if weightFiddleMode; figure(115); clf; imshow(fShine.cdata); end if weightFiddleMode; figure(116); clf; imshow(fGlare.cdata); end fGlare.cdata = imgaussfilt(fGlare.cdata,yDelta/2); % far reaching shine of the white letter at the drop tip if weightFiddleMode; figure(117); clf; imshow(fGlare.cdata); end fGlare.cdata = fGlare.cdata*1.5; % make brighter (automatically limited to range by unit8 format) if weightFiddleMode; figure(118); clf; imshow(fGlare.cdata); end fShine.cdata = imadd(fShine.cdata,fGlare.cdata); % combine; this makes the drop tip double bright if weightFiddleMode; figure(119); clf; imshow(fShine.cdata); end fShine.cdata = imadd(fShine.cdata*0.8,fClear.cdata*0.5); % combine shine with original sharp letters if weightFiddleMode; figure(120); clf; imshow(fShine.cdata); end if weightFiddleMode; return; end clearRGBimage(:,:,:,iFrame) = fClear.cdata; % store shineRGBimage(:,:,:,iFrame) = fShine.cdata; % store if snapshotAtFrame == iFrame imwrite(fClear.cdata,fullfile(pathstr, [fname '_' name '_clear.png'])) % save png (native RGB no colormap) imwrite(fShine.cdata,fullfile(pathstr, [fname '_' name '_shine.png'])) % save png (native RGB no colormap) end end colormapImage = permute(clearRGBimage(:,:,:,:),[1 2 4 3]); % step1; make unified image colormapImage = reshape(colormapImage, yLimits(2),xLimits(2)*nFrames,3,1); % step2; make unified image mapClear = createImMap(colormapImage,64,[0 0 0; 1 1 1]); % colormap colormapImage = permute(shineRGBimage(:,:,:,:),[1 2 4 3]); % step1; make unified image colormapImage = reshape(colormapImage, yLimits(2),xLimits(2)*nFrames,3,1); % step2; make unified image mapShine = createImMap(colormapImage,128,[0 0 0; 1 1 1]); % colormap imClear = uint8(ones(yLimits(2),xLimits(2),1,nFrames)); % allocate imShine = uint8(ones(yLimits(2),xLimits(2),1,nFrames)); % allocate for iFrame = 1:nFrames imClear(:,:,1,iFrame) = rgb2ind(clearRGBimage(:,:,:,iFrame),mapClear); % ,'nodither'); imShine(:,:,1,iFrame) = rgb2ind(shineRGBimage(:,:,:,iFrame),mapShine); % ,'nodither'); end imwrite(imClear,mapClear,fullfile(pathstr, [fname '_' name '_clear.gif']),'DelayTime',DelayTime,'LoopCount',inf) % save gif imwrite(imShine,mapShine,fullfile(pathstr, [fname '_' name '_shine.gif']),'DelayTime',DelayTime,'LoopCount',inf) % save gif disp([fname '_' name '_clear.gif has ' num2str(numel(imClear)/10^6 ,4) ' Megapixels']) % Category:Animated GIF files exceeding the 50 MP limitend function map = letterColMap(nCol)% map:% Pos 1: white (short burst)% a: bright green changing to green% b: staying green ()% c: fading to black querries = linspace(0,100,nCol-1); x = [0 15 40 100]; % percent of map% a b c red = interp1(x,... [0.4 0.0 0.0 0],... intensity querries);blue = interp1(x,... [0.4 0.0 0.0 0],... intensity querries);green = interp1(x,... [1 0.8 0.8 0],... intensity querries); map = [ [1 1 1];red(:) green(:) blue(:)]; % white in frontfunction map = createImMap(imRGB,nCol,startMap)% createImMap creates a color-map including predefined colors.% "rgb2ind" creates a map but there is no option to predefine some colors,% and it does not handle stacked images.% Input:% imRGB: image, [imRows x imColumns x 3(RGB) x nStack] (unit8)% nCol: total number of colors the map should have, [integer]% startMap: predefined colors; colormap format, [p x 3] (double)imRGB = permute(imRGB,[1 2 4 3]); % step1; make unified column-image (handling possible nStack)imRGBcolumn = reshape(imRGB,[],1,3,1); % step2; make unified column-imagefullMap = double(permute(imRGBcolumn,[1 3 2]))./255; % "column image" to color map [fullMap,~,imMapColumn] = unique(fullMap,'rows'); % find all unique colors; create indexed colormap-image% "cmunique" could be used but is buggy and inconvenient because the output changes between "uint8" and "double"nColFul = size(fullMap,1);nColStart = size(startMap,1);disp(['Number of colors: ' num2str(nColFul) ' (including ' num2str(nColStart) ' self defined)']);if nCol<=nColStart; error('Not enough colors'); endif nCol>nColFul; warning('More colors than needed'); endisPreDefCol = false(size(imMapColumn)); % init for iCol = 1:nColStart diff = sum(abs(fullMap-repmat(startMap(iCol,:),nColFul,1)),2); % difference between a predefined and all colors [mDiff,index] = min(diff); % find matching (or most similar) color if mDiff>0.05 % color handling is not precise warning(['Predefined color ' num2str(iCol) ' does not appear in image']) continue end isThisPreDefCol = imMapColumn==index; % find all pixel with predefined color disp([num2str(sum(isThisPreDefCol(:))) ' pixel have predefined color ' num2str(iCol)]); isPreDefCol = or(isPreDefCol,isThisPreDefCol); % combine with overall listend[~,mapAdditional] = rgb2ind(imRGBcolumn(~isPreDefCol,:,:),nCol-nColStart,'nodither'); % create map of remaining colorsmap = [startMap;mapAdditional];