// -------------------------------------------------------------- // System Headers // -------------------------------------------------------------- #include #include #include #include #include // -------------------------------------------------------------- // Local Headers // -------------------------------------------------------------- #include "util.h" #include "svg.h" // -------------------------------------------------------------- // Random // -------------------------------------------------------------- class Random { public: Random(int low, int high) : mDist(low, high) { seed(); } Random(int high) : mDist(0, high) { seed(); } int operator()() { return(mDist(mRng)); } private: void seed() { mRng.seed(std::random_device()()); } std::mt19937 mRng; std::uniform_int_distribution mDist; }; // -------------------------------------------------------------- // Point // -------------------------------------------------------------- struct Point { Point(int x, int y) : mX(x), mY(y) { } Point() : mX(0), mY(0) { } string toString() const { std::ostringstream out; out << "(" << mX << ", " << mY << ")"; return(out.str()); } int mX; int mY; }; // -------------------------------------------------------------- // Accept functions // -------------------------------------------------------------- typedef bool (*AcceptFunction)(int, int); static bool acceptOne(int next, int previous) { return(next != previous); } static bool acceptTwo(int next, int previous) { return(next != (previous + 1) % 4); } static bool acceptThree(int next, int previous) { return(next != (previous + 2) % 4); } // -------------------------------------------------------------- // getCorners - Return a vector of Point corresponding to // 'nSides', 'radius' and (xCenter, yCenter) // -------------------------------------------------------------- static std::vector getCorners( int nSides, int xCenter, int yCenter, int radius ) { vector corners; int step = (360 / nSides); for(int degrees = step / 2; degrees < 360; degrees += step) { double theta = Util::d2r(degrees); int x = Util::rnd(xCenter + radius * cos(theta)); int y = Util::rnd(yCenter + radius * sin(theta)); corners.push_back(Point(x, y)); } return(corners); } // -------------------------------------------------------------- // draw - Render our chaos game image // -------------------------------------------------------------- static void draw(int nSides, SVG *svg, AcceptFunction accept) { int w = svg->getWidth(); int h = svg->getHeight(); vector corners = getCorners( nSides, w / 2, h / 2, Util::rnd(.495 * (w >= h ? h : w)) ); Point current = corners[0]; int count = 0; int previous = 0; Random random(nSides - 1); while(count < 25000) { int next = random(); if(accept(next, previous)) { previous = next; current.mX = (current.mX + corners[next].mX) / 2; current.mY = (current.mY + corners[next].mY) / 2; svg->circle(current.mX, current.mY, 1, Colors::none, Colors::black); count++; } } } // -------------------------------------------------------------- // getFileName - Return a file name built from 'number', as in: // "chaos-" + number + ".html" // -------------------------------------------------------------- static string getFileName(int number) { std::ostringstream out; out << "chaos-" << number << ".html"; return(out.str()); } // ------------------------------------- // main // ------------------------------------- int main() { static const AcceptFunction list[] = { acceptOne, acceptTwo, acceptThree }; static const int WIDTH = 750; static const int HEIGHT = 750; static const int N_SIDES = 4; int index = 1; for(AcceptFunction accept : list) { SVG svg(WIDTH, HEIGHT); svg.border(Colors::black); draw(N_SIDES, &svg, accept); string fileName = getFileName(index++); string errMsg; if(!svg.write(fileName, errMsg)) std::cout << errMsg << "\n"; else std::cout << "Successfully wrote: " << fileName << "\n"; } return(0); }