File:Mono Tube Damper.gif

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search

Original file(203 × 945 pixels, file size: 17.74 MB, MIME type: image/gif, looped, 520 frames, 16 s)

Captions

Captions

Animation of a simplified monotube damper.

Summary[edit]

Description
English: Animation of a simplified monotube damper.
Date
Source Own work
Author Jahobr
Other versions
GIF development
InfoField
 
This diagram was created with MATLAB by Jahobr.
Source code
InfoField

MATLAB code

function [] = Oil_Damper()
% Programmed in Matlab R2017a.
% Animation of a twin and mono tube oil damper.
% Before anything is drawn the algorithm loops the frames
%    to determine the particle pathes.
% Several versions are created.
%    .gif, .mp4 and .webm
%
% 2020-08-07 Jahobr - Licensing: CC0 1.0 Universal Public Domain Dedication


[pathstr,fname] = fileparts(which(mfilename)); % save files under the same name and at file location

RGB.edge       = [0    0    0  ];
RGB.white      = [1    1    1  ];
RGB.brightGrey = [0.8  0.8  0.8]; % housing
RGB.blueGrey   = [0.75 0.75 0.9]; % moving metal
RGB.oil        = [0.9  0.9  0  ];

RGB = structfun(@(q)round(q*255)/255, RGB, 'UniformOutput',false); % round to values that are nicely uint8 compatible

screenSize = get(groot,'Screensize')-[0 0 5 20]; % [1 1 width height] (minus tolerance for figure borders)

