File:DeltoidLinkage1 Centrodes.gif

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

DeltoidLinkage1_Centrodes.gif(357 × 357 pixels, file size: 2.76 MB, MIME type: image/gif, looped, 400 frames, 16 s)

Captions

Captions

Add a one-line explanation of what this file represents

Summary[edit]

Description
Deutsch: Koppelgetriebe:
English: Four-bar linkage, Deltoid Linkage :
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 DeltoidLinkage1()
% source code that produces a GIF and a SVG
% Two neighbouring links have the same length (here: base=left; ceter=right)
%
% 2019-02-04 Jahobr


xLeftBearing = -0.5;
yLeftBearing = 0;
xRightBearing = 0.5;
yRightBearing = yLeftBearing; % on horizontal line
leftBar = xRightBearing-xLeftBearing; % must to be a DeltoidLinkage
rightBar = leftBar*3; % bigger
centerBar = rightBar; % must to ben a Deltoid Linkage

RGB.bkgd = [1   1   1  ]; % white background
RGB.edge = [0   0   0  ]; % Edge color
RGB.bars = [0.3 0.3 0.3]; % grey
RGB.icro = [1   1   0.1]; % yellow % Instant center of rotation
RGB.fixC = [0.2 0.2 1  ]; % blue   % fixed Centrode
RGB.movC = [0.1 0.7 0.1]; % green  % moving Centrode
RGB.RofM = [1   0   1  ]; % magenta% range of movment
RGB.sVec = [1   0   0  ]; % red    % speed Vector

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

nFrames = 400;
startFrame = round(0.25*nFrames); % get a nice first frame with everything visible
intermediatePoints = 1; % extra points between to smooth lines
nPos = nFrames*intermediatePoints+1; % number of positions that have to be calculated
anglesLeft = linspace(1/3*pi,13/3*pi,nPos); % define movemet of left bar

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

%% allocate memory
xRightJoint           = Inf(1,nPos); yRightJoint           = Inf(1,nPos);
xInstCentRot          = Inf(1,nPos); yInstCentRot          = Inf(1,nPos);
angleCenterBar        = Inf(1,nPos);
xInstCentRot_MovCoord = Inf(1,nPos); yInstCentRot_MovCoord = Inf(1,nPos);

%% calculate geometric values
xLeftJoint = xLeftBearing+cos(anglesLeft)*leftBar; % left bar end point
yLeftJoint = yLeftBearing-sin(anglesLeft)*leftBar; % left bar end point

for iPos = 1:nPos % calculate geometric values for all positions
    
    [xout,yout] = circcirc(xLeftJoint(iPos),yLeftJoint(iPos),centerBar,xRightBearing,yRightBearing,rightBar); % Intersections of circles in Cartesian plane
    if iPos==1
        xRightJoint(iPos) = xout(1); % select inital configuration
        yRightJoint(iPos) = yout(1); % select inital configuration
    else % predict based on last move, % next neighbour based on prediction
        if iPos==2 % next neighbour
            xPred = xRightJoint(iPos-1); % 1 old point is not enough for a prediction; just use last point
            yPred = yRightJoint(iPos-1); % 1 old point is not enough for a prediction; just use last point
        else % make prediction
            xPred = 2*xRightJoint(iPos-1)-xRightJoint(iPos-2); % last point + last delta;(iPos-1)-(iPos-2)
            yPred = 2*yRightJoint(iPos-1)-yRightJoint(iPos-2); % last point + last delta;(iPos-1)-(iPos-2)
        end
        dist1 = norm([xPred-xout(1) yPred-yout(1)]); % distance to prediction
        dist2 = norm([xPred-xout(2) yPred-yout(2)]); % distance to prediction
        if ~isnan(xout(1)) % normal case; calculation worked
            if dist1 < dist2 % choose neighbour
                xRightJoint(iPos) = xout(1);
                yRightJoint(iPos) = yout(1);
            else
                xRightJoint(iPos) = xout(2);
                yRightJoint(iPos) = yout(2);
            end
            
        else % if error; this happens on extreme endpoints of movements
            xRightJoint(iPos) = xPred; % failsafe hack; not nice but it does the job
            yRightJoint(iPos) = yPred; % failsafe hack; not nice but it does the job
        end
    end
    
    % calculate intersection to dermine instant center of rotation
    [xInstCentRot(iPos),yInstCentRot(iPos)] = intersectionOfLines(...
        xLeftBearing,     yLeftBearing,...     % Point1 Line1
        xLeftJoint(iPos), yLeftJoint(iPos),... % Point2 Line1
        xRightBearing,    yRightBearing,...    % Point1 Line2
        xRightJoint(iPos),yRightJoint(iPos));  % Point2 Line2
    
    %% instant center of rotation in center bar coordinates
    angleCenterBar(iPos) = atan2((yRightJoint(iPos) - yLeftJoint(iPos)) , (xRightJoint(iPos) - xLeftJoint(iPos)));
    rotM = [cos(-angleCenterBar(iPos)) -sin(-angleCenterBar(iPos)); sin(-angleCenterBar(iPos)) cos(-angleCenterBar(iPos))];
    vecTemp = rotM*[xInstCentRot(iPos)-xLeftJoint(iPos);  yInstCentRot(iPos)-yLeftJoint(iPos)];
    xInstCentRot_MovCoord(iPos) = vecTemp(1);
    yInstCentRot_MovCoord(iPos) = vecTemp(2);
end

%% create figure
figHandle = figure(15674455);
clf
axesHandle = axes;
axis equal
axis off % invisible axes (no ticks)
drawnow;
hold(axesHandle,'on')
set(figHandle,'Units'  ,'pixel');
set(figHandle,'Color'  ,RGB.bkgd); % white background 
set(figHandle,'MenuBar','none',  'ToolBar','none'); % free real estate for a maximally large image

%% plot loop
for currentCase = 1:5
    
    switch currentCase
        case 1 %
            saveName = [fname '_speedVector'];
        case 2 %
            saveName = [fname '_InstantCenterRotation']; % Momentanpol
        case 3 %
            saveName = [fname '_fixedCentrode']; % Rastpolbahn
        case 4 %
            saveName = [fname '_movingCentrode']; % Gangpolbahn
        case 5 %
            saveName = [fname '_Centrodes'];
    end
    
    xLimits = [-4.3 3.9];
    yLimits = [-4.1 4.1];
    
    xRange = xLimits(2)-xLimits(1);
    yRange = yLimits(2)-yLimits(1);
    
    screenSize = get(groot,'Screensize')-[0 0 5 20]; % [1 1 width height] (minus tolerance for figure borders)
    screenAspectRatio = screenSize(3)/screenSize(4); % width/height
    imageAspectRatio = xRange/yRange;
    MegaPixelTarget = 51*10^6; % Category:Animated GIF files exceeding the 50 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
%     if imageAspectRatio > screenAspectRatio % width will be the problem
%         scaleReduction = floor(screenSize(3)/xSize); % repeat as often as possible
%     else % height will be the problem
%         scaleReduction = floor(screenSize(4)/ySize); % repeat as often as possible
%     end
    scaleReduction = 2; % not auto mode. (Line width is not programmed adaptive! I want all verions to look similar)

    reducedRGBimage = uint8(ones(ySize,xSize,3,nFrames)); % allocate
    
    iFrame = 0;
    for iPos = 2:intermediatePoints:nPos % leave out first frame, it would be double
                    
        switch currentCase
            case 1 %
                saveName = [fname '_speedVector'];
            case 2 %
                saveName = [fname '_InstantCenterRotation']; % Momentanpol
            case 3 % 
                saveName = [fname '_fixedCentrode']; % Rastpolbahn
            case 4 % 
                saveName = [fname '_movingCentrode']; % Gangpolbahn
            case 5 %
                saveName = [fname '_Centrodes'];
        end
        iFrame = iFrame+1;
        cla(axesHandle) % fresh frame
        
        sizze = 0.45;
        bearing(xLeftBearing ,yLeftBearing ,sizze,RGB.edge)
        bearing(xRightBearing,yRightBearing,sizze,RGB.edge)
        
        if currentCase~=1
            plot([xLeftJoint(iPos)  xInstCentRot(iPos)], [yLeftJoint(iPos)  yInstCentRot(iPos)],'--','LineWidth',2.5,'Color',RGB.bars) % line to intersection
            plot([xRightJoint(iPos) xInstCentRot(iPos)], [yRightJoint(iPos) yInstCentRot(iPos)],'--','LineWidth',2.5,'Color',RGB.bars) % line to intersection
        end
        
        plot(xLeftJoint, yLeftJoint, '--','LineWidth',2.5,'Color',RGB.RofM) % range of movment
        plot(xRightJoint,yRightJoint,'--','LineWidth',2.5,'Color',RGB.RofM) % range of movment
        
        xBeams = [xLeftBearing xLeftJoint(iPos) xRightJoint(iPos) xRightBearing];
        yBeams = [yLeftBearing yLeftJoint(iPos) yRightJoint(iPos) yRightBearing];
        plot(xBeams,yBeams,'-','MarkerSize',15,'LineWidth',8,'Color',RGB.bars); % all bars
        plot(xBeams(2:3),yBeams(2:3),'-','MarkerSize',15,'LineWidth',12,'Color',RGB.edge); % main bar
        
        if or( currentCase==3 , currentCase==5 ) % fixed Centrode
            plot(xInstCentRot,yInstCentRot,'-','LineWidth',4,'Color',RGB.fixC);
        end
        
        if or( currentCase==4 , currentCase==5 ) % moving Centrode
            rotM = [cos(angleCenterBar(iPos)) -sin(angleCenterBar(iPos)); sin(angleCenterBar(iPos)) cos(angleCenterBar(iPos))];
            vecTemp = rotM*[xInstCentRot_MovCoord; yInstCentRot_MovCoord];
            plot(vecTemp(1,:)+xLeftJoint(iPos),vecTemp(2,:)+yLeftJoint(iPos),'-','LineWidth',4,'Color',RGB.movC);
        end
        
        plot(xBeams,yBeams,'o','LineWidth',3,'MarkerEdgeColor',RGB.edge,'MarkerFaceColor',RGB.bkgd,'MarkerSize',12); % joints
        
        diffVecLeft  = [xLeftJoint(iPos), yLeftJoint(iPos)]  - [xLeftJoint(iPos-1), yLeftJoint(iPos-1)];  % lazy way to approximate the speed vector
        diffVecRight = [xRightJoint(iPos),yRightJoint(iPos)] - [xRightJoint(iPos-1),yRightJoint(iPos-1)]; % lazy way to approximate the speed vector
        
        speedScale = intermediatePoints*15;
        
        line(xLeftJoint(iPos)+[0 diffVecLeft(1)]*speedScale, yLeftJoint(iPos)+[0 diffVecLeft(2)]*speedScale,    'Color',RGB.sVec,'LineWidth',4) % speed Vector
        plot(xLeftJoint(iPos)+   diffVecLeft(1) *speedScale, yLeftJoint(iPos)+   diffVecLeft(2) *speedScale,'.','Color',RGB.sVec,'LineWidth',4,'MarkerSize',20) % speed Vector end marker
        
        line(xRightJoint(iPos)+[0 diffVecRight(1)]*speedScale,yRightJoint(iPos)+[0 diffVecRight(2)]*speedScale,    'Color',RGB.sVec,'LineWidth',4) % speed Vector
        plot(xRightJoint(iPos)+   diffVecRight(1)*speedScale, yRightJoint(iPos)+   diffVecRight(2) *speedScale,'.','Color',RGB.sVec,'LineWidth',4,'MarkerSize',20) % speed Vector end marker
        
        if currentCase~=1
            plot(xInstCentRot(iPos),yInstCentRot(iPos),'o','LineWidth',2,'MarkerEdgeColor',RGB.edge,'MarkerFaceColor',RGB.icro,'MarkerSize',14); % Instant center of rotation
            plot(xInstCentRot(iPos),yInstCentRot(iPos),'.','MarkerSize',5,'Color',RGB.edge); % Instant center of rotation
        end
        
        %% resize figure
        axis equal;
        set(axesHandle,'Position',[0 0 1 1]); % stretch axis bigger as figure, to have nice line ends [x y width height]
        set(figHandle, 'Position',[1 1 xSize*scaleReduction ySize*scaleReduction]); % big start image for antialiasing later [x y width height]
        xlim(xLimits); ylim(yLimits);    
        drawnow;
        pause(0.005)     
        
        %% save SVG
        if iFrame == startFrame
            if ~isempty(which('plot2svg'))
                plot2svg(fullfile(pathstr, [saveName '.svg']),figHandle) % by Juerg Schwizer
            else
                disp('plot2svg.m not available; see http://www.zhinst.com/blogs/schwizer/');
            end
        end
        
        %% save animation
        f = getframe(figHandle);
        reducedRGBimage(:,:,:,iFrame) = imReduceSize(f.cdata,scaleReduction); % the size reduction: adds antialiasing
    end
    
    reducedRGBimage = circshift(reducedRGBimage,1-startFrame,4); % shift animation do get nice start frame
    
    switch currentCase
            case 1 %
                map = createImMap(reducedRGBimage,32,[RGB.bkgd;RGB.edge;RGB.sVec;RGB.bars;RGB.RofM]); % only speed Vector
            case 2 %
                map = createImMap(reducedRGBimage,32,[RGB.bkgd;RGB.edge;RGB.sVec;RGB.bars;RGB.RofM;RGB.icro]); % Instant Center Rotation
            case 3 % 
                map = createImMap(reducedRGBimage,64,[RGB.bkgd;RGB.edge;RGB.sVec;RGB.bars;RGB.RofM;RGB.icro;RGB.fixC]); % fixed Centrode
            case 4 % 
                map = createImMap(reducedRGBimage,64,[RGB.bkgd;RGB.edge;RGB.sVec;RGB.bars;RGB.RofM;RGB.icro;         RGB.movC]); % moving Centrode
            case 5 %
                map = createImMap(reducedRGBimage,64,[RGB.bkgd;RGB.edge;RGB.sVec;RGB.bars;RGB.RofM;RGB.icro;RGB.fixC;RGB.movC]); % both Centrodes
    end
    
    im = uint8(ones(ySize,xSize,1,nFrames)); % allocate
    for iFrame = 1:nFrames
        im(:,:,1,iFrame) = rgb2ind(reducedRGBimage(:,:,:,iFrame),map,'nodither'); % rgb to colormap image
    end
    
    imwrite(im,map,fullfile(pathstr, [saveName '.gif']),'DelayTime',1/25,'LoopCount',inf) % save gif
    disp([saveName '.gif  has ' num2str(numel(im)/10^6 ,4) ' Megapixels']) % Category:Animated GIF files exceeding the 50 MP limit
