This here is a code of my recent uni assignment to render Mandelbrot, Julia and Burning Ship sets, using SDL.
The code works properly, using MS Visual Studio 2010 Premium, but displays some errors (either numerical or type casting differences, I couldn't find them) after porting to Linux and compiling using g++.
If you compile it under VS and it runs slowly, try compiling it in release configuration instead of debug, as VS has quite a bit of run-time checks built into debugging versions of STL.
Also, the first idea was to store the rendered SDL_Surface internally, but I ran out of time to implement that :/
And I suppose you could optimize it somehow too, anyways I am putting it here mostly to ask other members of DaniWeb to evaluate it, and if someone finds it helpful, all the better :)

#include <vector>
#include <algorithm>
#include <complex>
#include <cmath>
#include "SDL/SDL.h"

namespace fractal{
	//	typedefs
	typedef enum {MANDEL=0, JULIA=1, SHIP=2} fractType;

	//	constants
	const int MODE_COUNT=3;

	//	a bit ugly, but shortens the code and such
	//	order as in modeType
	const int RES_W[MODE_COUNT]={400,300,400};
	const int RES_H[MODE_COUNT]={250,300,250};
	const double MIN_RE[MODE_COUNT]={-2.5,-2,-2.5};
	const double MAX_RE[MODE_COUNT]={1.5,2,1.5};
	const double MIN_IM[MODE_COUNT]={-1.25,-2,-1.75};
	const double MAX_IM[MODE_COUNT]={1.25,2,0.75};

	//	histogram
	class Histogram{
	private:
		static const int BPP=32;
		static const unsigned char COLOR_R=0x24;
		static const unsigned char COLOR_G=0xb0;
		static const unsigned char COLOR_B=0x15;
		fractType type;
		static int iters;
		int width,height;
		long double top, left,bottom, right;
		std::vector<std::vector<long double> > hist;
		bool validHist,validSurf;
		void init();
		void histValid(bool valid);
		void surfValid(bool valid);
		std::complex<long double> parameter;
	public:
		Histogram(const fractType &type_=MANDEL);
		~Histogram();
		long double& operator()(const int &x,const int &y);
		const long double &operator()(const int &x,const int &y)const;
		long double getLeft()const;
		long double getRight()const;
		long double getTop()const;
		long double getBottom()const;
		int getWidth()const;
		int getHeight()const;
		void increaseIters();
		void decreaseIters();
		int getIters()const;
		void setType(const fractType &type);
		fractType getType()const;
		void setParam(const std::complex<long double> &c);
		void setParam(const long double &cRe,const long double &cIm);
		void setRes(const int &width_,const int &height_);
		void setCenter(const long double &x,const long double &y);
		void setPos(const long double &left_, const long double &right_, const long double &top_, const long double &bottom_);
		void reset();
		void zoom(const double ×);
		void zoom(const std::complex<long double> ¢er,const double ×);
		void zoom(const long double &x, const long double &y, const double ×);
		void compute();
		void computeMandelbrot();
		void computeShip();
		void computeJulia();
		void render(SDL_Surface* const screen);
		bool isHistValid()const;
		bool isSurfValid()const;
		class Cursor{
		private:
			int x,y;
			long double re,im;
			const Histogram *hist;
		public:
			typedef enum{LEFT,RIGHT,UP,DOWN} direction;
			Cursor(Histogram *hist_=NULL);
			void center();
			void move(direction dir,int t);
			int getX();
			int getY();
			long double getRe()const;
			long double getIm()const;
			std::complex<long double> getC();
		} cursor;
	};
};

#include "histogram.h"

//////////////////////////////
//
//	constructors
//
//////////////////////////////

int fractal::Histogram::iters;

//	with some basic values
fractal::Histogram::Histogram(const fractType &type_){
	type=type_;
	this->init();
	this->setRes(RES_W[type],RES_H[type]);
	this->reset();
}

//	destructor
//	empty, since destructors for members are called by compiler
//	we don't have to clean them up
fractal::Histogram::~Histogram(){
}

//	init
void fractal::Histogram::init(){
	cursor=Cursor(this);
	iters=10;
	this->histValid(false);
	//	call from histValid marks the surface as not valid too
	//	but it's better to do it explcitly anyways
	this->surfValid(false);
}

//////////////////////////////
//
//	accesors
//
//////////////////////////////

//	informs of a change in histogram and sets whether it is valid or not
//	if it isn't, Histogram::render() will recompute the histogram
void fractal::Histogram::histValid(bool valid){
	validHist=valid;
	//	a call to this function means the histogram has changed
	//	so mark the surface not valid too, since it doesn't represent the histogram
	this->surfValid(false);
	//	safest way about it
	cursor.center();
}