for mode = {'Twin_Tube_Damper','Mono_Tube_Damper'} %
    
    %% y-axis-dimensions (Twin is default)
    %                     _______________________________________________________________
    wall = 0.5;          %                        wall width                             ¦
    outerRad = 4.5;      %  ,-------------------------outerRadius--------------------,   ¦
    %                    %  ¦    ____________________________________________________¦   ¦
    %                    %  ¦   ¦                         wall width                     ¦
    innerRad = 3.0;      %  ¦   ¦  ,---------,====,--innerRadius-(pistonRadius)--,       ¦
    %                    %  ¦   ¦__¦         ¦____¦                              ¦       ¦
    holeY = [2.2  1.5];  %  ¦   :__:  holeY  :____:                              ¦       ¦
    %                    %  ¦   ¦  ¦         ¦    ¦                              ¦       ¦
    rodRad = 1.0;        %  ¦   ¦  ¦         ¦    '--------rodRadius-----------------------------,
    %                    %  ¦   ¦  ¦         ¦                                                   ¦
    %% x-axis-dimensions    + centerline - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
    %                    %  ¦   ¦  0-Position¦                                                   ¦
    rodLenght = 20;      %  ¦   ¦  ¦         ¦    ,<----------------rodLenght------------------->'
    %                    %  ¦   ¦__¦         ¦    ¦                              ¦       ¦
    pistonWidth = 3;     %  ¦   :__:       pistonWidth                           ¦       ¦
    l_inner = 0.0;       %  ¦   ¦  ¦l_inner  ¦    ¦                              ¦       ¦
    r_inner = 20;        %  ¦   ¦  '----------====------------------------r_inner'       ¦
    %                    %  ¦   ¦____________________________________________________,   ¦
    l_outer = -2.5;      %  ¦left_outer                                              ¦   ¦
    r_outer = r_inner+3*wall; %  ---------------------------------------------r_outer'   ¦
    %                    %_______________________________________________________________¦
    
    xLimits = [-4.1 38.2]; % ADJUST
    yLimits = [-5.8  5.8]; % ADJUST

    switch mode{1} %
        case 'Mono_Tube_Damper'
            l_inner = -4.0;
            xLimits = [-5.5 38.2]; % ADJUST
            yLimits = [-4.7  4.7]; % ADJUST
    end
    
    nParticlesOil = 250; % number of moving particles (oil "bubbles")
    nParticlesGas = 50;  % number of moving particles (gas)

    xRange = xLimits(2)-xLimits(1);
    yRange = yLimits(2)-yLimits(1);
    imageAspectRatio = xRange/yRange;

    for form = {'gif'} % {'mp4','gif'} % file format

        switch form{1} %
            case 'gif'
                
                nFrames = 520;
                pistonPosList = cos(linspace(2*pi,0,nFrames)); % [1 -1 1] one loop
                
                delayTime = ones(1,nFrames)*1/30; % delay per frame
                %             delayTime(1) = 0.5;  % keep first frame longer (did not look good)
                %             delayTime(end) = 0.5; % keep last frame longer (did not look good)
                %             delayTime(round(nFrames/2)) = 1; % keep other extreme position frame longer
                
                MegaPixelTarget = 100*10^6; % Category:Animated GIF files exceeding the 100 MP limit
                pxPerImage = MegaPixelTarget/nFrames; % pixel per gif frame
                ySize = sqrt(pxPerImage/imageAspectRatio); % gif height
                xSize = ySize*imageAspectRatio; % gif width
                xSize = floor(xSize); ySize = floor(ySize); % full pixels
                scaleReduction = min(...% repeat as often as possible for nice antialiasing
                    floor(screenSize(4)/ySize), floor(screenSize(3)/xSize));
                
                if scaleReduction == 0;   error('"MegaPixelTarget" not possible; use smaller target or bigger monitor'); end % check
                
            case 'mp4'
                repetitions = 3;
                nFrames = 520*repetitions;
                %nFrames = 30;
                pistonPosList = cos(linspace(2*pi*repetitions,0,nFrames)); % [1 -1 1] one loop
                
                xSize = screenSize(3); %max size
                ySize = xSize/imageAspectRatio; % gif width
                xSize = floor(xSize/16)*16; ySize = floor(ySize/16)*16; % full pixels, multiple-of-16 boundaries (MPEG splits the video into 16x16 squares called macroblocks)
                scaleReduction = 1; % not usefull for mp4 or webm (gif only)
            otherwise
                error('unknown mode')
        end
        
        oilRed = ceil(xSize*scaleReduction/500); % size reduction factor to speed things up
        
        figPos = [1 1 xSize*scaleReduction ySize*scaleReduction]; % big start image for antialiasing later [x y width height]

        liSc = ySize*scaleReduction/150; % LineWidth scale; LineWidth is absolute, a bigger images needs thicker lines to keep them in proportion
        
        yRange = xRange * ySize/xSize;  % xSize and ySize were rounded; we have the adjust the yRange to stay in the same aspect ratio
        yLimits = [-yRange/2 yRange/2]; % xSize and ySize were rounded; we have the adjust the yRange to stay in the same aspect ratio
        
        figHandle = figure(117854461); clf
        axesHandleOil = axes; hold(axesHandleOil,'on')
        set(figHandle, 'Position', figPos);
        if ~all(get(figHandle, 'Position') == figPos);   error('figure Position could not be set');   end % check
        set(figHandle,'Color',RGB.white); % white background
        set(axesHandleOil,'Position',[0 0 1 1]); % stretch axis as big as figure, [x y width height]
        xlim(xLimits); ylim(yLimits); % set axis limits
        axis off % invisible axes (no ticks)
        axis equal; drawnow;
        
        firstIter = true;

        pistonPosList = (pistonPosList(:)+1)*0.5; % normalize range to [0 1]
        fullPistonPosTravel = r_inner-pistonWidth; % left end is defined as 0
        
        pistonPosList = (pistonPosList*fullPistonPosTravel*0.75) + (fullPistonPosTravel*0.125); % 75% travel
        % pistonPosList = pistonPosList*fullPistonPosTravel; % 100% travel

        ctrs = NaN(nParticlesOil,2,nFrames); % allocate paths of clusters(numberOFCluster, x-y-coordinate, frame)
        
        oilPos = 18-pistonPosList;  % Twin_Tube_Damper
        pistonPos = [pistonPosList pistonPosList+pistonWidth]; % Mono & Twin_Tube_Damper
        
        AirOilSeparatorPos = 5+((pistonPosList-fullPistonPosTravel)/3);      % Mono_Tube_Damper
        AirOilSeparatorPos = [AirOilSeparatorPos-wall*4 AirOilSeparatorPos]; % Mono_Tube_Damper
        
        %% particle path loop
        %               r g b
        OilColStatic = [0 0 0]; % black,   static
        OilColOuter  = [0 0 1]; % blue,    outer chamber moves left slow
        OilColHolesB = [0 1 0]; % green,   holes base moves right fast
        OilColHolesP = [1 0 0]; % red,     holes piston moves left fast
        OilColBase   = [1 1 0]; % yellow,  outer moves left slow
        OilColBaseUp = [1 0 1]; % magenta, vertical movement
        OilColBaseDo = [0 1 1]; % cyan,    vertical movement
        
        for iFrame = 1:nFrames
            
            figure(figHandle); cla
                        
            plotBox([pistonPos(iFrame,2) r_inner], [ innerRad  rodRad], OilColStatic, 'none', liSc) % inner oil chamber above the rod
            plotBox([pistonPos(iFrame,2) r_inner], [-rodRad -innerRad], OilColStatic, 'none', liSc) % inner oil chamber below the rod
            
            if strcmp(mode{1},'Twin_Tube_Damper')
                plotBox([l_inner pistonPos(iFrame,1)], [-1 1]*innerRad, OilColBase, 'none', liSc) % inner oil chamber, in front of the piston
            else % Mono_Tube_Damper
                plotBox([AirOilSeparatorPos(iFrame,2) pistonPos(iFrame,1)], [-1 1]*innerRad, OilColBase, 'none', liSc) % inner oil chamber, in front of the piston
            end
            
            plotBox(pistonPos(iFrame,:)+[-wall wall]*2, +holeY, OilColHolesP, 'none', liSc) % upper hole, through the piston
            plotBox(pistonPos(iFrame,:)+[-wall wall]*2, -holeY, OilColHolesP, 'none', liSc) % lower hole, through the piston           
            
            if strcmp(mode{1},'Twin_Tube_Damper')
                plotBox([l_outer oilPos(iFrame)], [innerRad+wall outerRad],OilColOuter, 'none' ,liSc) % upper outer oil chamber
                plotBox([l_outer oilPos(iFrame)],-[innerRad+wall outerRad],OilColOuter, 'none', liSc) % lower outer oil chamber
                
                plotBox([l_outer l_inner-2*wall], [-1 1]*(innerRad+wall),     OilColStatic, 'none', liSc) % outer oil chamber bottom, static oil
                plotBox([l_outer l_inner-2*wall], [-outerRad+wall -holeY(2)], OilColBaseUp, 'none', liSc) % outer oil chamber bottom, oil up
                plotBox([l_outer l_inner-2*wall], [+outerRad-wall +holeY(2)], OilColBaseDo, 'none', liSc) % outer oil chamber bottom, oil down
                
                plotBox([l_outer l_inner+wall*2],+holeY, OilColHolesB, 'none', liSc) % upper hole, connecting inner and outer volume
                plotBox([l_outer l_inner+wall*2],-holeY, OilColHolesB, 'none', liSc) % lower hole, connecting inner and outer volume
            end
            
            xlim(xLimits); ylim(yLimits); % set axis limits
            axis equal; drawnow;
            f = getframe(figHandle);
            OilImage = f.cdata;
            
            isOilMap = ~and(and(OilImage(:,:,1)==uint8(255),OilImage(:,:,2)==uint8(255)),OilImage(:,:,3)==uint8(255));
            isOilMap = imerode(isOilMap,strel('rectangle',[1 1].*round(ySize*scaleReduction/35))); % paricles away from border
            
            OilImage = randSubsampling(OilImage, oilRed); % reduce size
            isOilMap = randSubsampling(isOilMap, oilRed); % reduce size
            
            figure(23446); clf
            image(isOilMap*255)
            
            [row,col] = find(isOilMap);

            optsFirst = statset('Display', 'final', 'UseParallel', true, 'MaxIter', 3000);
            optsIter  = statset('Display', 'final', 'UseParallel', true, 'MaxIter', 1);
            if firstIter
                [idx,ctrs(:,:,iFrame)] = kmeans([row,col],nParticlesOil,'Distance','sqEuclidean','start','uniform',...
                    'Options',optsFirst,'replicates',40);
                firstIter = false;
            else
                
                disp('nextIter')
                movSpeed = pistonPosList(iFrame)-pistonPosList(iFrame-1);
                movSpeedPx = movSpeed/xRange*xSize/oilRed*scaleReduction;
                ctrs(:,:,iFrame) = ctrs(:,:,iFrame-1); % use previous state
                
                for iCluster = 1:nParticlesOil
                    colorParticle = OilImage(round(ctrs(iCluster,1,iFrame)),round(ctrs(iCluster,2,iFrame)),:);
                    colorParticle = colorParticle(:)';
                    if(all(colorParticle==OilColStatic*255)) % black static
                        % do nothing
                    elseif(all(colorParticle==OilColOuter*255))  % = [0 0 1]; blue, outer chamber moves left slow
                        ctrs(iCluster,2,iFrame) = ctrs(iCluster,2,iFrame)-movSpeedPx;
                    elseif(all(colorParticle==OilColHolesB*255)) % = [0 1 0]; green, holes base moves right fast
                        ctrs(iCluster,2,iFrame) = ctrs(iCluster,2,iFrame)+1.5*movSpeedPx;
                    elseif(all(colorParticle==OilColHolesP*255)) % = [1 0 0]; red, holes piston moves left fast
                        ctrs(iCluster,2,iFrame) = ctrs(iCluster,2,iFrame)-3.5*movSpeedPx; % 3 3.2
                    elseif(all(colorParticle==OilColBase*255))   % = [1 1 0]; yellow, outer moves left slow
                        ctrs(iCluster,2,iFrame) = ctrs(iCluster,2,iFrame)+0.4*movSpeedPx; % 0.3333
                        
                    elseif(all(colorParticle==OilColBaseUp*255)) % = [1 0 1]; magenta, vertical movement down
                        ctrs(iCluster,1,iFrame) = ctrs(iCluster,1,iFrame)-movSpeedPx*0.8;
                    elseif(all(colorParticle==OilColBaseDo*255)) % = [0 1 1]; cyan, vertical movement up
                        ctrs(iCluster,1,iFrame) = ctrs(iCluster,1,iFrame)+movSpeedPx*0.8;
                        
                    end
                end

                [idx,ctrs(:,:,iFrame)] = kmeans([row,col],nParticlesOil,'Distance','sqEuclidean','start',ctrs(:,:,iFrame),...
                    'Options',optsIter);
            end
        end
        
        %% filter paths
        
        % figure(2376574); clf; hold on
        % xlim([1 xSize]/oilRed); ylim([1 ySize]/oilRed); % set axis limits
        wind = 4;
        for iCluster = 1:nParticlesOil % for all cluster-enters (particles)
            %     cla
            %     plot(permute(ctrs(iCluster,2,:),[3,2,1]),permute(ctrs(iCluster,1,:),[3,2,1]),'b-');
            ctrs(iCluster,1,:) = filtfilt(ones(1,wind)/wind,1,ctrs(iCluster,1,:)); % filter particle path x-coordinate
            ctrs(iCluster,2,:) = filtfilt(ones(1,wind)/wind,1,ctrs(iCluster,2,:)); % filter particle path y-coordinate
            %     plot(permute(ctrs(iCluster,2,:),[3,2,1]),permute(ctrs(iCluster,1,:),[3,2,1]),'m-');
            %     pause()
        end

        switch form{1} %
            case 'gif'
                
                reducedRGBimage = ones([ySize,xSize,3,nFrames],'uint8'); % allocate
                
            case 'mp4'
                vid = VideoWriter(mode{1},'MPEG-4'); % Prepare the new file
                vid.FrameRate = 30;
                vid.Quality = 100; % quality in [0% 100%]
                open(vid);
        end
        
        gasColMap = cool(100);
        minOilPos = min(oilPos);
        maxOilPos = max(oilPos);        

        if strcmp(mode{1},'Twin_Tube_Damper')
            gasOffset_x = 0.75*((rand(nParticlesGas/2,2).*wall)-wall/2);  % gas positions distribution.
            gasOffset_x(1,:)   = 0; % avoid the leftmost particle get over the line
            gasOffset_x(end,:) = 0; % avoid the rightmost particle get over the line
            gasOffset_x = gasOffset_x(:);
            gasOffset_y = (rand(nParticlesGas,1).*wall*0.2)+0.09; % gas positions distribution.
            gasOffset_y = gasOffset_y .* (2*rem( (1:nParticlesGas)', 2) - 1); % alternate 1 -1 1 -1 offset
            colorShading = [ones(nParticlesGas/2,1)*0.5; ones(nParticlesGas/2,1)*0.7];

            gasPos_x = linspace(0, 1, (nParticlesGas)+1)';
            gasPos_x = [gasPos_x(2:2:end); gasPos_x(2:2:end)];
            gasPos_y = mean(outerRad-wall, innerRad)* ones(nParticlesGas/2,1);
            gasPos_y_scale = [gasPos_y; -gasPos_y];
        else
            XY_gasPos = net(haltonset(2,'Skip',1),nParticlesGas*1.2); % generate quasi random numbers, 'Skip',1 to remove the point at 0,0 
            clusterIndex = clusterdata(XY_gasPos,nParticlesGas); % XY_gasPos contains still some very close neighbours (touching particles)
            [~,representativeOfCluster] = unique(clusterIndex); % get only one member of each cluster
            gasPos_x = XY_gasPos(representativeOfCluster,1); % extract nicely distributed quasi-random random numbers
            gasPos_y = XY_gasPos(representativeOfCluster,2); % extract nicely distributed quasi-random random numbers
            colorShading = interp1([0 1],[0.7 0.5],gasPos_y);
            gasPos_y_scale = (gasPos_y-0.5)*2*(innerRad-wall/2); % scale to full radius
            gasOffset_x = (rand(nParticlesGas,1)-0.5).*wall*0.3; % 
            gasOffset_y = (rand(nParticlesGas,1)-0.5).*wall*0.3; % 
        end
        
        %%  plot loop
        for iFrame = 1:nFrames
            
            figure(figHandle); cla
            
            gasTempPercent = round((oilPos(iFrame)-minOilPos)/(maxOilPos-minOilPos)*99)+1;
            
            if strcmp(mode{1},'Twin_Tube_Damper')
                plotBox([l_outer-2*wall l_outer], [-1 1]*(outerRad+wall), RGB.brightGrey, RGB.edge ,liSc) % outer body left, bottom
                
                plotCylinder([l_outer oilPos(iFrame)], [-1 1]*outerRad, RGB.oil,   RGB.edge, liSc, false) % outer body inner void (oil)
                plotCylinder([oilPos(iFrame) r_outer], [-1 1]*outerRad, gasColMap(gasTempPercent,:), RGB.edge, liSc, false) % outer body inner void (air)
                
                xLid = [...
                    r_outer r_outer+2*wall;... box1
                    r_inner r_inner+3*wall]; %  box2, both boxes will be unified into one object
                yLid =  [...
                    [-1 1]*(outerRad+wall);... box1
                    [-1 1]*innerRad]; %  box2, both boxes will be unified into one object
                plotBox(xLid, yLid, RGB.brightGrey, RGB.edge ,liSc) % outer body right, lid
                
                plotBox([l_outer-wall r_outer+wall],  [outerRad outerRad+wall], RGB.brightGrey, RGB.edge ,liSc) % outer body upper wall
                plotBox([l_outer-wall r_outer+wall], -[outerRad outerRad+wall], RGB.brightGrey, RGB.edge ,liSc) % outer body lower wall
                
                plotCylinder([l_inner r_inner], [-1 1]*innerRad,RGB.oil ,RGB.edge, liSc, false) % inner body oil filling
            else % Mono_Tube_Damper
                plotCylinder([AirOilSeparatorPos(iFrame,2) r_inner], [-1 1]*innerRad, RGB.oil ,RGB.edge, liSc, false) % inner body oil filling
                plotBox(AirOilSeparatorPos(iFrame,:),[-1 1]*innerRad, RGB.blueGrey, RGB.edge, liSc) % 2nd piston between oil and air
                plotCylinder([l_inner AirOilSeparatorPos(iFrame,1)], [-1 1]*innerRad, gasColMap(gasTempPercent,:) ,RGB.edge, liSc, false) % inner body air filling
                
                plotBox(AirOilSeparatorPos(iFrame,:)+[wall -wall], [0 -wall/2]+innerRad, [0.1 0.1 0.1], RGB.edge, liSc) % flat piston seal
                plotBox(AirOilSeparatorPos(iFrame,:)+[wall -wall], [0 +wall/2]-innerRad, [0.1 0.1 0.1], RGB.edge, liSc) % flat piston seal

                plotBox([r_inner r_outer+3*wall], [-1 1]*(innerRad+wall), RGB.brightGrey, RGB.edge ,liSc) % outer body right, lid
                
            end
            
            plotBox([l_inner-2*wall l_inner], [-1 1]*(innerRad+wall),RGB.brightGrey, RGB.edge ,liSc) % inner body left, bottom
            
            plotBox([l_inner-wall r_inner+3*wall],  [innerRad innerRad+wall], RGB.brightGrey, RGB.edge ,liSc) % inner body upper wall
            plotBox([l_inner-wall r_inner+3*wall], -[innerRad innerRad+wall], RGB.brightGrey, RGB.edge ,liSc) % inner body upper wall
            
            if strcmp(mode{1},'Twin_Tube_Damper')
                plotCylinder([l_inner-2*wall l_inner], +holeY, RGB.oil ,RGB.edge ,liSc, false) % hole in inner lid
                plotCylinder([l_inner-2*wall l_inner], -holeY, RGB.oil ,RGB.edge ,liSc, false) % hole in inner lid
            end
            
            plotSeal([1 2]*wall+r_outer, [0  wall]+rodRad, RGB.brightGrey, RGB.edge, liSc, '\') % plot upper wiper seal
            plotSeal([1 2]*wall+r_outer, [0 -wall]-rodRad, RGB.brightGrey, RGB.edge, liSc, '/') % plot lower wiper seal
            
            plotSeal([1 2]*wall+r_inner, [0  wall]+rodRad, RGB.brightGrey, RGB.edge, liSc, 'o') % plot o-Ring
            plotSeal([1 2]*wall+r_inner, [0 -wall]-rodRad, RGB.brightGrey, RGB.edge, liSc, 'o') % plot o-Ring
            
            plotCylinder(pistonPos(iFrame,2)+[0 rodLenght],[-1 1]*rodRad, RGB.blueGrey, RGB.edge, liSc, true) % rod
            plotBox(pistonPos(iFrame,:),[-1 1]*innerRad, RGB.blueGrey, RGB.edge, liSc) % piston
            
            plotCylinder(pistonPos(iFrame,:),+holeY, RGB.oil, RGB.edge, liSc, false) % hole in piston
            plotCylinder(pistonPos(iFrame,:),-holeY, RGB.oil, RGB.edge, liSc, false) % hole in piston
            
            plotBox(pistonPos(iFrame,:)+[wall -wall], [0 -wall/2]+innerRad, [0.1 0.1 0.1], RGB.edge, liSc) % flat piston seal
            plotBox(pistonPos(iFrame,:)+[wall -wall], [0 +wall/2]-innerRad, [0.1 0.1 0.1], RGB.edge, liSc) % flat piston seal
            
            figure(figHandle)
            
            scatter(... % plot oil particles
                (ctrs(:,2,iFrame)*oilRed/scaleReduction-1)*(xRange/xSize)+xLimits(1),... % x-pos
                (ctrs(:,1,iFrame)*oilRed/scaleReduction-1)*(yRange/ySize)+yLimits(1),... % y-pos
                (liSc^2)*10, 'o',...  % size and shape
                'LineWidth',  liSc,...
                'MarkerEdgeColor', RGB.oil.*0.6,...
                'MarkerFaceColor', RGB.oil.*0.9,...
                'MarkerEdgeAlpha', 0.5,...
                'MarkerFaceAlpha', 0.5)
            
            if strcmp(mode{1},'Twin_Tube_Damper')
                gasPos_x_scale = interp1([0 1],[oilPos(iFrame)+wall/4, r_outer-wall/4],gasPos_x);
            else % Mono_Tube_Damper
                gasPos_x_scale = interp1([0 1],[l_inner+wall/2, AirOilSeparatorPos(iFrame,1)-wall/2],gasPos_x);
            end
            
            scatter(... % plot gas particles
                gasPos_x_scale + gasOffset_x*gasTempPercent/100 + randn(nParticlesGas,1)*0.05*gasTempPercent/100,... % x-pos
                gasPos_y_scale + gasOffset_y*gasTempPercent/100 + randn(nParticlesGas,1)*0.05*gasTempPercent/100,... % y-pos
                200 + (pistonPos(iFrame,2)^2 * (liSc^2)*0.8),...
                colorShading * gasColMap(gasTempPercent,:),...
                '.')
            
            %% save animation
            xlim(xLimits); ylim(yLimits); % set axis limits
            axis equal; drawnow;
            f = getframe(figHandle);
            
            switch form{1} % Init frame(s)
                case {'mp4'}
                    writeVideo(vid, rot90(f.cdata)); % save video frames
                case {'gif'}
                    reducedRGBimage(:,:,:,iFrame) = imReduceSize(f.cdata,scaleReduction); % the size reduction: adds antialiasing
            end
            
            if iFrame == nFrames
                plot((col*oilRed/scaleReduction-1)*(xRange/xSize)+xLimits(1), (row*oilRed/scaleReduction-1)*(yRange/ySize)+yLimits(1),'.b') % check points for clustering (debug)
            end
            
        end % plot loop
        
        switch form{1} %
            case 'gif'
                
                colormapImage = permute(reducedRGBimage(:,:,:,:),[1 2 4 3]); % step1; make unified image
                colormapImage = reshape(colormapImage,[ySize,xSize*(nFrames) 3 1]); % step2; make unified image
                
                map = createImMap(colormapImage,256,[RGB.edge;RGB.white;RGB.brightGrey;RGB.blueGrey;RGB.oil;min(colorShading)*gasColMap(end,:)]); % colormap
                
                for iFrame = 1:nFrames
                    im = rgb2ind(reducedRGBimage(:,:,:,iFrame),map,'dither');
                    
                    if iFrame == 1
                        imwrite(rot90(im),map,fullfile(pathstr, [mode{1} '.gif']), 'LoopCount',Inf,'DelayTime',delayTime(iFrame)); % individual timings
                    else
                        imwrite(rot90(im),map,fullfile(pathstr, [mode{1} '.gif']), 'WriteMode','append','DelayTime',delayTime(iFrame)); % individual timings
                    end
                end
                
                disp([mode{1} '.gif  has ' num2str(numel(reducedRGBimage)/3/10^6 ,4) ' Megapixels']) % Category:Animated GIF files exceeding the 100 MP limit
                
            case 'mp4'
                close(vid ); disp(['MP4 completed: ' mode{1} '.mp4']);
                disp(['Conversion of ' mode{1} ' from MP4 to WebM (Matlab currently does not support webm)']);
                if exist('ffmpeg.exe','file') == 2 % check if it is a valid path to an existing file
                    try
                        dosCommand = ['!ffmpeg -i ' mode{1} '.mp4 '...   % Command and source
                            '-c:v libvpx-vp9 -crf 30 -b:v 0 -deadline best '... % constant (best) quality; see: https://trac.ffmpeg.org/wiki/Encode/VP9
                            '-metadata title="' mode{1} '" '... % see: https://wiki.multimedia.cx/index.php/FFmpeg_Metadata
                            '-metadata author="Jahobr" -metadata copyright="CC0 1.0 Universal Public Domain Dedication" '...
                            '-metadata license="CC0 1.0 Universal Public Domain Dedication" '...
                            mode{1} '.webm'];                            % conversion target
                        eval(dosCommand); % run conversion from mp4 to webm
                        % delete(fullfile(pathstr,[mode{1} '.mp4'])); % delete mp4 version after conversion (to save disc space)
                    catch ME
                        disp(ME)
                    end
                else
                    disp(['"ffmpeg.exe" not available in path "' pathstr '" (or other problem); see https://ffmpeg.zeranoe.com/builds/']);
                end
        end
    end
end
  

function disc(x,y,r,colFa)
% x coordinates of the center
% y coordinates of the center
% r is the radius of the circle
% colFa face color   [r g b]

angleOffPoints = linspace(0,2*pi,300);
xc = x + r*sin(angleOffPoints);
yc = y + r*cos(angleOffPoints);

patch(xc,yc,'k','LineWidth',1,'EdgeColor','none','FaceColor',colFa);


function plotSeal(x,y,colFa,colEd,linw,type)
% x [left right]
% y [top bottom]
% colFa  face color  [r g b]
% colEd  edge color  [r g b]
% linw  line width

x = sort(x); y = sort(y); % just nor make sure
plotBox(x, y, colFa, colEd, linw)
switch type
    case {'o','O'}
        disc(mean(x), mean(y), (y(2)-y(1))/2.3, [0.1 0.1 0.1])
    case '/'
        plot(x,           y, 'Color', colEd, 'LineWidth', linw*1.5)
    case '\'
        plot([x(2) x(1)], y, 'Color', colEd, 'LineWidth', linw*1.5)
end


function plotBox(x,y,colFa,colEd,linw)
% x [left right]
% y [top bottom]
%  Or 
% x [left right;... box1
%    left right] %  box2, both boxes will be unified int one object
% y [left right;... box1
%    left right] %  box2, both boxes will be unified int one object
% colFa  face color  [r g b]
% colEd  edge color  [r g b]
% linw  line width

xs = [ ]; % x
ys = [ ]; % y

for iBox = 1:size(x,1)
    xAdd = [x(iBox,2)       x(iBox,2) x(iBox,1) x(iBox,1) x(iBox,2)       x(iBox,2)]; % x
    yAdd = [mean(y(iBox,:)) y(iBox,1) y(iBox,1) y(iBox,2) y(iBox,2) mean(y(iBox,:))]; % y joint in the middle of an edge to get nice corners
    [xs,ys] = polybool('union', xs, ys, xAdd, yAdd);
end

if strcmp(colFa,'none')
   plot(xs,ys,'Color',colEd,'LineWidth',linw) %
else
   patch(xs,ys,colFa,'EdgeColor',colEd,'LineWidth',linw) %
end


function plotCylinder(x,y,colFa,colEd,linw,material_or_hole)
% x [left right]
% y [top bottom]
% colFa  face color  [r g b]
% colEd  edge color  [r g b]
% linw  line width
% material_or_hole:    1 Material, Highlight in top
%                      0 Hole, Highlight in bottom
x = sort(x);
y = sort(y);
if material_or_hole
   y = flip(y);
end
y_center = mean(y);
y_highlight = mean([y(1) y_center]);

vertex_matrix = [ % clockwise                                8----1
    x(1)  y(1);        % 1 right top                         ¦    ¦
    x(1)  y_highlight; % 2 right upper highlight             7----2
    x(1)  y_center;    % 3 right center                      ¦    ¦
    x(1)  y(2);        % 4 right bottom                      6----3
    x(2)  y(2);        % 5 left  bottom                      ¦    ¦
    x(2)  y_center;    % 6 left  center                      ¦    ¦
    x(2)  y_highlight; % 7 left  upper highlight             ¦    ¦
    x(2)  y(1)];       % 8 left  top                         5----4


vertex_color = [ % color matrix
    colFa;           % 1 top 
    1-(1-colFa)*0.6; % 2 upper highlight (brighter)
    colFa;           % 3 center 
    colFa*0.8];      % 4 bottom (darker)

vertex_color = [vertex_color; flipud(vertex_color)]; % colors 5 to 8 in inverse order

faces_matrix = [ % top to bottom
    1 2 7;  % top to highlight,    upper right triangle
    1 7 8;  % top to highlight,    lower left triangle
    2 3 6;  % highlight to center, upper right triangle
    2 6 7;  % highlight to center, lower left triangle
    3 4 5;  % center to bottom,    upper right triangle
    3 5 6]; % center to bottom,    lower left triangle

patch('Vertices',vertex_matrix,'Faces',faces_matrix,...
      'FaceVertexCData',vertex_color,'FaceColor','interp',...
      'Edgecolor','none');
plotBox(x,y,'none',colEd,linw)


function im = imReduceSize(im,redSize)
% Input:
%  im:      image, [imRows x imColumns x nChannel x nStack] (unit8)
%                      imRows, imColumns: must be divisible by redSize
%                      nChannel: usually 3 (RGB) or 1 (grey)
%                      nStack:   number of stacked images
%                                usually 1; >1 for animations
%  redSize: 2 = half the size (quarter of pixels)
%           3 = third the size (ninth of pixels)
%           ... and so on
% Output:
%  im:     [imRows/redSize x imColumns/redSize x nChannel x nStack] (unit8)
%
% an alternative is: imNew = imresize(im,1/reduceImage,'bilinear');
%        BUT 'bicubic' & 'bilinear'  produces fuzzy lines
%        IMHO this function produces nicer results as "imresize"
 
[nRow,nCol,nChannel,nStack] = size(im);

if redSize==1;  return;  end % nothing to do
if redSize~=round(abs(redSize));             error('"redSize" must be a positive integer');  end
if rem(nRow,redSize)~=0;     error('number of pixel-rows must be a multiple of "redSize"');  end
if rem(nCol,redSize)~=0;  error('number of pixel-columns must be a multiple of "redSize"');  end

nRowNew = nRow/redSize;
nColNew = nCol/redSize;

im = double(im).^2; % brightness rescaling from "linear to the human eye" to the "physics domain"; see youtube: /watch?v=LKnqECcg6Gw
im = reshape(im, nRow, redSize, nColNew*nChannel*nStack); % packets of width redSize, as columns next to each other
im = sum(im,2); % sum in all rows. Size of result: [nRow, 1, nColNew*nChannel]
im = permute(im, [3,1,2,4]); % move singleton-dimension-2 to dimension-3; transpose image. Size of result: [nColNew*nChannel, nRow, 1]
im = reshape(im, nColNew*nChannel*nStack, redSize, nRowNew); % packets of width redSize, as columns next to each other
im = sum(im,2); % sum in all rows. Size of result: [nColNew*nChannel, 1, nRowNew]
im = permute(im, [3,1,2,4]); % move singleton-dimension-2 to dimension-3; transpose image back. Size of result: [nRowNew, nColNew*nChannel, 1]
im = reshape(im, nRowNew, nColNew, nChannel, nStack); % putting all channels (rgb) back behind each other in the third dimension
im = uint8(sqrt(im./redSize^2)); % mean; re-normalize brightness: "scale linear to the human eye"; back in uint8


function 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-image

fullMap = 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');        end
if nCol>nColFul;   warning('More colors than needed');  end

isPreDefCol = 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 list
end
[~,mapAdditional] = rgb2ind(imRGBcolumn(~isPreDefCol,:,:),nCol-nColStart,'nodither'); % create map of remaining colors
map = [startMap;mapAdditional];


function im = randSubsampling(im, imRed)
% Input:
%  Subsampling image. Colors are not mixed, no new colors are created.
%  im:      image, [imRows x imColumns x nChannel] (unit8)
%                      imRows, imColumns: must be divisible by redSize
%                      nChannel: usually 3 (RGB) or 1 (grey)
%  redSize: 2 = half the size (quarter of pixels)
%           3 = third the size (ninth of pixels)
%           ... and so on
%  Example: imRed=3 results in use of one      010 000 000 000 100 000 000
%           random specimen chosen from        000 000 001 010 000 000 001
%           the 3x3-origal pixel block         000 100 000 000 000 100 000   

[nRow,nCol,nChannel] = size(im);

index = reshape(1:nRow*nCol, nRow, nCol); % build index matrix using linear indexing
index = index(1:imRed:end,1:imRed:end);   % reduce size (this uses effectively the upper left corner of each sub-box)
[nRowTarget, nColTarget] = size(index);   % new size

randRowOffset =  randi([0 imRed-1],nRowTarget,nColTarget); % rand row-offset within pixel block
indexCheckLimRow = nRowTarget*imRed+randRowOffset(end,:) > nRow; % find creation of index out of original image size
randRowOffset(end,indexCheckLimRow) = 0; % avoid creation of index out of original image size

randColOffset =  randi([0 imRed-1],nRowTarget,nColTarget); % rand col-offset within pixel block
indexCheckLimCol = nColTarget*imRed+randColOffset(:,end) > nCol; % find creation of index out of original image size
randColOffset(indexCheckLimCol,end) = 0; % avoid creation of index out of original image size

index = index + randRowOffset + randColOffset*nRow; % create linear indexing that chooses random pixel in each pixel block

if nChannel == 3
    index = repmat(index,1,1,3);        % for each color channel
    index(:,:,2) = index(:,:,2) + nRow*nCol;   % for each color channel
    index(:,:,3) = index(:,:,3) + nRow*nCol*2; % for each color channel
end

im = im(index);

Licensing[edit]

I, the copyright holder of this work, hereby publish it under the following license:
Creative Commons CC-Zero This file is made available under the Creative Commons CC0 1.0 Universal Public Domain Dedication.
The person who associated a work with this deed has dedicated the work to the public domain by waiving all of their rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.

File history

Click on a date/time to view the file as it appeared at that time.

Date/TimeThumbnailDimensionsUserComment
current20:45, 7 August 2020Thumbnail for version as of 20:45, 7 August 2020203 × 945 (17.74 MB)Jahobr (talk | contribs)Uploaded own work with UploadWizard