Mandelbrot Set Generators in JoyNick Forde [nickf@system-7.freeserve.co.uk]Creation Date: Wed Apr 10 2002 Last Updated: Mon Apr 15 2002 |
The following simple Mandelbrot set generator example was discussed on the "concatenative" mailing list in March 2002 under the subject title, "Another imperative example in Joy". This document is a reference to the implementations provided on the list. For more details see the mailing list archive. Suggested alternative implementations would be most welcome.
This simple implementation was based on some Basic code written back in the 1980's. There are lots of variations on this floating around the net.
int main(int argc, char *argv[])
{
int x, y, k;
char *p = " .:,;!/>)|&IH%*#";
double z1, z2, t, c1, c2;
for (y = 30; y >= 0; y--) {
c2 = ( y * 0.1) - 1.5;
for (x = 0; x < 75; x++) {
c1 = (x * 0.04) - 2;
z1 = z2 = 0;
for (k = 0; k < 112; k++) {
t = (z1 * z1) - (z2 * z2) + c1;
z2 = (2 * z1 * z2) + c2;
z1 = t;
if ((z1 * z1) + (z2 * z2) > 10) break;
}
printf("%c", p[k%16]);
}
printf("\n");
}
}
This was my naive attempt which tried to be clear and flexible but is not at all elegant.
"seqlib" libload.
nrows == 30; ncols == 75; (* size of output display *)
mandel == -1 nrows ncols * (* one loop for rows & cols. *)
[ succ dup dup [col] dip row (* calc. column & row indices *)
get_c1 swap get_c2 (* calc. coefficients 1 & 2 *)
get_k putpt (* calc. k and then print *)
[eol] ['\n putch] [] ifte (* put newline if appropriate *)
] times; (* loop for each cell *)
row == ncols /; (* I:i -> I:row *)
col == ncols rem; (* I:i -> I:col *)
eol == col ncols pred =; (* I:i -> B *)
putpt == 16 rem " .:,;!/>)|&IH%*#" of putch; (* we've a palette of 16 *)
get_k == pairlist [0 0] swoncat (* F:c1 F:c2 -> A *)
0 [112 <= [eoloop] dip and] (* condition *)
[succ [inc_zs] dip] while (* if true *)
popd 1 -; (* cleanup -> I *)
get_c1 == 0.10 * 1.5 -; (* outer loop coeff. I:i -> F *)
get_c2 == 0.04 * 2.0 -; (* inner loop coeff. I:i -> F *)
sqr_z1 == first dup *; (* A -> I *)
sqr_z2 == 1 at dup *; (* A -> I *)
inc_z1 == dup [sqr_z1] [sqr_z2] cleave - (* (z1*z1)-(z2*z2)+c2 *)
[3 at] dip +; (* A -> I *)
inc_z2 == 3 take reverse uncons uncons (* (2*z1*z2)+c1 *)
uncons pop (* *)
2 * * + ; (* A -> I *)
eoloop == dup sqr_z1 swap sqr_z2 + 10 <=; (* (z1*z1)+(z2*z2)<=10 A -> B *)
inc_zs == dup [inc_z1] [inc_z2] cleave (* *)
pairlist swap rest rest concat; (* A -> A *)
mandel ==
0 30 from-to-list [-0.10 * 1.5 +] map
0 74 from-to-list [-0.04 * 1.0 +] map 'n swons
cartproduct [putpt] step;
putpt ==
unpair
[pop2 newline]
[pair get_k 16 rem " .:,;!/>)|&IH%*#" of putch]
ifchar;
get_k ==
[-1 [0 0]] dip [[+] mapr2 inczs [succ] dip] cons
[null not swap 112 < and] repeat pop;
inczs ==
[[* 2 *] nullary rollup [dup *] unary2
[+ 10 >] [pop2 pop] [- swap] ifte] infra;
For the main loop I had wanted to write:
mandel ==
0 30 from-to-list [-0.10 * 1.5 +] map
0 74 from-to-list [-0.04 * 1.0 +] map
cartproduct 31 slices
[[putput] step newline] step;
putpt == ... just call get_k and output a char
Unfortunately there was nothing like a "slices" function that I could find - ie. take a list and slice it up into that number of equi-sized sublists. So I ended up deciding the 'n hack would be quicker than writing up 'slices' myself. But the slices solution would have been more elegant ...
mandel ==
0 30 from-to-list [-0.10 * 1.5 +] map
0 74 from-to-list [-0.04 * 1.0 +] map
cartproduct 31 slices
[[putpt] step newline] step;
putpt ==
get_k 16 rem " .:,;!/>)|&IH%*#" of putch;
inczs ==
[[* 2 *] nullary rollup [dup *] unary2
[+ 10 >] [pop2 pop] [- swap] ifte] infra;
get_k ==
[-1 [0 0]] dip [[+] mapr2 inczs [succ] dip] cons
[null not swap 112 < and] repeat pop;
slices ==
[reverse dup size [] rollup] dip /
[dup swapd [] rollup
[swapd [unswons] dipd consd swapd pred]
[0 >] repeat
pop [rollup consd] dip swap]
[pop null not] repeat
pop2;
The output from the above implementations should look something like the following:
|