//	informs of a change in the surface
//	and whether it is valid for the current histogram
void fractal::Histogram::surfValid(bool valid){
	validSurf=valid;
}

bool fractal::Histogram::isHistValid()const{
	return validHist;
}

bool fractal::Histogram::isSurfValid()const{
	return validSurf;
}

//	don't know why you would want to acces a particualr point on the histogram, but here you go
//	marks the histogram not valid - watch out for this one!
long double& fractal::Histogram::operator()(const int &x,const int &y){
	return hist[x][y];
}

//	just a const overloaded version
const long double& fractal::Histogram::operator()(const int &x,const int &y)const{
	return hist[x][y];
}

long double fractal::Histogram::getLeft()const{
	return left;
}

long double fractal::Histogram::getRight()const{
	return right;
}

long double fractal::Histogram::getTop()const{
	return top;
}

long double fractal::Histogram::getBottom()const{
	return bottom;
}

int fractal::Histogram::getWidth()const{
	return width;
}

int fractal::Histogram::getHeight()const{
	return height;
}

void fractal::Histogram::increaseIters(){
	iters++;
	this->histValid(false);
}

void fractal::Histogram::decreaseIters(){
	iters--;
	this->histValid(false);
}

int fractal::Histogram::getIters()const{
	return iters;
}

void fractal::Histogram::setType(const fractType& type_){
	type=type_;
}

fractal::fractType fractal::Histogram::getType()const{
	return type;
}

void fractal::Histogram::setParam(const std::complex<long double> &c){
	parameter=c;
}

void fractal::Histogram::setParam(const long double &cRe,const long double &cIm){
	parameter=std::complex<long double>(cRe,cIm);
}

//////////////////////////////
//
//	positioning
//
//////////////////////////////

//	sets the histogram resolution
//	marks the histogram not valid
void fractal::Histogram::setRes(const int &width_, const int &height_){
	width=width_;
	height=height_;
	hist.resize(width);
	for(int x=0;x<width;x++)
		hist[x].resize(height);
	this->histValid(false);
}

//	if you want to just move the whole histogram, without considering the boundaries
//	marks the histogram not valid
void fractal::Histogram::setCenter(const long double &x, const long double &y){
	this->zoom(x,y,1);
}

//	properly setting the position, with all the boundaries
//	marks the histogram not valid
void fractal::Histogram::setPos(const long double &left_, const long double &right_, const long double &top_, const long double &bottom_){
	left=left_;
	right=right_;
	top=top_;
	bottom=bottom_;
	//	commenting this out will allow for flipping of the histogram
	if(left>right)std::swap(left,right);
	if(top<bottom)std::swap(top,bottom);
	this->histValid(false);
}

void fractal::Histogram::reset(){
	this->setPos(MIN_RE[type],MAX_RE[type],MAX_IM[type],MIN_IM[type]);
}

//	overloaded version
void fractal::Histogram::zoom(const double ×){
	this->zoom(cursor.getRe(),cursor.getIm(),times);
}

void fractal::Histogram::zoom(const std::complex<long double> ¢er,const double ×){
	this->zoom(center.real(),center.imag(),times);
}

//	zoom in at position
//	marks the histogram not valid
//	zooming in or out too much will cause overflow or underflow errors
//	IEEE 754 lists the max values as more or less +/- 10^4931
//	although most likely you will sooner encounter precision troubles
void fractal::Histogram::zoom(const long double &x, const long double &y, const double ×){
	long double w=abs(right-left)*0.5/times;
	long double h=w*height/width;
	left=x-w;
	right=x+w;
	top=y+h;
	bottom=y-h;
	this->histValid(false);
}

//////////////////////////////
//
//	computing
//
//////////////////////////////

//	general compute call, takes the fractal type as a parameter
//	just launches the apropriate compute method
void fractal::Histogram::compute(){
	switch(type){
	case MANDEL:
		this->computeMandelbrot();
		break;
	case JULIA:
		this->computeJulia();
		break;
	case SHIP:
		this->computeShip();
		break;
	}
}

//	computes the histogram
//	marks it valid
void fractal::Histogram::computeMandelbrot(){
	long double xstep=(right-left)/width;
	long double ystep=(top-bottom)/height;
	std::complex<long double> p(left,top);
	//	the computation 
	for(int x=0;x<width;x++){
		for(int y=0;y<height;y++){
			p-=std::complex<long double>(0,ystep);
			int n;
			std::complex<long double> z=0;
			for(n=0;n<iters;n++)
				if((abs(z)<2)){
					z*=z;
					z+=p;
				}
				else
					break;
			hist[x][y]=n==iters?0:(long double)n/iters;
		}
		p+=std::complex<long double>(xstep,top-bottom);
	}
	this->histValid(true);
}

