program mandelbrot2; {Second Mandelbrot Set drawer. This program extends the previous one by including a limited zoom mode.} uses crt; {A unit that allows us to read single keypresses.} {Note to C programmers: the variable type "real" is equivalent to the C type "float".} var cx,cy: real; {Where do we want to center the brot?} scale: real; {This is the "zoom" factor.} limit: word; {Divergence check value.} lp: word; {Convergence check value.} a1,b1,a2,b2: real; {For calculating the iterations.} x,y: integer; {The pixel we are drawing.} ax,ay: real; {The actual position of (x,y) in relation to the Mandelbrot set.} key: char; {Dummy value for keypresses.} procedure init256; {Initialises a VGA mode 320x200 pixels, 256 colours. A little chunky but the extra colours are worth it, and SVGA is beyond the scope of this program.} begin asm mov ah,0 mov al,$13 int $10 end; {This is a little direct assembly language. Feel free to use this routine, and the others that follow, in your own code.} end; procedure end256; {Turns off the video mode and returns to standard 16-colour text mode.} begin asm mov ah,0 mov al,$03 int $10 end; end; procedure pixel(a,b: word; c: byte); {This procedure plots a pixel at screen coordinate (a,b) in colour c. Again, there is no need for you to understand the intrinsic workings of this routine - you are welcome to use it in your programs but do be aware that silly values for a and b could cause unexpected results.} var v: word; begin v:=a+b*320; mem[$A000:v]:=c; end; procedure draw_brot; begin {Loop through all pixels on screen. For reasons that will become clear, I am counting not from (0,0) but from (-160,-100).} for x:=-160 to 159 do for y:=-100 to 100 do begin {What is the *mathematical* value of this point?} ax:=cx+x*scale; ay:=cy+y*scale; {And now for the magic formula!} a1:=ax; b1:=ay; lp:=0; repeat {Do one iteration.} lp:=lp+1; a2:=a1*a1-b1*b1+ax; b2:=2*a1*b1+ay; {This is indeed the square of a+bi, done component-wise.} a1:=a2; b1:=b2; until (lp>255) or ((a1*a1)+(b1*b1)>limit); {The first condition is satisfied if we have convergence. The second is satisfied if we have divergence.} {Define colour and draw pixel.} if lp>255 then lp:=0; {If the point converges, it is part of the brot and we draw it with colour 0, or black.} pixel(x+160,y+100,lp); end; end; function getpix(x,y: integer): integer; {Function that returns the colour of pixel (x,y). Basically the Pixel procedure in reverse.} var q: word; begin q:=x+y*320; getpix:=Mem[$A000:q]; end; procedure draw_cross(x,y: word); {Draws a crosshair centered on (x,y), using XOR plotting. See article for details!} var i: integer; j: byte; begin {Vertical line...} for i:=-5 to 5 do begin j:=getpix(x,y+i) xor 255; pixel(x,y+i,j); end; {And horizontal line...} for i:=-5 to 5 do begin j:=getpix(x+i,y) xor 255; pixel(x+i,y,j); end; end; procedure select_point; {A procedure which lets the user select a place to zoom in on.} var xcross,ycross: integer; {These are the x and y coordinates of our selecting crosshair.} begin {Put crosshair in middle of screen.} xcross:=160; ycross:=100; draw_cross(xcross,ycross); repeat key:=readkey; draw_cross(xcross,ycross); {A neat thing about XOR plotting is that if you do it twice, the second time restores the old colours!} if (key='z') or (key='Z') then begin xcross:=xcross-1; if xcross<0 then xcross:=0; end; {Prevents the cross being moved off the display. The same trick applies to the next three.} if (key='x') or (key='X') then begin xcross:=xcross+1; if xcross>319 then xcross:=319; end; if (key='k') or (key='K') then begin ycross:=ycross-1; if ycross<0 then ycross:=0; end; if (key='m') or (key='M') then begin ycross:=ycross+1; if ycross>199 then ycross:=199; end; draw_cross(xcross,ycross); {Redraw cross, in new location.} until key=' '; {When spacebar is pressed, start the zoom.} cx:=cx+scale*(xcross-160); cy:=cy+scale*(ycross-100); scale:=scale/2; {Shifts cx,cy accordingly and doubles the zoom level. The picture is redrawn outside this procedure.} end; begin {Main section!} {Set up video mode.} init256; {Set up initial values for drawing. Try compiling the program with different values here if you like!} cx:=0; cy:=0; scale:=0.02; limit:=4; draw_brot; {Draws Mandelbrot set with initial values.} repeat key:=readkey; {Takes a keypress from user.} if key=' ' then begin select_point; draw_brot; end; {If spacebar pressed, user wishes to zoom in.} until (key='Q') or (key='q'); {If Q pressed, user wishes to leave program.} end256; {Return to text mode.} end.