% Author: David Sabin-Miller % Nov 24, 2024 function Mandelbrot_Julia() close all; % fun parameters c to try! % try -0.75 + 0.165i ("mandala") % try -0.74835 + 0.0843 i ("seahorse") % try -0.745 + 0.0834 i (just outside M, 0 diverges) % try -0.744 + 0.0834 i (inside M, 0 doesn't diverge) % try 0.25 + 0.5 i (exact root of 4-bulb, indifferent) % try 0 + i (on boundary, tip of antenna!) %set initial parameter c (can change this later in the gui) S.cRe = 0; S.cIm = 0; S.zRe = 0; S.zIm = 0; set(0,'units','centimeters'); cm_SS = get(0,'ScreenSize'); screenW = cm_SS(3); screenH = cm_SS(4); W = 0.95*screenW; H = 0.76*screenH; S.fig = figure('units','centimeters','position',[1, 1, W, H]); % MandelAxis location x01 = 3; %3; y01 = 2; %1; w1 = 0.45*W; %28 h1 = 3/3.5*w1; S.MandelAxis = subplot(1,2,1); set(S.MandelAxis, 'units','centimeters','position',[x01,y01,w1,h1]); S.x_minM = -2.5; S.x_maxM = 1; S.y_minM = -1.5; S.y_maxM = 1.5; S.dxM = 0.005; S.x_gridM = S.x_minM : S.dxM : S.x_maxM; S.y_gridM = (S.y_minM : S.dxM : S.y_maxM)'; S.x_NM = length(S.x_gridM); S.y_NM = length(S.y_gridM); S.max_iterM = 100; % draw the Mandelbrot Set background drawMandelbrot(S); title("The Mandelbrot Set",'Interpreter','latex','FontSize',18); xlabel('Re(c)','Interpreter','latex','fontsize',14); ylabel('Im(c)','Interpreter','latex','fontsize',14) % Juliaxis location x02 = 0.55*W; y02 = y01; w2 = h1; %24; h2 = w2; %24; %this will also change to make it square S.cReEditBox = uicontrol('style','edit',... 'unit','centimeters',... 'position',[x01+4, y01+0.95*h1 4 0.8],... 'String', num2str(S.cRe),... 'FontSize',14,... 'callback', {@editCB, 'cRe'}); S.cReText = annotation('textbox','units','centimeters'); set(S.cReText,'Position',[x01+0.5 y01+0.95*h1 4 .8],'String',"Re(c)",... 'fontsize',14,'interpreter','latex','HorizontalAlignment','center',... 'BackgroundColor',[0.9,0.9,0.9],'VerticalAlignment','middle','FitBoxToText','on'); S.cImEditBox = uicontrol('style','edit',... 'unit','centimeters',... 'position',[x01+4, y01+.9*h1 4 0.8],... 'String', num2str(S.cIm),... 'FontSize',14,... 'callback', {@editCB, 'cIm'}); S.cImText = annotation('textbox','units','centimeters'); set(S.cImText , 'Position', [x01+0.5 y01+.9*h1 4 .8], 'String',"Im(c)",... 'fontsize',14,'interpreter','latex','HorizontalAlignment','center',... 'BackgroundColor',[0.9,0.9,0.9],'VerticalAlignment','middle','FitBoxToText','on'); S.dxMEditBox = uicontrol('style','edit',... 'unit','centimeters',... 'position',[x01+5.5 y01+0.15*h1 4 0.8],... 'String', num2str(S.dxM),... 'FontSize',14,... 'callback', {@editCB, 'dxM'}); S.dxText = annotation('textbox','units','centimeters'); set(S.dxText , 'Position', [x01+0.5 y01+0.15*h1 4 .8],'String',"Pixel Size",... 'fontsize',14,'interpreter','latex','HorizontalAlignment','center',... 'BackgroundColor',[0.9,0.9,0.9],'VerticalAlignment','middle','FitBoxToText','on'); S.max_iterMEditBox = uicontrol('style','edit',... 'unit','centimeters',... 'position',[x01+5.5 y01+0.08*h1 4 0.8],... 'String', num2str(S.max_iterM),... 'FontSize',14,... 'callback', {@editCB, 'max_iterM'}); S.max_iterText = annotation('textbox','units','centimeters'); set(S.max_iterText , 'Position', [x01+0.5 y01+0.08*h1 4 .8], 'String',"Max Iterates",... 'fontsize',14,'interpreter','latex','HorizontalAlignment','center',... 'BackgroundColor',[0.9,0.9,0.9],'VerticalAlignment','middle','FitBoxToText','on'); S.cText = annotation('textbox','units','centimeters'); set(S.cText , 'Position', [x01+3 y01+0.84*h1 4 .8],... 'String',"Selected: $c = "+num2str(S.cRe) + " + "+num2str(S.cIm)+"i$", ... 'fontsize',14,'BackgroundColor',[0.9,0.9,0.9],'interpreter','latex', ... 'HorizontalAlignment','center','VerticalAlignment','middle', ... 'EdgeColor','none','FitBoxToText','on'); S.cText2 = annotation('textbox','units','centimeters'); set(S.cText2 , 'Position', [x01+1 y01+0.8*h1 4 .8], ... 'String',"(click or enter values to change)", ... 'fontsize',14,'interpreter','latex', 'EdgeColor','none',... 'HorizontalAlignment','left','VerticalAlignment','middle', ... 'EdgeColor','none','FitBoxToText','on'); S.zText = annotation('textbox','units','centimeters'); set(S.zText , 'Position', [x02+w2/2-2 y02+0.95*h2 4 .8], ... 'String',"$z_0 = "+num2str(S.zRe) + " + "+num2str(S.zIm)+"i$", ... 'fontsize',14,'interpreter','latex','HorizontalAlignment','center', ... 'VerticalAlignment','middle','EdgeColor','none','FitBoxToText','on'); S.zText2 = annotation('textbox','units','centimeters'); set(S.zText2 , 'Position', [x02+w2/2-2 y02+0.92*h2 4 .8], ... 'String',"(click to change)",'fontsize',14,'interpreter','latex', ... 'HorizontalAlignment','center','VerticalAlignment','middle', ... 'EdgeColor','none','FitBoxToText','on'); hold on; % indicate the current c value S.currentC = scatter(S.cRe, S.cIm, 50, 'MarkerFaceColor','White'); uistack(S.currentC, 'top'); uistack(S.dxText,'top'); uistack(S.max_iterText,'top'); xlim([S.x_minM, S.x_maxM]); ylim([S.y_minM,S.y_maxM]); % S.cReSlider = uicontrol('style','slider',... % 'unit','centimeters',... % 'position',[x01-0.4 y01-0.2 w1+0.8 0.5],... % 'min',-2.5,'max',1,'value', S.cRe,... % 'sliderstep',[0.001/3.5 0.001/3.5],... % 'callback', {@SliderCB, 'cRe'}); % % % S.cImSlider = uicontrol('style','slider',... % 'unit','centimeters',... % 'position',[x01-2.4 y01-0.4 0.5 h1+0.8],... % 'min',-1.5,'max',1.5,'value', S.cIm,... % 'sliderstep',[0.001/3 0.001/3],... % 'callback', {@SliderCB, 'cIm'}); S.zResetButton = uicontrol('Style','pushbutton',... 'Units','centimeters',... 'position',[x02+w2/2-1.5 y02-1.7 3 0.8],... 'String','Reset to 0',... 'Callback',{@resetZ}); %slider labels want to stick to actual plot coordinates axes(S.MandelAxis); % S.cReSliderLabel = text(S.cRe, -1.68,num2str(S.cRe),'fontSize',11,'interpreter','latex','HorizontalAlignment','center'); % S.cImSliderLabel = text(-2.53, S.cIm ,num2str(S.cIm),'fontSize',11,'interpreter','latex','HorizontalAlignment','left','VerticalAlignment','middle'); S.JuliAxis = subplot(1,2,2); set(S.JuliAxis, 'units','centimeters','position',[x02,y02,w2,h2]); title("Corresponding Filled Julia Set $J_c$",'Interpreter','latex','FontSize',18); hold on; axes(S.JuliAxis); % uistack(S.zText,'top'); % uistack(S.zText2 ,'top'); set(S.fig, 'WindowButtonDownFcn', @clicker); S.x_min = -2; %-1.5; S.x_max = 2; % 1.5; S.y_min = -2; %-1.5; S.y_max = 2; % 1.5; S.dx = 0.005; S.x_grid = S.x_min : S.dx : S.x_max; S.y_grid = (S.y_min : S.dx : S.y_max)'; S.x_N = length(S.x_grid); S.y_N = length(S.y_grid); S.max_iterJ = 100; S.N_orbit = 100000; S.max_iterJEditBox = uicontrol('style','edit',... 'unit','centimeters',... 'position',[x02+5.5 y02+0.08*h1 4 0.8],... 'String', num2str(S.max_iterJ),... 'FontSize',14,... 'callback', {@editCB, 'max_iterJ'}); S.max_iterJText = annotation('textbox','units','centimeters'); set(S.max_iterJText , 'Position', [x02+0.5 y02+0.08*h1 4 .8], 'String',"Max Iterates",... 'fontsize',14,'interpreter','latex','HorizontalAlignment','center',... 'BackgroundColor',[0.9,0.9,0.9],'VerticalAlignment','middle','FitBoxToText','on'); % dummy orbit, data will be edited S.orbitline = plot(1,1,'-o','color','white','MarkerFaceColor','auto'); S.orbitConverge = scatter(5,5,'red','filled'); xlim([-1.5,1.5]); ylim([-1.5, 1.5]); guidata(S.fig, S); % Store S struct in the figure %draw the right panel and initial orbit update(S,'Julia'); update(S,'orbit'); end function resetZ(button, EventData) S = guidata(button); S.zRe = 0; S.zIm = 0; zStr = "$z_0 = "+num2str(S.zRe) + " + "+num2str(S.zIm)+"i$"; S.zText.String = zStr; update(S,'orbit'); end function editCB(box, eventData, Param) S = guidata(box); S.(Param) = str2double(box.String); % edit 'S.cRe','S.cIm', 'S.dxM', 'S.max_iterM', or 'S.max_iterJ' % changes which redraw the Julia Set if strcmp(Param, 'cRe') || strcmp(Param, 'cIm') || strcmp(Param, 'max_iterJ') % move indicator point S.currentC.XData = S.cRe; S.currentC.YData = S.cIm; cStr = "$c = "+num2str(S.cRe) + " + "+num2str(S.cIm)+"i$"; S.cText.String = cStr; % S.cReSlider.Value = S.cRe; % S.cImSlider.Value = S.cIm; % set(S.cReSliderLabel,'String',num2str(S.cRe),'Position',[S.cRe, -1.68]); % set(S.cImSliderLabel,'String',num2str(S.cIm),'Position',[-2.65, S.cIm]); %redraw the Julia set update(S,'Julia'); %changes which redraw the Mandelbrot Set elseif strcmp(Param, 'dxM') || strcmp(Param,'max_iterM') axes(S.MandelAxis); S.x_minM = S.MandelAxis.XLim(1); S.x_maxM = S.MandelAxis.XLim(2); S.y_minM = S.MandelAxis.YLim(1); S.y_maxM = S.MandelAxis.YLim(2); S.x_gridM = S.x_minM : S.dxM : S.x_maxM; S.y_gridM = (S.y_minM : S.dxM : S.y_maxM)'; S.x_NM = length(S.x_gridM); S.y_NM = length(S.y_gridM); drawMandelbrot(S); uistack(S.currentC,'top'); elseif strcmp(Param, 'max_iterJ') end guidata(box, S); end function SliderCB(slider, EventData, Param) % Callback for c sliders S = guidata(slider); % Get S struct from the figure S.(Param) = get(slider, 'Value'); % edit either 'S.cRe' or 'S.cIm' S.currentC.XData = S.cRe; S.currentC.YData = S.cIm; cStr = "$c = "+num2str(S.cRe) + " + "+num2str(S.cIm)+"i$"; S.cText.String = cStr; S.cReEditBox.String = num2str(S.cRe); S.cImEditBox.String = num2str(S.cIm); % set(S.cReSliderLabel,'String',num2str(S.cRe),'Position',[S.cRe, -1.68]); % set(S.cImSliderLabel,'String',num2str(S.cIm),'Position',[-2.65, S.cIm]); axes(S.MandelAxis) uistack(S.currentC, 'top'); %redraw the Julia set update(S,'Julia'); guidata(slider, S); % Store modified S in figure end function clicker(h, ~) % Callback for clicking S = guidata(h); % Get S struct from the figure coords = get(S.JuliAxis, 'currentpoint'); if coords(1,1)>-1.5 S.zRe = coords(1,1); S.zIm = coords(1,2); zStr = "$z_0 = "+num2str(S.zRe) + " + "+num2str(S.zIm)+"i$"; S.zText.String = zStr; update(S,'orbit'); else coords = get(S.MandelAxis, 'currentpoint'); if (coords(1,1)>-2.5) && (coords(1,1)<1) && (coords(1,2)>-1.5) && (coords(1,2)<1.5) S.cRe = coords(1,1); S.cIm = coords(1,2); S.currentC.XData = S.cRe; S.currentC.YData = S.cIm; cStr = "$c = "+num2str(S.cRe) + " + "+num2str(S.cIm)+"i$"; S.cText.String = cStr; S.cReSlider.Value = S.cRe; S.cImSlider.Value = S.cIm; % set(S.cReSliderLabel,'String',num2str(S.cRe),'Position',[S.cRe, -1.68]); % set(S.cImSliderLabel,'String',num2str(S.cIm),'Position',[-2.65, S.cIm]); S.cReEditBox.String = num2str(S.cRe); S.cImEditBox.String = num2str(S.cIm); update(S,'Julia'); end end guidata(h, S); % Store modified S in figure end function update(S, updateType) if strcmp(updateType, 'orbit') c = S.cRe + S.cIm*i; z = S.zRe + S.zIm*i; esc = max(2,abs(c)); N_orbit = S.N_orbit; orbit = zeros(N_orbit,1); orbit(1) = z; iter = 2; while iter < N_orbit if abs(z)>esc break end z = z^2 + c; orbit(iter) = z; iter = iter + 1; end orbit_re = real(orbit(1:iter-1)); orbit_im = imag(orbit(1:iter-1)); set(S.orbitline, 'XData', orbit_re); set(S.orbitline, 'YData', orbit_im); if length(S.orbitline.XData)>=S.N_orbit-1 S.orbitConverge.XData = S.orbitline.XData(end-20:end); S.orbitConverge.YData = S.orbitline.YData(end-20:end); else S.orbitConverge.XData = []; S.orbitConverge.YData = []; end xlim([S.x_min, S.x_max]); ylim([S.y_min, S.y_max]); uistack(S.zText,'top'); uistack(S.zText2,'top'); uistack(S.orbitline,'top'); uistack(S.orbitConverge,'top'); elseif strcmp(updateType, 'Julia') c = S.cRe + S.cIm*i; esc = max(2,abs(c)); Julia = zeros(S.y_N, S.x_N, 3); for x_ind = 1:S.x_N x = S.x_grid(x_ind); for y_ind = 1:S.y_N y = S.y_grid(y_ind); z = x + y*i; iter = 0; while iter < S.max_iterJ if abs(z)>esc break; end z = z^2 + c; iter = iter + 1; end if iter ==S.max_iterJ Julia(y_ind, x_ind,:) = [0,0,0]; else Julia(y_ind, x_ind,:) = [.6 + .3*cos(iter/10), .5 + .5*sin(iter/8), .55 + .2*cos(iter/5)]; end end end axes(S.JuliAxis); S.Julimage = image(S.x_grid, S.y_grid, Julia); set(gca, 'YDir', 'normal'); guidata(S.fig, S); % Store modified S in figure update(S, 'orbit'); end end function drawMandelbrot(S) tic Mandelbrot = zeros(S.y_NM, S.x_NM, 3); for x_ind = 1:S.x_NM x = S.x_gridM(x_ind); for y_ind = 1:S.y_NM y = S.y_gridM(y_ind); c = x + y*i; esc = max(2,abs(c)); z = 0 + 0i; iter = 0; while iter < S.max_iterM if abs(z)>esc break; end z = z^2 + c; iter = iter + 1; end if iter ==S.max_iterM Mandelbrot(y_ind, x_ind,:) = [0,0,0]; else if iter>100 iter = 100+ (iter-100)^0.8; % slows the color cycling a bit end Mandelbrot(y_ind, x_ind,:) = [.6 + .3*cos(iter/10), .5 + .5*sin(iter/8), .55 + .2*cos(iter/5)]; end end end S.Mandelimage = image(S.x_gridM,S.y_gridM,Mandelbrot); set(gca, 'YDir', 'normal'); guidata(S.fig, S); % Store modified S in figure toc end