//	computes the histogram
//	marks it valid
void fractal::Histogram::computeJulia(){
	long double xstep=(right-left)/width;
	long double ystep=(top-bottom)/height;
	std::complex<long double> p(left,top);
	//	the computation 
	for(int x=0;x<width;x++){
		for(int y=0;y<height;y++){
			p-=std::complex<long double>(0,ystep);
			int n;
			std::complex<long double> z=p;
			for(n=0;n<iters;n++)
				if(abs(z)<2){
					z*=z;
					z+=parameter;
				}
				else
					break;
			hist[x][y]=n==iters?0:(long double)n/iters;
		}
		p+=std::complex<long double>(xstep,top-bottom);
	}
	this->histValid(true);
}

//	computes the histogram
//	marks it valid
void fractal::Histogram::computeShip(){
	long double xstep=(right-left)/width;
	long double ystep=(top-bottom)/height;
	std::complex<long double> p(left,top);
	//	the computation 
	for(int x=0;x<width;x++){
		for(int y=0;y<height;y++){
			p-=std::complex<long double>(0,ystep);
			int n;
			std::complex<long double> z=0;
			for(n=0;n<iters;n++)
				if((abs(z)<2)){
					z=std::complex<long double>(abs(z.real()),abs(z.imag()));
					z*=z;
					z+=p;
				}
				else
					break;
			hist[x][y]=n==iters?0:(long double)n/iters;
		}
		p+=std::complex<long double>(xstep,top-bottom);
	}
	this->histValid(true);
}

//////////////////////////////
//
//	rendering
//
//////////////////////////////

//	renders the fractal
void fractal::Histogram::render(SDL_Surface* const screen){
	if(!isHistValid())throw("fratcal::Histogram::render() error: the histogram is marked as not valid, cannot render");

	bool locked=false;
	if(SDL_MUSTLOCK(screen)){
		SDL_LockSurface(screen);
		locked=true;
	}
	Uint32 *screenPixels=(Uint32*)screen->pixels;
	for(int x=0;x<width;x++)
		for(int y=0;y<height;y++){
			unsigned char r=COLOR_R*hist[x][y];
			unsigned char g=COLOR_G*hist[x][y];
			unsigned char b=COLOR_B*hist[x][y];
			Uint32 pixel=SDL_MapRGB(screen->format,r,g,b);
			screenPixels[(y+(screen->h-height)/2)*screen->w+x+(screen->w-width)/2]=pixel;
		}	
	if(locked)
		SDL_UnlockSurface(screen);
	this->surfValid(true);
}

//////////////////////////////
//
//	cursor
//
//////////////////////////////

fractal::Histogram::Cursor::Cursor(Histogram *hist_){
	hist=hist_;
}

int fractal::Histogram::Cursor::getX(){
	return x;
}

int fractal::Histogram::Cursor::getY(){
	return y;
}

long double fractal::Histogram::Cursor::getRe()const{
	return re;
}

long double fractal::Histogram::Cursor::getIm()const{
	return im;
}

std::complex<long double> fractal::Histogram::Cursor::getC(){
	return std::complex<long double>(re,im);
}

void fractal::Histogram::Cursor::center(){
	x=hist->getWidth()/2;
	y=hist->getHeight()/2;
	re=(hist->getRight()+hist->getLeft())*0.5;
	im=(hist->getTop()+hist->getBottom())*0.5;
}

void fractal::Histogram::Cursor::move(direction dir,int times){
	if(dir==LEFT||dir==RIGHT){
		int dx=times;
		long double dRe=abs(hist->getRight()-hist->getLeft())/hist->getWidth()*times;
		if(dir==LEFT){
			x=std::max(x-dx,0);
			re=std::max(re-dRe,hist->getLeft());
		}
		else{
			x=std::min(x+dx,hist->getWidth()-1);
			re=std::min(re+dRe,hist->getRight());
		}
	}
	else{
		int dy=times;
		long double dIm=abs(hist->getTop()-hist->getBottom())/hist->getHeight()*times;
		if(dir==UP){
			y=std::max(y-dy,0);
			im=std::min(im+dIm,hist->getTop());
		}
		else{
			y=std::min(y+dy,hist->getHeight());
			im=std::max(im-dIm,hist->getBottom());
		}
	}
}
DeanMSands3 commented: Rock on! +3

Recommended Answers

All 3 Replies

Very nice. However, somewhere along the line, in the zoom functions, times became an asterisk and center became a cent.

Very nice. However, somewhere along the line, in the zoom functions, times became an asterisk and center became a cent.

Well, the times sign is an asterisk, so that makes some sense.
And you probably zoomed in so far the er couldn't fit on the screen anymore...
:D

Maybe something went wrong with ampersands or something?
It displays the same way for me :/

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.