end

if ispc; dos(['explorer ' pathstr]); end % open folder with files in it
return

function bearing(x,y,sizze,col)
% x coordinates of the center
% y coordinates of the center
% size
plot([0 -0.5 0.5 0]*sizze+x,[0 -0.8660 -0.8660 0]*sizze+y,'k','LineWidth',3,'Color',col); % Triangle %  0.8660 = sqrt(3)*0.5
plot([-0.7 0.7]*sizze+x,[-0.87 -0.87]*sizze+y,'k','LineWidth',3,'Color',col); % base line
for iLine = -0.6:0.2:0.7
    plot(([-0.1 0.1]+iLine)*sizze+x,[-1.07 -0.87]*sizze+y,'k','LineWidth',2,'Color',col); % Hatching
end

function [xs,ys] = intersectionOfLines(x1,y1, x2,y2, x3,y3, x4,y4)
% Point1_Line1 % Point2_Line1 % Point1_Line2 % Point2_Line2
xs = ((x4-x3)*(x2*y1-x1*y2) - (x2-x1)*(x4*y3-x3*y4)) / ((y4-y3)*(x2-x1) - (y2-y1)*(x4-x3));
ys = ((y1-y2)*(x4*y3-x3*y4) - (y3-y4)*(x2*y1-x1*y2)) / ((y4-y3)*(x2-x1) - (y2-y1)*(x4-x3));

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];

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:36, 4 February 2019Thumbnail for version as of 20:36, 4 February 2019357 × 357 (2.76 MB)Jahobr (talk | contribs)User created page with UploadWizard