#include "tinyxml2.h" #include "fractal.h" #include #include #include "triangle.h" Variation::Variation(Type _type, double _weight, std::vector _params) : type(_type), weight(_weight), params(_params) { } void Fractal::set_palette(std::string colors) { for (int i=0; i<256; i++) { std::string hexstr = colors.substr(i*6, 6); palette.push_back(Color::fromHex(hexstr.c_str())); } } void Fractal::add_transform(double weight, Matrix3x3 transform, double color, std::vector variations) { max += weight; transforms[max] = Transform(transform, color, variations); } void Fractal::sample(double& x, double& y, double& c) const { double rtran = (double)rand()/RAND_MAX*max; const Transform& transform = transforms.upper_bound(rtran)->second; Vector3D in = transform.transform * Vector3D(x, y, 1.0); double r2 = pow(in.x,2.0)+pow(in.y,2.0); double r = sqrt(r2); double tx, ty; x = 0.0; y = 0.0; for (auto& variation : transform.variations) { switch (variation.type) { case Variation::Type::linear: { tx = in.x; ty = in.y; break; } case Variation::Type::sinusoidal: { tx = sin(in.x); ty = sin(in.y); break; } case Variation::Type::spherical: { tx = in.x/r2; ty = in.y/r2; break; } case Variation::Type::eyefish: { double xp = 2.0/(r+1.0); tx = in.x*xp; ty = in.y*xp; break; } case Variation::Type::fisheye: { double xp = 2.0/(r+1.0); tx = in.y*xp; ty = in.x*xp; break; } case Variation::Type::bubble: { double xp = 4.0/(r2+4); tx = in.x*xp; ty = in.y*xp; break; } case Variation::Type::cylinder: { tx = sin(in.x); ty = in.y; break; } case Variation::Type::noise: { double phi1 = (double)rand()/RAND_MAX; double phi2 = (double)rand()/RAND_MAX; tx = in.x * phi1 * cos(2*M_PI*phi2); ty = in.y * phi1 * sin(2*M_PI*phi2); break; } case Variation::Type::blur: { double phi1 = (double)rand()/RAND_MAX; double phi2 = (double)rand()/RAND_MAX; tx = phi1 * cos(2*M_PI*phi2); ty = phi1 * sin(2*M_PI*phi2); break; } case Variation::Type::horseshoe: { tx = (1.0/r)*((in.x-in.y)*(in.x+in.y)); ty = 2*in.x*in.y/r; break; } case Variation::Type::swirl: { tx = in.x*sin(r2) - in.y*cos(r2); ty = in.x*cos(r2) + in.y*sin(r2); break; } case Variation::Type::julian: { double p1 = variation.params[0]; double p2 = variation.params[1]; double p3 = (double)(rand()%(int)floor(std::abs(p1))); double t = (atan2(in.y, in.x) + 2*M_PI*p3) / p1; double pw = pow(r2, p2 / p1 / 2.0); tx = pw * cos(t); ty = pw * sin(t); break; } case Variation::Type::hyperbolic: { double theta = atan2(in.x, in.y); tx = sin(theta)/r; ty = r*cos(theta); break; } case Variation::Type::polar: { double theta = atan2(in.x, in.y); tx = theta/M_PI; ty = r - 1; break; } case Variation::Type::handkerchief: { double theta = atan2(in.x, in.y); tx = r * sin(theta + r); ty = r * cos(theta - r); break; } case Variation::Type::heart: { double theta = atan2(in.x, in.y); tx = r * sin(theta * r); ty = -r * cos(theta * r); break; } case Variation::Type::disc: { double theta = atan2(in.x, in.y); tx = theta/M_PI*sin(r*M_PI); ty = theta/M_PI*cos(r*M_PI); break; } case Variation::Type::spiral: { double theta = atan2(in.x, in.y); tx = 1.0/r * (cos(theta) + sin(r)); ty = 1.0/r * (sin(theta) - cos(r)); break; } case Variation::Type::diamond: { double theta = atan2(in.x, in.y); tx = sin(theta)*cos(r); ty = cos(theta)*sin(r); break; } case Variation::Type::ex: { double theta = atan2(in.x, in.y); double p0 = pow(sin(theta + r), 3.0); double p1 = pow(cos(theta - r), 3.0); tx = r * (p0 + p1); ty = r * (p0 - p1); break; } case Variation::Type::julia: { double theta = atan2(in.x, in.y); double omega = (double)(rand()%2) * M_PI; double sr = sqrt(r); tx = sr * cos(theta/2.0 + omega); ty = sr * sin(theta/2.0 + omega); break; } case Variation::Type::bent: { if (in.x >= 0) { tx = in.x; } else { tx = 2*in.x; } if (in.y >= 0) { ty = in.y; } else { ty = in.y/2.0; } break; } } x += tx * variation.weight; y += ty * variation.weight; } c = (c + transform.color) * (1.0/2.0); } Color Fractal::get_color(double c) const { int sc = std::min((int)floor(c * 256.0), 255); return palette[sc]; } LogScale::LogScale(double brightness, double quality) { double contrast = 1.0; double brightadjust = 2.3; double white = 200.0; k1 = contrast * (268.0 * brightadjust) * 100.0 * brightness / (256.0*256.0); k2 = 1.0 / (contrast * white * quality); for (int i=0; i<1024; i++) { memo.push_back(k1 * std::log(1+white*i*k2)/(std::log(10)*white*i)); } } double LogScale::log(double n) const { int in = (int)floor(n); if (in < 1024) { return memo[in]; } else { double white = 200.0; return k1 * std::log(1+white*n*k2)/(std::log(10)*white*n); } } template Container split(std::string input, std::string delimiter) { Container result; while (!input.empty()) { int divider = input.find(delimiter); if (divider == std::string::npos) { result.push_back(input); input = ""; } else { result.push_back(input.substr(0, divider)); input = input.substr(divider+delimiter.length()); } } return result; } int Fractal::load(const char* filename, Fractal& fractal) { std::ifstream in(filename); if(!in.is_open()) { return -1; } in.close(); tinyxml2::XMLDocument doc; doc.LoadFile(filename); if(doc.Error()) { doc.PrintError(); exit(1); } tinyxml2::XMLElement* root = doc.FirstChildElement( "flame" ); if( !root ) { std::cerr << "Error: not a flame file!" << std::endl; exit( 1 ); } root->QueryDoubleAttribute("filter", &fractal.filterlevel); root->QueryDoubleAttribute("gamma", &fractal.gamma); root->QueryDoubleAttribute("gamma_threshold", &fractal.gammathresh); root->QueryDoubleAttribute("brightness", &fractal.brightness); const char* sizestr = root->Attribute("size"); sscanf(sizestr, "%lf %lf", &fractal.width, &fractal.height); tinyxml2::XMLElement* elem = root->FirstChildElement(); while (elem) { std::string elementType (elem->Value()); if (elementType == "xform") { double weight; double color; Matrix3x3 transform; elem->QueryDoubleAttribute("weight", &weight); elem->QueryDoubleAttribute("color", &color); std::string transstr(elem->Attribute("coefs")); auto transvals = split>(transstr, " "); transform(0,0) = std::stod(transvals[0]); transform(0,1) = std::stod(transvals[2]); transform(0,2) = std::stod(transvals[4]); transform(1,0) = std::stod(transvals[1]); transform(1,1) = std::stod(transvals[3]); transform(1,2) = std::stod(transvals[5]); transform(2,0) = 0.0; transform(2,1) = 0.0; transform(2,2) = 1.0; std::vector varies; const char* varyval = 0; if ((varyval = elem->Attribute("linear")) != 0) { varies.push_back(Variation(Variation::Type::linear, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("sinusoidal")) != 0) { varies.push_back(Variation(Variation::Type::sinusoidal, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("spherical")) != 0) { varies.push_back(Variation(Variation::Type::spherical, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("eyefish")) != 0) { varies.push_back(Variation(Variation::Type::eyefish, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("bubble")) != 0) { varies.push_back(Variation(Variation::Type::bubble, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("cylinder")) != 0) { varies.push_back(Variation(Variation::Type::cylinder, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("noise")) != 0) { varies.push_back(Variation(Variation::Type::noise, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("blur")) != 0) { varies.push_back(Variation(Variation::Type::blur, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("pre_blur")) != 0) { varies.push_back(Variation(Variation::Type::blur, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("horseshoe")) != 0) { varies.push_back(Variation(Variation::Type::horseshoe, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("swirl")) != 0) { varies.push_back(Variation(Variation::Type::swirl, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("hyperbolic")) != 0) { varies.push_back(Variation(Variation::Type::hyperbolic, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("julian")) != 0) { varies.push_back(Variation(Variation::Type::julian, std::stod(std::string(varyval)), {elem->DoubleAttribute("julian_power"), elem->DoubleAttribute("julian_dist")})); } if ((varyval = elem->Attribute("polar")) != 0) { varies.push_back(Variation(Variation::Type::polar, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("handkerchief")) != 0) { varies.push_back(Variation(Variation::Type::handkerchief, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("heart")) != 0) { varies.push_back(Variation(Variation::Type::heart, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("disc")) != 0) { varies.push_back(Variation(Variation::Type::disc, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("spiral")) != 0) { varies.push_back(Variation(Variation::Type::spiral, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("diamond")) != 0) { varies.push_back(Variation(Variation::Type::diamond, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("ex")) != 0) { varies.push_back(Variation(Variation::Type::ex, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("julia")) != 0) { varies.push_back(Variation(Variation::Type::julia, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("bent")) != 0) { varies.push_back(Variation(Variation::Type::bent, std::stod(std::string(varyval)))); } if ((varyval = elem->Attribute("fisheye")) != 0) { varies.push_back(Variation(Variation::Type::fisheye, std::stod(std::string(varyval)))); } fractal.add_transform(weight, transform, color, varies); } else if (elementType == "palette") { std::string val(elem->GetText()); for (int i=0; iNextSiblingElement(); } return 0; } Fractal Fractal::random() { Fractal fractal; int xforms = rand() % 2 + 2; double remweight = 1.0; for (int i=0; i variations; double remaffix = 1.0; for (int j=0; j<2; j++) { double affix; if (j == 1) { affix = remaffix; } else { affix = ((double)rand()/RAND_MAX)*remaffix; remaffix -= affix; } Variation::Type type; switch (rand()%16) { case 0: type = Variation::Type::linear; break; case 1: type = Variation::Type::sinusoidal; break; case 2: type = Variation::Type::spherical; break; case 3: type = Variation::Type::eyefish; break; case 4: type = Variation::Type::bubble; break; case 5: type = Variation::Type::cylinder; break; case 6: type = Variation::Type::blur; break; case 7: type = Variation::Type::horseshoe; break; case 8: type = Variation::Type::swirl; break; case 9: type = Variation::Type::hyperbolic; break; case 10: type = Variation::Type::polar; break; case 11: type = Variation::Type::handkerchief; break; case 12: type = Variation::Type::heart; break; case 13: type = Variation::Type::disc; break; case 14: type = Variation::Type::spiral; break; case 15: type = Variation::Type::diamond; break; } variations.push_back(Variation(type, affix)); } fractal.add_transform(weight, affine, color, variations); } std::vector colors; std::ifstream colorfile("colors.txt"); if (!colorfile.is_open()) { std::cout << "Could not find colors.txt" << std::endl; exit(-1); } std::string line; while (getline(colorfile, line)) { if (line.back() == '\r') { line.pop_back(); } colors.push_back(line); } colorfile.close(); fractal.set_palette(colors[rand() % colors.size()]); return fractal; }