Skip to content

Instantly share code, notes, and snippets.

@silicontrip
Created May 4, 2022 00:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save silicontrip/815b6b84d5f46831d5bd57178a1469ea to your computer and use it in GitHub Desktop.
Save silicontrip/815b6b84d5f46831d5bd57178a1469ea to your computer and use it in GitHub Desktop.
Mandelbrot animator
#include <complex.h>
#include <stdio.h>
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import <unistd.h>
NSColor* colourWithHSV(float h, float s, float v)
{
double r, g, b;
double j;
double f, p, q, t;
/* convert HSV back to RGB */
if (s==0.0)
{
return [NSColor colorWithCalibratedRed:v green:v blue:v alpha:1.0];
} else {
while (h > 1.0) h -= 1.0;
while (h < 0) h += 1.0;
if (h==1.0) h = 0.0;
j = floor(h*6.0);
p = v * (1-s);
f = h * 6.0 - j;
q = v * (1 - (s*f));
t = v * (1 - (s*(1 - f)));
if (j < 0.5)
return [NSColor colorWithCalibratedRed:v green:t blue:p alpha:1.0];
if (j < 1.5)
return [NSColor colorWithCalibratedRed:q green:v blue:p alpha:1.0];
if (j < 2.5)
return [NSColor colorWithCalibratedRed:p green:v blue:t alpha:1.0];
if (j < 3.5)
return [NSColor colorWithCalibratedRed:p green:q blue:v alpha:1.0];
if (j < 4.5)
return [NSColor colorWithCalibratedRed:t green:p blue:v alpha:1.0];
if (j < 5.5)
return [NSColor colorWithCalibratedRed:v green:p blue:q alpha:1.0];
return [NSColor colorWithCalibratedRed:v green:t blue:p alpha:1.0];
}
}
//NSDictionary<NSString*,id>* getOptions (int argc, char *const * argv, NSString* arg)
NSDictionary* getOptions (int argc, char *const * argv, NSString* arg)
{
NSMutableDictionary* toptions = [NSMutableDictionary dictionaryWithCapacity:3];
NSMutableArray *targuments = [NSMutableArray arrayWithCapacity:3];
if (argc > 0)
{
int k;
while ((k=getopt(argc,argv,[arg UTF8String])) != -1)
{
NSString* strarg = [NSString stringWithFormat:@"%c",k];
if (optarg==NULL)
{
[toptions setObject:@"" forKey:strarg];
} else {
NSString* stropt = [NSString stringWithUTF8String:optarg];
[toptions setObject:stropt forKey:strarg];
}
// the rest are positional arguments
}
argc -= optind;
argv += optind;
int i;
for (i=0; i < argc; ++i)
[targuments addObject:[NSString stringWithUTF8String:argv[i]]];
[toptions setObject:[NSArray arrayWithArray:targuments] forKey:@"ARGS"];
// how do we return both of these?
return [NSDictionary dictionaryWithDictionary:toptions];
//arguments = [NSArray arrayWithArray:targuments];
}
return [[NSDictionary new] autorelease];
}
int main(int argc, char* argv[])
{
NSAutoreleasePool *p1 = [NSAutoreleasePool new];
// NSDictionary<NSString*,id>* options = getOptions(argc,argv,@"Z:z:o:x:y:W:H:f:h");
NSDictionary* options = getOptions(argc,argv,@"Z:z:o:x:y:W:H:f:h");
if ([options objectForKey:@"h"])
{
NSLog(@"-Z zoomPerFrame -z zoomLevelStart -o fileName -x xcoordinate -y ycoordinate -W width -H height -f frames");
exit(-1);
}
double zoom = 4.0 / 1080;
double cox = 0;
double coy = 0;
int sizex = 1920;
int sizey = 1080;
int frameCount = 1;
double zpf = 0.95;
NSString* fileName = @"mand";
if ([options objectForKey:@"Z"])
zpf = [[options objectForKey:@"Z"] floatValue];
if ([options objectForKey:@"o"])
fileName = [options objectForKey:@"o"];
if ([options objectForKey:@"z"])
zoom = [[options objectForKey:@"z"] floatValue];
if ([options objectForKey:@"x"])
cox = [[options objectForKey:@"x"] floatValue];
if ([options objectForKey:@"y"])
coy = [[options objectForKey:@"y"] floatValue];
if ([options objectForKey:@"W"])
sizex = [[options objectForKey:@"W"] intValue];
if ([options objectForKey:@"H"])
sizey = [[options objectForKey:@"H"] intValue];
if ([options objectForKey:@"f"])
frameCount = [[options objectForKey:@"f"] intValue];
NSBitmapImageRep *outrep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes: NULL
pixelsWide: sizex
pixelsHigh: sizey
bitsPerSample: 8
samplesPerPixel: 3
hasAlpha: NO
isPlanar: NO
colorSpaceName: NSDeviceRGBColorSpace
bytesPerRow: sizex * 3
bitsPerPixel: 24];
// float st = 4.0 / 1280;
float cl;
float ss;
float vv;
int count;
NSColor *nsc;
complex double c;
complex double z;
double x;
double y;
int yp; int xp;
int fc;
for (fc = 0; fc < frameCount; fc++)
{
NSLog(@"frame: %d X: %f-%f Y: %f-%f",
fc,
(-sizex / 2) * zoom + cox,
(sizex / 2) * zoom + cox,
(-sizey / 2) * zoom + coy,
(sizey / 2) * zoom + coy
);
NSAutoreleasePool *p2 = [NSAutoreleasePool new];
for(yp=0; yp<sizey; yp++) {
for(xp=0; xp<sizex; xp++) {
x = (xp - sizex / 2) * zoom + cox;
y = (yp - sizey / 2) * zoom + coy;
// NSLog(@"xp: %d yp: %d xs: %f ys: %f",xp,yp,x,y);
c = x + y * I;
z=0;
count =0;
while(cabs(z) < 2.0 && count < 256)
{
z = (z * z) + c;
count ++;
}
cl = 1.0 * count / 256.0;
ss = 1.0 * count / 8.0;
if (ss>1.0) ss = 1.0;
vv = 1.0 * (256 - count) / 8.0;
if (vv>1.0) vv = 1.0;
nsc = colourWithHSV(cl, ss, vv);
[outrep setColor:nsc atX:xp y:yp];
//[nsc autorelease];
}
}
// NSLog(@"outrep rc: %lu",[outrep retainCount]);
NSDictionary *prop = nil;
//NSData *dd = [outrep representationUsingType:NSBitmapImageFileTypePNG properties:prop];
NSData *dd = [outrep representationUsingType:NSPNGFileType properties:prop];
// NSLog(@"data rc: %lu",[dd retainCount]);
NSString* fns = [NSString stringWithFormat:@"%@%03d.png",fileName,fc];
// NSLog(@"fns rc: %lu",[fns retainCount]);
[dd writeToFile:fns atomically: NO];
[p2 release];
zoom = zoom * zpf;
}
[p1 release];
}
@silicontrip
Copy link
Author

I should've added comments on the two critical lines at the heart of the algorithm. Line 191 sets the coordinate to test. And line 198 is the Mandelbrot iterator. Z remains inside the boundary for 256 iterations, it's deemed part of the Mandelbrot set and shown in Black. otherwise the colour is determined by the number of iterations it took to cross the boundary and is therefore not part of the Mandelbrot set. The number of iterations give the image its striking colours.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment