function unnecessarily_complicated_gears()
% source code for drawing unnecessarily complicated gearing
% The shape of the gears is not precise, it creates a decent GIF and a SVG.
%
% 2017-06-20 Jahobr (update 2019-04-14 Jahobr)
teethSun = 48; %
teethPlan = 12; %
teethRing = teethSun+teethPlan*2;
nPlan = 12; % number of planets
teethSun_top = 24;
teethHub_spoked = 60;
teethHub_back = 24;
teethBigSpoke = 96;
teethRackTrans_Bottom = 25;
teethRackTrans_Top = 25/5*4;
teethRackDriver = 16;
module_Epicyclic = 1;
module_Hub_Spoke = 1.8;
module_Sun_Hub = 1.5;
module_LL = 0.8;
module_top = 0.9;
module_Rack = 1.4;
RGB.edge = [0 0 0 ]; % Edge color
RGB.bkgd = [1 1 1 ]; % white background
RGB.carrier = [0.1 0.7 0.1 ]; % green
RGB.sun = [0.95 0.65 0 ]; % yellow (obviously)
RGB.palnet = [0.2 0.2 1 ]; % blue (obviously)
RGB.ring = [1 0.2 0.2 ]; % red
RGB.hub = [0.6 0.2 0.8 ]; % violet
RGB.spoke = [0.5 0.5 0.5 ]; %
RGB.rackTrans = [0.4 0.24 0.08]; %
RGB.rack = [1 0.5 0.2 ]; %
RGB.rackDrive = [0.8 0.3 0.2 ]; %
RGB.LL_1 = [0.4 0.6 0.6 ]; %
RGB.LL_2 = [0.3 0.6 0.8 ]; %
RGB.top = [0.5 0.7 1 ]; %
RGB = structfun(@(q)round(q*255)/255, RGB, 'UniformOutput',false); % round to values that are nicely uint8 compatible
diameterSun = module_Epicyclic.*teethSun;
diameterPlan = module_Epicyclic.*teethPlan;
diameterCarr = diameterSun+diameterPlan;
dist_Sun_Hub = mean([module_Sun_Hub*teethSun_top, module_Sun_Hub*teethHub_spoked]);
centerHub = [dist_Sun_Hub*cosd(60) -dist_Sun_Hub*sind(60)];
dist_Hub_Spoke = mean([module_Hub_Spoke*teethHub_back, module_Hub_Spoke*teethBigSpoke]) ;
centerSpoke = [dist_Hub_Spoke*cosd(60) +dist_Hub_Spoke*sind(60)]+centerHub;
dist_Hub_RackTrans = mean([module_Sun_Hub*teethHub_spoked, module_Sun_Hub*teethRackTrans_Bottom]);
centerRackTrans = [dist_Hub_RackTrans*cosd(53) +dist_Hub_RackTrans*sind(53)]+centerHub;
dist_RackTrans_RGB.rackDriver = mean([module_Rack*teethRackTrans_Top, module_Rack*teethRackDriver ]);
centerRackDriver = [dist_RackTrans_RGB.rackDriver*cosd(55) -dist_RackTrans_RGB.rackDriver*sind(55)]+centerRackTrans;
[pathstr,fname] = fileparts(which(mfilename)); % save files under the same name and at file location
xLimits = [-20 100];
yLimits = [-60 40];
xRange = xLimits(2)-xLimits(1);
yRange = yLimits(2)-yLimits(1);
ySize = 700; % final gif size
xSize = floor(ySize/yRange*xRange); % pixel
screenSize = get(groot,'Screensize')-[0 0 5 20]; % [1 1 width height] (minus tolerance for figure borders)
scaleReduction = min(...% reduction for nice antialiasing (for >1 you need a 4K monitor or a virtural combination of several monitors using "Nvidia Surround" to fit the figure)
floor(screenSize(4)/ySize), floor(screenSize(3)/xSize));
linW = 2.5/1000 * (ySize*scaleReduction); % LineWidth
nFrames = 170;
figHandle = figure(15674455); clf
set(figHandle, 'Units','pixel');
set(figHandle, 'Position',[1 1 xSize*scaleReduction ySize*scaleReduction]); % big start image for antialiasing later [x y width height]
set(figHandle, 'GraphicsSmoothing','on') % requires at least version 2014b
set(figHandle,'MenuBar','none', 'ToolBar','none'); % free real estate for a maximally large image
set(figHandle, 'Color',RGB.bkgd); % white background
axesHandle = axes;
hold(axesHandle,'on');
axis off % invisible axes (no ticks)
axis equal;
setXYlim(axesHandle,xLimits,yLimits);
angleCarrier = -linspace(0,pi*2/nPlan,nFrames+1); % define gear position in frames
angleCarrier = angleCarrier(1:end-1); % remove last frame, it would be double
anglePlan = angleCarrier.*( teethSun/teethPlan+1 ); % gear ratio
anglePlan = -anglePlan + (pi/teethPlan); % ALLIGNMENT; THIS MAY NEED MANUAL ADJUSTMENT
angleSun = angleCarrier.* (1+teethRing/teethSun); % gear ratio
angleSun = angleSun + 0; % ALLIGNMENT; THIS MAY NEED MANUAL ADJUSTMENT
angleRing = zeros(size(anglePlan));
reducedRGBimage = uint8(ones(ySize,xSize,3,nFrames)); % allocate
for iFrame = 1:nFrames
cla(axesHandle); % fresh frame
%% background wheels
% Hub background wheel 24 teeth rotates 2, interacts with spoked
drawSpurWheel(centerHub,teethHub_back ,module_Hub_Spoke,... % center, number of teeth, module
RGB.hub*0.6, linW, RGB.edge,... % patch color, LineWidth, line color
-angleSun(iFrame)/2.5,... % angle of rotaion
NaN, 1, 3, 1); % nSpoke, spokeW, sideOffset, shaftDiameter
% fine Spoked wheel 96 teeth rotates 2
drawSpurWheel(centerSpoke,teethBigSpoke, module_Hub_Spoke,... % center, number of teeth, module
RGB.spoke, linW, RGB.edge,... % patch color, LineWidth, line color
angleSun(iFrame)/2.5/4 + pi/teethBigSpoke,... % angle of rotaion
teethBigSpoke/2, 0.5, 0, 13.2); % nSpoke, spokeW, sideOffset, shaftDiameter
%% %%%%%%%%%% Epicyclic_Gearing %%%%%%%%%%%%%%%%%%%%%
% ring
drawRingGear(teethRing,module_Epicyclic,RGB.ring,linW,RGB.edge,angleRing(iFrame))
% sun
drawSpurWheel([0 0],teethSun ,module_Epicyclic,... % center, number of teeth, module
RGB.sun, linW, RGB.edge,... % patch color, LineWidth, line color
angleSun(iFrame),... % angle of rotaion
NaN, 0, 0, 0); % nSpoke, spokeW, sideOffset, shaftDiameter
% planets
angPlan = linspace(0,2*pi,nPlan+1);
angPlan = angPlan(1:end-1);
for iPlan = angPlan
[X,Y] = pol2cart(iPlan+angleCarrier(iFrame) ,diameterCarr/2);
drawSpurWheel([X,Y],teethPlan,module_Epicyclic,... % center, number of teeth, module
RGB.palnet, linW, RGB.edge,... % patch color, LineWidth, line color
anglePlan(iFrame),... % angle of rotaion
NaN, 0, 0, 0); % nSpoke, spokeW, sideOffset, shaftDiameter
end
% carrier
angCarr = linspace(0,2*pi,nPlan+1);
[X,Y] = pol2cart([angCarr fliplr(angCarr)]+angleCarrier(iFrame) ,[ones(size(angCarr))*diameterCarr/2.07 ones(size(angCarr))* diameterCarr/1.93]);
patch(X,Y,RGB.carrier,'EdgeColor',RGB.edge,'LineWidth',linW) % full outer disc
for iPlan = angPlan
[X,Y] = pol2cart(iPlan+angleCarrier(iFrame) ,diameterCarr/2);
circlePatch(X,Y,diameterPlan*0.25,RGB.carrier,linW,RGB.edge);
circlePatch(X,Y,diameterPlan*0.15,RGB.palnet, linW,RGB.edge);
end
%% connection Epicyclic Gearing and hub
% 24 teeth, rotates 5 teeth
drawSpurWheel([0 0],teethSun_top ,module_Sun_Hub,... % center, number of teeth, module
1-(1-RGB.sun)*0.5, linW, RGB.edge,... % patch color, LineWidth, line color
angleSun(iFrame),... % angle of rotaion
NaN, 0, 0, 3); % nSpoke, spokeW, sideOffset, shaftDiameter
% spoked hub wheel: 60 teeth; rotates 5
drawSpurWheel(centerHub,teethHub_spoked ,module_Sun_Hub,... % center, number of teeth, module
RGB.hub, linW, RGB.edge,... % patch color, LineWidth, line color
-angleSun(iFrame)/2.5 +pi/teethHub_spoked,... % angle of rotaion
teethSun/2*2.5/5,... % 12 nSpoke,
1, 5.5, 7); % spokeW, sideOffset, shaftDiameter
%% chain of wheels on the Top
% on top big spoked (wheel 96 teeth rotates 2) 48 teeth rotates 1
drawSpurWheel(centerSpoke,48 ,module_top,... % center, number of teeth, module
1-(1-RGB.spoke)*0.7, linW, RGB.edge,... % patch color, LineWidth, line color
angleSun(iFrame)/2.5/4+0.2,... % angle of rotaion
NaN, 1, 0, 7); % nSpoke, spokeW, sideOffset, shaftDiameter
% top idler , meshing with small wheel big spoked; 17 teeth rotates 1
drawSpurWheel([57.35 31],17 ,module_top,... % center, number of teeth, module
RGB.top, linW, RGB.edge,... % patch color, LineWidth, line color
-angleSun(iFrame)/2.5/4 * 48/17 - 0.067,... % angle of rotaion
NaN, 1, 0, 3); % nSpoke, spokeW, sideOffset, shaftDiameter
% last on top, meshing with idler; 23 teeth rotates 1
drawSpurWheel([42.34 41],23 ,module_top,... % center, number of teeth, module
RGB.top*0.8, linW, RGB.edge,... % patch color, LineWidth, line color
angleSun(iFrame)/2.5/4 * 48/23 +0.12,... % angle of rotaion
NaN, 1, 0, 3); % nSpoke, spokeW, sideOffset, shaftDiameter
%% Rack
% Rack-Transmission; meshes in Hub-spoked 25 teeth rotates 5
drawSpurWheel(centerRackTrans,teethRackTrans_Bottom,module_Sun_Hub,... % center, number of teeth, module
RGB.rackTrans, linW, RGB.edge,... % patch color, LineWidth, line color
angleSun(iFrame)/teethRackTrans_Bottom*teethSun_top +0.003,... % angle of rotaion
NaN, 1, 0, 0); % nSpoke, spokeW, sideOffset, shaftDiameter
% Rack-Transmission; meshes in Rack-driver; 15 teeth rotates 3
drawSpurWheel(centerRackTrans,teethRackTrans_Top,module_Rack,... % center, number of teeth, module
1-(1-RGB.rackTrans)*0.7,linW, RGB.edge,... % patch color, LineWidth, line color
angleSun(iFrame)/teethRackTrans_Bottom*teethSun_top +0.157,... % angle of rotaion
NaN, 1, 0, 2.5); % nSpoke, spokeW, sideOffset, shaftDiameter
% Rack-driver; idler between Rack-Transmission and Rack
drawSpurWheel(centerRackDriver,teethRackDriver ,module_Rack,... % center, number of teeth, module
RGB.rackDrive, linW, RGB.edge,... % patch color, LineWidth, line color
-angleSun(iFrame)/teethRackTrans_Bottom*teethSun_top *15/12 +pi/teethRackDriver ,... % angle of rotaion
NaN, 0.5, 0, 2); % nSpoke, spokeW, sideOffset, shaftDiameter
drawRack([centerRackDriver(1)+module_Rack*teethRackDriver/2 centerRackDriver(2)],50,module_Rack,...
RGB.rack,linW,RGB.edge,... % patch color, LineWidth, line color
-angleSun(iFrame)/teethRackTrans_Bottom*teethSun_top *module_Rack/2*teethRackTrans_Top)
%% lower left
% on top of hub; rotates 3 teeth
drawSpurWheel(centerHub,36 ,module_LL,... % center, number of teeth, module
1-(1-RGB.hub)*0.9, linW, RGB.edge,... % patch color, LineWidth, line color
-angleSun(iFrame)/2.5,... % angle of rotaion
NaN, 1, 3, 9); % nSpoke, spokeW, sideOffset, shaftDiameter
% next to hub; lower left trasmission; bottom wheel; rotates 3 teeth
drawSpurWheel([centerHub(1)-28.8 centerHub(2)],36 ,module_LL,... % center, number of teeth, module
RGB.LL_1*0.5, linW, RGB.edge,... % patch color, LineWidth, line color
angleSun(iFrame)/2.5 +pi/36,... % angle of rotaion
NaN, 1, 0, 1); % nSpoke, spokeW, sideOffset, shaftDiameter
% lower left trasmission; top wheel; rotates 2 teeth
drawSpurWheel([centerHub(1)-28.8 centerHub(2)],24 ,module_LL,... % center, number of teeth, module
RGB.LL_1, linW, RGB.edge,... % patch color, LineWidth, line color
angleSun(iFrame)/2.5 ,... % angle of rotaion
NaN, 1, 0, 4); % nSpoke, spokeW, sideOffset, shaftDiameter
% lower left wheel; rotates 2 teeth; rotates 1 spoke
drawSpurWheel([centerHub(1)-57.6 centerHub(2)],48 ,module_LL,... % center, number of teeth, module
RGB.LL_2, linW, RGB.edge,... % patch color, LineWidth, line color
-angleSun(iFrame)/2.5 /2 +pi/48,... % angle of rotaion
24, 1, 0, 10); % nSpoke, spokeW, sideOffset, shaftDiameter
%% save animation
setXYlim(axesHandle,xLimits,yLimits);
f = getframe(figHandle);
reducedRGBimage(:,:,:,iFrame) = imReduceSize(f.cdata,scaleReduction); % the size reduction: adds antialiasing
% if iFrame == 10 % SVG
% if ~isempty(which('plot2svg'))
% plot2svg(fullfile(pathstr, [fname '_a.svg']),figHandle) % by Juerg Schwizer
% else
% disp('plot2svg.m not available; see http://www.zhinst.com/blogs/schwizer/');
% end
% end
end
map = createImMap(reducedRGBimage,128,struct2map(RGB)); % colormap
im = uint8(ones(ySize,xSize,1,nFrames)); % allocate
for iFrame = 1:nFrames
im(:,:,1,iFrame) = rgb2ind(reducedRGBimage(:,:,:,iFrame),map,'nodither');
end
imwrite(im,map,fullfile(pathstr, [fname '_a.gif']),'DelayTime',1/25,'LoopCount',inf) % save gif
disp([fname '.gif has ' num2str(numel(im)/10^6 ,4) ' Megapixels']) % Category:Animated GIF files exceeding the 100 MP limit
function drawSpurWheel(center,toothNumber,module,colFilling,linW,linC,startOffset,nSpoke,spokeW,sideOffset,shaftDia)
% DRAWSPURWHEEL - draw a simple Toothed Wheel
% center: [x y]
% toothNumber: scalar
% module: scalar tooth "size"
% colFilling: color of filling [r g b]
% linW: LineWidth (scalar)
% linC: LineColor
% startOffset: start rotation (scalar)[rad]
% nSpoke: number of spokes; NaN for filled wheel (scalar integer)
% spokeW: spoke width [modules]
% sideOffset: spoke side offset [modules]
% shaftDia: inner shaft diameter [modules]
effectiveRadius = module*toothNumber/2; % effective effectiveRadius
outsideRadius = effectiveRadius+1* module; % +---+ +---+
upperRisingRadius = effectiveRadius+0.5*module; % / \ / \
% effective Radius % / \ / \
lowerRisingRadius = effectiveRadius-0.5*module; % I I I I
rootRadius = effectiveRadius-1.1*module; % + - - - + + - - - + +
angleBetweenTeeth = 2*pi/toothNumber; % angle between 2 teeth
angleOffPoints = (0:angleBetweenTeeth/16:(2*pi));
angleOffPoints = angleOffPoints+startOffset; % apply rotation offset
angleOffPoints(7:16:end) = angleOffPoints(7:16:end) + 1/toothNumber^1.2; % hack to create smaller tooth tip
angleOffPoints(11:16:end) = angleOffPoints(11:16:end) - 1/toothNumber^1.2; % hack to create smaller tooth tip
angleOffPoints(8:16:end) = (angleOffPoints(7:16:end) + angleOffPoints(9:16:end))/2; % shift the neighbouring tip point in accordingly
angleOffPoints(10:16:end) = (angleOffPoints(11:16:end) + angleOffPoints(9:16:end))/2; % shift the neighbouring tip point in accordingly
angleOffPoints(6:16:end) = angleOffPoints(6:16:end) + 1/toothNumber^1.7; % hack to create slender upperRisingRadius
angleOffPoints(12:16:end) = angleOffPoints(12:16:end) - 1/toothNumber^1.7; % hack to create slender upperRisingRadius
radiusOffPoints = angleOffPoints; % allocate with correct site
radiusOffPoints( 1:16:end) = rootRadius; % center bottom I
radiusOffPoints( 2:16:end) = rootRadius; % left bottom I
radiusOffPoints( 3:16:end) = rootRadius; % left bottom corner +
radiusOffPoints( 4:16:end) = lowerRisingRadius; % lower rising bottom \
radiusOffPoints( 5:16:end) = effectiveRadius; % rising edge \
radiusOffPoints( 6:16:end) = upperRisingRadius; % upper rising edge \
radiusOffPoints( 7:16:end) = outsideRadius; % right top corner +
radiusOffPoints( 8:16:end) = outsideRadius; % right top I
radiusOffPoints( 9:16:end) = outsideRadius; % center top I
radiusOffPoints(10:16:end) = outsideRadius; % left top I
radiusOffPoints(11:16:end) = outsideRadius; % left top corner +
radiusOffPoints(12:16:end) = upperRisingRadius; % upper falling edge /
radiusOffPoints(13:16:end) = effectiveRadius; % falling edge /
radiusOffPoints(14:16:end) = lowerRisingRadius; % lower falling edge /
radiusOffPoints(15:16:end) = rootRadius; % right bottom corner +
radiusOffPoints(16:16:end) = rootRadius; % right bottom I
[X,Y] = pol2cart(angleOffPoints,radiusOffPoints);
X = X+center(1); % center offset
Y = Y+center(2); % center offset
if ~isnan(nSpoke)
for iSpoke = 1:nSpoke
Xs = ([-1 1 1 -1]+sideOffset)*module*spokeW;
Ys = [ 1 1 0 0]*(rootRadius-1*module);
[Xs,Ys] = rotateCordiantes(Xs,Ys,2*pi/nSpoke*iSpoke+startOffset);% apply rotation
Xs = Xs+center(1); % center offset
Ys = Ys+center(2); % center offset
patch(Xs,Ys,colFilling*0.85,'EdgeColor',linC,'LineWidth',linW)
end
[Xc,Yc] = pol2cart(angleOffPoints,effectiveRadius-3*module);
Xc = Xc+center(1); % center offset
Yc = Yc+center(2); % center offset
[Xc,Yc] = poly2cw(Xc,Yc);
[X,Y] = poly2cw(X,Y);
[Xb,Yb] = polybool('subtraction', X,Y,Xc,Yc);
Xb = Xb(~isnan(Xb)); % notNaN
Yb = Yb(~isnan(Yb)); % notNaN
patch(Xb,Yb,colFilling,'EdgeColor','none')
plot(X, Y, 'LineWidth',linW,'Color',linC); % extra line
plot(Xc,Yc,'LineWidth',linW,'Color',linC); % extra line
else % filled
patch(X,Y,colFilling,'EdgeColor',linC,'LineWidth',linW)
end
if and(~isnan(shaftDia),shaftDia>0)
drawCirclePatch(center(1),center(2),shaftDia*module,1-(1-colFilling)*0.85,linW,linC);
end
% % effective Radius
% [X,Y] = pol2cart(angleOffPoints,effectiveRadius);
% X = X+center(1); % center offset
% Y = Y+center(2); % center offset
% plot(X,Y,'-.','Color',RGB.edge;
function h = circlePatch(x,y,r,col,linW,linC)
% x and y: coordinates of the center
% r: is the radius of the circle
% fillC: color of filling [r g b]
% linW: LineWidth
% linC: LineColor
angleOffPoints = linspace(0,2*pi,200);
xc = x + r*cos(angleOffPoints);
yc = y + r*sin(angleOffPoints);
% h = plot(xc,yc,'color',col,'LineWidth',linW,'LineStyle',sty);
h = patch(xc,yc,col,'EdgeColor',linC,'LineWidth',linW);
function [x,y] = rotateCordiantes(x,y,anglee)
% x and y: coordinates of the center
% anglee: angle of rotation in [rad]
rotM = [cos(anglee) -sin(anglee); sin(anglee) cos(anglee)];
x_y = rotM*[x(:)';y(:)'];
x = x_y(1,:);
y = x_y(2,:);
function drawRack(center,toothNumber,module,fillC,linW,linC,startOffset)
% x and y: coordinates of the center
% toothNumber: number of teeth
% module: scalar tooth "size"
% fillC: color of filling [r g b]
% linW: LineWidth
% linC: LineColor
% startOffset: initial shift
y = (0:toothNumber*4-1)*pi*module/4;
y = y-mean(y)+center(2)+startOffset;
x = ones(size(y))*center(1);
x(1:4:end) = x(1:4:end)+1.1*module; % +###I bottom
x(2:4:end) = x(2:4:end)-1 *module; % +######I tip
x(3:4:end) = x(3:4:end)-1 *module; % +######I tip
x(4:4:end) = x(4:4:end)+1.1*module; % +###I bottom
y(1:4:end) = y(1:4:end)-0.14*module; % bottom smaller
y(2:4:end) = y(2:4:end)+0.14*module; % tip smaller
y(3:4:end) = y(3:4:end)-0.14*module; % tip smaller
y(4:4:end) = y(4:4:end)+0.14*module; % bottom smaller
y = [y(1) y y(end)];
x = [x(1)+5*module x x(end)+5*module];
patch(x,y,fillC,'EdgeColor',linC,'LineWidth',linW);
function drawCirclePatch(x,y,r,fillC,linW,linC)
% x and y: coordinates of the center
% r: is the radius of the circle
% fillC: color of filling [r g b]
% linW: LineWidth
% linC: LineColor
% startOffset: start rotation (scalar)[rad]
angleOffPoints = linspace(0,2.001*pi,200);
xc = x + r*cos(angleOffPoints);
yc = y + r*sin(angleOffPoints);
patch(xc,yc,fillC,'EdgeColor',linC,'LineWidth',linW);
function drawRingGear(toothNumber,module,fillC,linW,linC,startOffset)
% DRAWRINGGEAR - draw a outer gear
%
% Input:
% center: [x y]
% toothNumber: scalar
% module: scalar tooth "size"
% fillC: color of filling [r g b]
% linW: LineWidth
% linC: LineColor
% startOffset: start rotation (scalar)[rad]
effectiveRadius = module*toothNumber/2; % effective effectiveRadius
outsideRadius = effectiveRadius-1* module; % +---+ +---+
upperRisingRadius = effectiveRadius-0.5*module; % / \ / \
% effective Radius % / \ / \
lowerRisingRadius = effectiveRadius+0.5*module; % I I I I
rootRadius = effectiveRadius+1.1*module; % + - - - + + - - - + +
angleBetweenTeeth = 2*pi/toothNumber; % angle between 2 teeth
angleOffPoints = (0:angleBetweenTeeth/16:(2*pi));
angleOffPoints = angleOffPoints+startOffset; % apply rotation offset
%% outerEdge
maxRadius = rootRadius*1.2; % definition of outer line
[Xout,Yout] = pol2cart(angleOffPoints,maxRadius);
%% inner teeth
radiusOffPoints = angleOffPoints; % init
angleOffPoints( 7:16:end) = angleOffPoints(7:16:end) + 1/toothNumber^1.2; % hack to create smaller tooth tip
angleOffPoints(11:16:end) = angleOffPoints(11:16:end) - 1/toothNumber^1.2; % hack to create smaller tooth tip
angleOffPoints( 8:16:end) = (angleOffPoints(7:16:end) + angleOffPoints(9:16:end))/2; % shift the neighbouring tip point in accordingly
angleOffPoints(10:16:end) = (angleOffPoints(11:16:end) + angleOffPoints(9:16:end))/2; % shift the neighbouring tip point in accordingly
angleOffPoints( 6:16:end) = angleOffPoints(6:16:end) + 1/toothNumber^1.7; % hack to create slender tooth
angleOffPoints(12:16:end) = angleOffPoints(12:16:end) - 1/toothNumber^1.7; % hack to create slender tooth
radiusOffPoints( 1:16:end) = rootRadius; % center bottom I
radiusOffPoints( 2:16:end) = rootRadius; % left bottom I
radiusOffPoints( 3:16:end) = rootRadius; % left bottom corner +
radiusOffPoints( 4:16:end) = lowerRisingRadius; % lower rising bottom \
radiusOffPoints( 5:16:end) = effectiveRadius; % rising edge \
radiusOffPoints( 6:16:end) = upperRisingRadius; % upper rising edge \
radiusOffPoints( 7:16:end) = outsideRadius; % right top corner +
radiusOffPoints( 8:16:end) = outsideRadius; % right top I
radiusOffPoints( 9:16:end) = outsideRadius; % center top I
radiusOffPoints(10:16:end) = outsideRadius; % left top I
radiusOffPoints(11:16:end) = outsideRadius; % left top corner +
radiusOffPoints(12:16:end) = upperRisingRadius; % upper falling edge /
radiusOffPoints(13:16:end) = effectiveRadius; % falling edge /
radiusOffPoints(14:16:end) = lowerRisingRadius; % lower falling edge /
radiusOffPoints(15:16:end) = rootRadius; % right bottom corner +
radiusOffPoints(16:16:end) = rootRadius; % right bottom I
[X,Y] = pol2cart(angleOffPoints,radiusOffPoints);
[Xout,Yout] = poly2cw(Xout,Yout);
[X, Y ] = poly2cw(X ,Y );
[Xb,Yb] = polybool('subtraction',Xout,Yout, X,Y);
Xb = Xb(~isnan(Xb)); % notNaN
Yb = Yb(~isnan(Yb)); % notNaN
patch(Xb,Yb,fillC,'EdgeColor','none')
plot(X, Y, 'LineWidth',linW,'Color',linC); % draw teeth outline
plot(Xout,Yout,'LineWidth',linW,'Color',linC); % draw outer circle
function map = struct2map(RGB)
% RGB: struct of depth 1 with [r g b] in each field
fNames = fieldnames(RGB);
nNames = numel(fNames);
map = NaN(nNames,3); % allocate
for iName = 1:nNames
map(iName,:) = RGB.(fNames{iName}); %
end
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 setXYlim(axesHandle,xLimits,yLimits)
% set limits; practically the axis overhangs the figure all around, to
% hide rendering errors at line-ends.
% Input:
% axesHandle:
% xLimits, yLimits: [min max]
overh = 0.05; % 5% overhang all around; 10% bigger in x and y
xlim([+xLimits(1)*(1+overh)-xLimits(2)*overh -xLimits(1)*overh+xLimits(2)*(1+overh)])
ylim([+yLimits(1)*(1+overh)-yLimits(2)*overh -yLimits(1)*overh+yLimits(2)*(1+overh)])
set(axesHandle,'Position',[-overh -overh 1+2*overh 1+2*overh]); % stretch axis as bigger as figure, [x y width height]
drawnow;