Message Boards Message Boards

Fractal art: custom Mandelbrot set functions

Posted 2 years ago

MODERATOR NOTE: related resource function can be found here
https://resources.wolframcloud.com/FunctionRepository/resources/MandelbrotSetRemap


enter image description here

POSTED BY: Mark Greenberg
13 Replies

This was a lot of fun to play around with! Thanks!enter image description here

POSTED BY: Eli Lichtblau
Posted 2 years ago

Okay, one more. Then I'd better turn my attention to trimming the hedges and other things my wife thinks are important. Fractals don't impress her much. : )

enter image description here

This one has two layers also, the bottom layer's custom mapping is based on the complex distance to the corner coordinates of the image. The custom mapping function for the top layer is similar, but I changed the complex numbers to real plane coordinates with ReIm. Once you get the hang of what's going on in the mapping function, it's pretty easy to explore and come up with some wild and amazing images.

Here's the code:

msr = ResourceFunction["MandelbrotSetRemap"];
myMapping1 = 
  Function[{c, center, corner, maxIterations}, 
   Module[{list, corner2, dist1, dist2},
    corner2 = center + (center - corner);
    dist1 = dist2 = 1000000;
    list = NestWhileList[(
        dist1 = Min[dist1, EuclideanDistance[corner, #]];
        dist2 = Min[dist2, EuclideanDistance[corner2, #]];
        #^2 + c
        ) &,
      0, Abs[#] <= 7 &, 1, maxIterations];
    Abs[dist1 - dist2]]];
img1 = msr[-1.3276 - .06866 I, 108, MaxIterations -> 64, "MappingFunction" -> myMapping1, 
  ColorFunction -> ColorData["LakeColors"], ImageSize -> {1200, 800}];

myMapping2 =
  Function[{c, center, corner, maxIterations}, 
   Module[{list, corner1, corner2, dist1, dist2},
    corner1 = ReIm[corner];
    corner2 = ReIm[center] + (ReIm[center] - corner1);
    dist1 = dist2 = 1000000;
    list = NestWhileList[(
      dist1 = Min[dist1, EuclideanDistance[corner1, #]];
      dist2 = Min[dist2, EuclideanDistance[corner2, #]];
      #^2 + c
    ) &,
  0, Abs[#] <= 7 &, 1, maxIterations];
dist1 - dist2]];
colFn = (Blend[{
  {0, Transparent},
  {.28, RGBColor["#301361"]},
  {.5, RGBColor["#F16A17"]},
  {.77, RGBColor["#FFE07C"]},
  {1, LightYellow}},
 #1] &);
img2 = msr[-1.33 - .1 I, 22, MaxIterations -> 64, "MappingFunction" -> myMapping2,
  ColorFunction -> colFn, ImageSize -> {1200, 800}];
ImageCompose[img1, img2]
POSTED BY: Mark Greenberg
Posted 2 years ago

Okay, okay. I'll stop after this. But each new image is more exciting than the last. Like the image above, this one has two layers put together with ImageCompose. The bottom layer is made by the resource function MandelbrotSetRemap with a custom mapping function based on standard deviation. The variety of custom mappings seems boundless when you look at all the Wolfram functions for lists and numbers. The top layer uses the default mapping in MandelbrotSetRemap, but the color function has transparency to let the bottom layer through.

enter image description here

Here's the code. Be patient, custom mapping functions take a lot of time to render and this code renders at a very large scale and then shrinks the image to avoid the jaggies:

msr = ResourceFunction["MandelbrotSetRemap"];
myMapping1 = 
  Function[{c, center, corner, maxIterations}, Module[{list},
    list = NestWhileList[#^2 + c &, 0, Abs[#] <= 4 &, 1, maxIterations];
    Total[Arg[list]] * StandardDeviation[list]]
  ];
img1 = msr[-.875 + .256 I, 78, MaxIterations -> 32, "MappingFunction" -> myMapping1, 
  ColorFunction -> ColorData[{"PlumColors", "Reversed"}], ImageSize -> 2400];
colFn = (Blend[{{0, Transparent}, {.25, Transparent}, {.45, Black}, {.97, LightPurple}, {1, Darker[Blue, .8]}}, #1] &);
img2 = msr[-.875 + .256 I, 78, MaxIterations -> 72, ColorFunction -> colFn, ImageSize -> 2400];
ImageResize[ImageCompose[img1, img2], 600]
POSTED BY: Mark Greenberg

Please don't stop :)

POSTED BY: Ahmed Elbanna
Posted 2 years ago

Yes! Don't stop Mark. Would be great if you could also include the code you used to generate the images so others can use it as a starting point for their own explorations.

POSTED BY: Rohit Namjoshi
Posted 2 years ago

Hi Mark,

custom mapping functions take a lot of time to render

Compilation can speed it up significantly. MandelbrotSetRemap uses FunctionCompile internally, using Compile instead here because it is easier to deal with and supports a larger range of built-in functions. NestWhileList is not supported so While is used instead.

myMapping1Compiled = 
 Compile[{{c, _Complex}, {center, _Complex}, {corner, _Complex}, {maxIterations, _Integer}},
  Module[{list = {0. + 0. I}, iter = 0, z = 0. + 0. I},
   While[Abs[z] < 4.0 && iter < maxIterations,
    AppendTo[list, z = z^2 + c]; ++iter];
   Total[Arg[list]]*StandardDeviation[list]],
  CompilationTarget -> "C"]

img1 = msr[-.875 + .256 I, 78, 
  MaxIterations -> 32, 
  "MappingFunction" -> myMapping1Compiled, 
  ColorFunction -> ColorData[{"PlumColors", "Reversed"}], 
  ImageSize -> 2400]

is about 4x faster on my machine.

POSTED BY: Rohit Namjoshi
Posted 2 years ago

And here's another image. This one combines two layers rendered from the resource function MandelbrotSetRemap with no additional filtering.

enter image description here

Here's the code:

msr = ResourceFunction["MandelbrotSetRemap"];
colFn1 = (Blend[{{0, Transparent}, {.35, Black}, {.4, Transparent}, {1,  Black}}, #1] &);
layer1 = msr[-.8298 - .2281 I, 1103, MaxIterations -> 96, ColorFunction -> colFn1, ImageSize -> {1200, 800}];
colFn2 = (Blend[{{0, Red}, {.2, Darker[Red]}, {.6, Orange}, {1, Yellow}}, #1] &);
layer2 = msr[-.8298 - .2281 I, 1103, MaxIterations -> 44, MappingFunction -> "LineOrbitTrap", ColorFunction -> colFn2, ImageSize -> {1200, 800}];
ImageCompose[layer2, layer1]
POSTED BY: Mark Greenberg

enter image description here -- you have earned Featured Contributor Badge enter image description here Your exceptional post has been selected for our editorial column Staff Picks http://wolfr.am/StaffPicks and Your Profile is now distinguished by a Featured Contributor Badge and is displayed on the Featured Contributor Board. Thank you!

POSTED BY: Moderation Team

This is quite beautiful and I hope some form makes its way to the Wolfram Function Repository.

And ignore those who object to the artistry, they're just being Mandelbrats.

POSTED BY: Daniel Lichtblau
Posted 2 years ago

Thanks, Daniel. I think I'll wait and see if anyone in the Community has an idea of how to speed up the code. Functions that take so long to evaluate would not be widely appealing. I'd be willing to collaborate with someone. Writing speedy functions is not my forte. : )

Mandelbrats... lol.

POSTED BY: Mark Greenberg

The speed is not too bad. It takes 10 or so seconds to create the first couple of custom images on my machine. You could probably get a good speed boost using Compile though.

POSTED BY: Daniel Lichtblau
Posted 2 years ago

I did pair up with fellow programmer and frequent Community contributor @Rohit Namjoshi to write a resource function called MandelbrotSetRemap. It captures the essence of what I outlined in this article but speeds up the renders with compile and parallel processing. It turned out well. Check it out in the Function Repository.

POSTED BY: Mark Greenberg
Posted 2 years ago

Here is an image I made with the new resource function MandelbrotSetRemap. It has been further stylized with other Wolfram Language filters. enter image description here

Here's the code for this image:

msr = ResourceFunction["MandelbrotSetRemap"];
img1 = msr[.333, 32, MaxIterations -> 128, MappingFunction -> "ModTrailings", ImageSize -> {1200, 800}];
img2 = msr[.333, 32, MaxIterations -> 64, MappingFunction -> "Decomposition", 
  ColorFunction -> ColorData["BrightBands"], ImageSize -> {1200, 800}];
ImageAdjust[ImageEffect[ImageMultiply[{img1, img2}], {"EdgeStylization"}]]
POSTED BY: Mark Greenberg
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract