summary refs log tree commit diff stats
path: root/fractal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'fractal.cpp')
-rw-r--r--fractal.cpp602
1 files changed, 602 insertions, 0 deletions
diff --git a/fractal.cpp b/fractal.cpp new file mode 100644 index 0000000..145886a --- /dev/null +++ b/fractal.cpp
@@ -0,0 +1,602 @@
1#include "tinyxml2.h"
2#include "fractal.h"
3#include <iostream>
4#include <fstream>
5#include "triangle.h"
6
7Variation::Variation(Type _type, double _weight, std::vector<double> _params) : type(_type), weight(_weight), params(_params)
8{
9
10}
11
12void Fractal::set_palette(std::string colors)
13{
14 for (int i=0; i<256; i++)
15 {
16 std::string hexstr = colors.substr(i*6, 6);
17 palette.push_back(Color::fromHex(hexstr.c_str()));
18 }
19}
20
21void Fractal::add_transform(double weight, Matrix3x3 transform, double color, std::vector<Variation> variations)
22{
23 max += weight;
24 transforms[max] = Transform(transform, color, variations);
25}
26
27void Fractal::sample(double& x, double& y, double& c) const
28{
29 double rtran = (double)rand()/RAND_MAX*max;
30 const Transform& transform = transforms.upper_bound(rtran)->second;
31 Vector3D in = transform.transform * Vector3D(x, y, 1.0);
32 double r2 = pow(in.x,2.0)+pow(in.y,2.0);
33 double r = sqrt(r2);
34
35 double tx, ty;
36 x = 0.0;
37 y = 0.0;
38 for (auto& variation : transform.variations)
39 {
40 switch (variation.type)
41 {
42 case Variation::Type::linear:
43 {
44 tx = in.x;
45 ty = in.y;
46
47 break;
48 }
49
50 case Variation::Type::sinusoidal:
51 {
52 tx = sin(in.x);
53 ty = sin(in.y);
54
55 break;
56 }
57
58 case Variation::Type::spherical:
59 {
60 tx = in.x/r2;
61 ty = in.y/r2;
62
63 break;
64 }
65
66 case Variation::Type::eyefish:
67 {
68 double xp = 2.0/(r+1.0);
69 tx = in.x*xp;
70 ty = in.y*xp;
71
72 break;
73 }
74
75 case Variation::Type::fisheye:
76 {
77 double xp = 2.0/(r+1.0);
78 tx = in.y*xp;
79 ty = in.x*xp;
80
81 break;
82 }
83
84 case Variation::Type::bubble:
85 {
86 double xp = 4.0/(r2+4);
87 tx = in.x*xp;
88 ty = in.y*xp;
89
90 break;
91 }
92
93 case Variation::Type::cylinder:
94 {
95 tx = sin(in.x);
96 ty = in.y;
97
98 break;
99 }
100
101 case Variation::Type::noise:
102 {
103 double phi1 = (double)rand()/RAND_MAX;
104 double phi2 = (double)rand()/RAND_MAX;
105 tx = in.x * phi1 * cos(2*M_PI*phi2);
106 ty = in.y * phi1 * sin(2*M_PI*phi2);
107
108 break;
109 }
110
111 case Variation::Type::blur:
112 {
113 double phi1 = (double)rand()/RAND_MAX;
114 double phi2 = (double)rand()/RAND_MAX;
115 tx = phi1 * cos(2*M_PI*phi2);
116 ty = phi1 * sin(2*M_PI*phi2);
117
118 break;
119 }
120
121 case Variation::Type::horseshoe:
122 {
123 tx = (1.0/r)*((in.x-in.y)*(in.x+in.y));
124 ty = 2*in.x*in.y/r;
125
126 break;
127 }
128
129 case Variation::Type::swirl:
130 {
131 tx = in.x*sin(r2) - in.y*cos(r2);
132 ty = in.x*cos(r2) + in.y*sin(r2);
133
134 break;
135 }
136
137 case Variation::Type::julian:
138 {
139 double p1 = variation.params[0];
140 double p2 = variation.params[1];
141 double p3 = (double)(rand()%(int)floor(std::abs(p1)));
142 double t = (atan2(in.y, in.x) + 2*M_PI*p3) / p1;
143 double pw = pow(r2, p2 / p1 / 2.0);
144 tx = pw * cos(t);
145 ty = pw * sin(t);
146
147 break;
148 }
149
150 case Variation::Type::hyperbolic:
151 {
152 double theta = atan2(in.x, in.y);
153 tx = sin(theta)/r;
154 ty = r*cos(theta);
155
156 break;
157 }
158
159 case Variation::Type::polar:
160 {
161 double theta = atan2(in.x, in.y);
162 tx = theta/M_PI;
163 ty = r - 1;
164
165 break;
166 }
167
168 case Variation::Type::handkerchief:
169 {
170 double theta = atan2(in.x, in.y);
171 tx = r * sin(theta + r);
172 ty = r * cos(theta - r);
173
174 break;
175 }
176
177 case Variation::Type::heart:
178 {
179 double theta = atan2(in.x, in.y);
180 tx = r * sin(theta * r);
181 ty = -r * cos(theta * r);
182
183 break;
184 }
185
186 case Variation::Type::disc:
187 {
188 double theta = atan2(in.x, in.y);
189 tx = theta/M_PI*sin(r*M_PI);
190 ty = theta/M_PI*cos(r*M_PI);
191
192 break;
193 }
194
195 case Variation::Type::spiral:
196 {
197 double theta = atan2(in.x, in.y);
198 tx = 1.0/r * (cos(theta) + sin(r));
199 ty = 1.0/r * (sin(theta) - cos(r));
200
201 break;
202 }
203
204 case Variation::Type::diamond:
205 {
206 double theta = atan2(in.x, in.y);
207 tx = sin(theta)*cos(r);
208 ty = cos(theta)*sin(r);
209
210 break;
211 }
212
213 case Variation::Type::ex:
214 {
215 double theta = atan2(in.x, in.y);
216 double p0 = pow(sin(theta + r), 3.0);
217 double p1 = pow(cos(theta - r), 3.0);
218 tx = r * (p0 + p1);
219 ty = r * (p0 - p1);
220
221 break;
222 }
223
224 case Variation::Type::julia:
225 {
226 double theta = atan2(in.x, in.y);
227 double omega = (double)(rand()%2) * M_PI;
228 double sr = sqrt(r);
229 tx = sr * cos(theta/2.0 + omega);
230 ty = sr * sin(theta/2.0 + omega);
231
232 break;
233 }
234
235 case Variation::Type::bent:
236 {
237 if (in.x >= 0)
238 {
239 tx = in.x;
240 } else {
241 tx = 2*in.x;
242 }
243
244 if (in.y >= 0)
245 {
246 ty = in.y;
247 } else {
248 ty = in.y/2.0;
249 }
250
251 break;
252 }
253 }
254
255 x += tx * variation.weight;
256 y += ty * variation.weight;
257 }
258
259 c = (c + transform.color) * (1.0/2.0);
260}
261
262Color Fractal::get_color(double c) const
263{
264 int sc = std::min((int)floor(c * 256.0), 255);
265 return palette[sc];
266}
267
268LogScale::LogScale(double brightness, double quality)
269{
270 double contrast = 1.0;
271 double brightadjust = 2.3;
272 double white = 200.0;
273 k1 = contrast * (268.0 * brightadjust) * 100.0 * brightness / (256.0*256.0);
274 k2 = 1.0 / (contrast * white * quality);
275
276 for (int i=0; i<1024; i++)
277 {
278 memo.push_back(k1 * std::log(1+white*i*k2)/(std::log(10)*white*i));
279 }
280}
281
282double LogScale::log(double n) const
283{
284 int in = (int)floor(n);
285 if (in < 1024)
286 {
287 return memo[in];
288 } else {
289 double white = 200.0;
290 return k1 * std::log(1+white*n*k2)/(std::log(10)*white*n);
291 }
292}
293
294template <class Container>
295Container split(std::string input, std::string delimiter)
296{
297 Container result;
298
299 while (!input.empty())
300 {
301 int divider = input.find(delimiter);
302 if (divider == std::string::npos)
303 {
304 result.push_back(input);
305
306 input = "";
307 } else {
308 result.push_back(input.substr(0, divider));
309
310 input = input.substr(divider+delimiter.length());
311 }
312 }
313
314 return result;
315}
316
317int Fractal::load(const char* filename, Fractal& fractal)
318{
319 std::ifstream in(filename);
320 if(!in.is_open())
321 {
322 return -1;
323 }
324
325 in.close();
326
327 tinyxml2::XMLDocument doc;
328 doc.LoadFile(filename);
329 if(doc.Error())
330 {
331 doc.PrintError();
332 exit(1);
333 }
334
335 tinyxml2::XMLElement* root = doc.FirstChildElement( "flame" );
336 if( !root )
337 {
338 std::cerr << "Error: not a flame file!" << std::endl;
339 exit( 1 );
340 }
341
342 root->QueryDoubleAttribute("filter", &fractal.filterlevel);
343 root->QueryDoubleAttribute("gamma", &fractal.gamma);
344 root->QueryDoubleAttribute("gamma_threshold", &fractal.gammathresh);
345 root->QueryDoubleAttribute("brightness", &fractal.brightness);
346
347 const char* sizestr = root->Attribute("size");
348 sscanf(sizestr, "%lf %lf", &fractal.width, &fractal.height);
349
350 tinyxml2::XMLElement* elem = root->FirstChildElement();
351 while (elem)
352 {
353 std::string elementType (elem->Value());
354 if (elementType == "xform")
355 {
356 double weight;
357 double color;
358 Matrix3x3 transform;
359 elem->QueryDoubleAttribute("weight", &weight);
360 elem->QueryDoubleAttribute("color", &color);
361 std::string transstr(elem->Attribute("coefs"));
362 auto transvals = split<std::vector<std::string>>(transstr, " ");
363 transform(0,0) = std::stod(transvals[0]);
364 transform(0,1) = std::stod(transvals[2]);
365 transform(0,2) = std::stod(transvals[4]);
366 transform(1,0) = std::stod(transvals[1]);
367 transform(1,1) = std::stod(transvals[3]);
368 transform(1,2) = std::stod(transvals[5]);
369 transform(2,0) = 0.0;
370 transform(2,1) = 0.0;
371 transform(2,2) = 1.0;
372 std::vector<Variation> varies;
373 const char* varyval = 0;
374 if ((varyval = elem->Attribute("linear")) != 0)
375 {
376 varies.push_back(Variation(Variation::Type::linear, std::stod(std::string(varyval))));
377 }
378
379 if ((varyval = elem->Attribute("sinusoidal")) != 0)
380 {
381 varies.push_back(Variation(Variation::Type::sinusoidal, std::stod(std::string(varyval))));
382 }
383
384 if ((varyval = elem->Attribute("spherical")) != 0)
385 {
386 varies.push_back(Variation(Variation::Type::spherical, std::stod(std::string(varyval))));
387 }
388
389 if ((varyval = elem->Attribute("eyefish")) != 0)
390 {
391 varies.push_back(Variation(Variation::Type::eyefish, std::stod(std::string(varyval))));
392 }
393
394 if ((varyval = elem->Attribute("bubble")) != 0)
395 {
396 varies.push_back(Variation(Variation::Type::bubble, std::stod(std::string(varyval))));
397 }
398
399 if ((varyval = elem->Attribute("cylinder")) != 0)
400 {
401 varies.push_back(Variation(Variation::Type::cylinder, std::stod(std::string(varyval))));
402 }
403
404 if ((varyval = elem->Attribute("noise")) != 0)
405 {
406 varies.push_back(Variation(Variation::Type::noise, std::stod(std::string(varyval))));
407 }
408
409 if ((varyval = elem->Attribute("blur")) != 0)
410 {
411 varies.push_back(Variation(Variation::Type::blur, std::stod(std::string(varyval))));
412 }
413
414 if ((varyval = elem->Attribute("pre_blur")) != 0)
415 {
416 varies.push_back(Variation(Variation::Type::blur, std::stod(std::string(varyval))));
417 }
418
419 if ((varyval = elem->Attribute("horseshoe")) != 0)
420 {
421 varies.push_back(Variation(Variation::Type::horseshoe, std::stod(std::string(varyval))));
422 }
423
424 if ((varyval = elem->Attribute("swirl")) != 0)
425 {
426 varies.push_back(Variation(Variation::Type::swirl, std::stod(std::string(varyval))));
427 }
428
429 if ((varyval = elem->Attribute("hyperbolic")) != 0)
430 {
431 varies.push_back(Variation(Variation::Type::hyperbolic, std::stod(std::string(varyval))));
432 }
433
434 if ((varyval = elem->Attribute("julian")) != 0)
435 {
436 varies.push_back(Variation(Variation::Type::julian, std::stod(std::string(varyval)), {elem->DoubleAttribute("julian_power"), elem->DoubleAttribute("julian_dist")}));
437 }
438
439 if ((varyval = elem->Attribute("polar")) != 0)
440 {
441 varies.push_back(Variation(Variation::Type::polar, std::stod(std::string(varyval))));
442 }
443
444 if ((varyval = elem->Attribute("handkerchief")) != 0)
445 {
446 varies.push_back(Variation(Variation::Type::handkerchief, std::stod(std::string(varyval))));
447 }
448
449 if ((varyval = elem->Attribute("heart")) != 0)
450 {
451 varies.push_back(Variation(Variation::Type::heart, std::stod(std::string(varyval))));
452 }
453
454 if ((varyval = elem->Attribute("disc")) != 0)
455 {
456 varies.push_back(Variation(Variation::Type::disc, std::stod(std::string(varyval))));
457 }
458
459 if ((varyval = elem->Attribute("spiral")) != 0)
460 {
461 varies.push_back(Variation(Variation::Type::spiral, std::stod(std::string(varyval))));
462 }
463
464 if ((varyval = elem->Attribute("diamond")) != 0)
465 {
466 varies.push_back(Variation(Variation::Type::diamond, std::stod(std::string(varyval))));
467 }
468
469 if ((varyval = elem->Attribute("ex")) != 0)
470 {
471 varies.push_back(Variation(Variation::Type::ex, std::stod(std::string(varyval))));
472 }
473
474 if ((varyval = elem->Attribute("julia")) != 0)
475 {
476 varies.push_back(Variation(Variation::Type::julia, std::stod(std::string(varyval))));
477 }
478
479 if ((varyval = elem->Attribute("bent")) != 0)
480 {
481 varies.push_back(Variation(Variation::Type::bent, std::stod(std::string(varyval))));
482 }
483
484 if ((varyval = elem->Attribute("fisheye")) != 0)
485 {
486 varies.push_back(Variation(Variation::Type::fisheye, std::stod(std::string(varyval))));
487 }
488
489 fractal.add_transform(weight, transform, color, varies);
490 } else if (elementType == "palette")
491 {
492 std::string val(elem->GetText());
493 for (int i=0; i<val.length(); i++)
494 {
495 if (val[i] == ' ' || val[i] == '\n')
496 {
497 val.erase(i, 1);
498 i--;
499 }
500 }
501
502 fractal.set_palette(val);
503 }
504
505 elem = elem->NextSiblingElement();
506 }
507
508 return 0;
509}
510
511Fractal Fractal::random()
512{
513 Fractal fractal;
514
515 int xforms = rand() % 2 + 2;
516 double remweight = 1.0;
517 for (int i=0; i<xforms; i++)
518 {
519 double weight;
520 if (i == (xforms-1))
521 {
522 weight = remweight;
523 } else {
524 weight = ((double)rand()/RAND_MAX)*remweight;
525 remweight -= weight;
526 }
527
528 Triangle gentri(
529 ((double)rand()/RAND_MAX)*2-1,
530 ((double)rand()/RAND_MAX)*2-1,
531 ((double)rand()/RAND_MAX)*2-1,
532 ((double)rand()/RAND_MAX)*2-1,
533 ((double)rand()/RAND_MAX)*2-1,
534 ((double)rand()/RAND_MAX)*2-1
535 );
536 Matrix3x3 affine = affineTransform(Triangle(0, 0, 1, 0, 0, 1), gentri);
537 double color = (double)i/(xforms-1);
538 std::vector<Variation> variations;
539
540 double remaffix = 1.0;
541 for (int j=0; j<2; j++)
542 {
543 double affix;
544 if (j == 1)
545 {
546 affix = remaffix;
547 } else {
548 affix = ((double)rand()/RAND_MAX)*remaffix;
549 remaffix -= affix;
550 }
551
552 Variation::Type type;
553 switch (rand()%16)
554 {
555 case 0: type = Variation::Type::linear; break;
556 case 1: type = Variation::Type::sinusoidal; break;
557 case 2: type = Variation::Type::spherical; break;
558 case 3: type = Variation::Type::eyefish; break;
559 case 4: type = Variation::Type::bubble; break;
560 case 5: type = Variation::Type::cylinder; break;
561 case 6: type = Variation::Type::blur; break;
562 case 7: type = Variation::Type::horseshoe; break;
563 case 8: type = Variation::Type::swirl; break;
564 case 9: type = Variation::Type::hyperbolic; break;
565 case 10: type = Variation::Type::polar; break;
566 case 11: type = Variation::Type::handkerchief; break;
567 case 12: type = Variation::Type::heart; break;
568 case 13: type = Variation::Type::disc; break;
569 case 14: type = Variation::Type::spiral; break;
570 case 15: type = Variation::Type::diamond; break;
571 }
572
573 variations.push_back(Variation(type, affix));
574 }
575
576 fractal.add_transform(weight, affine, color, variations);
577 }
578
579 std::vector<std::string> colors;
580 std::ifstream colorfile("colors.txt");
581 if (!colorfile.is_open())
582 {
583 std::cout << "Could not find colors.txt" << std::endl;
584 exit(-1);
585 }
586
587 std::string line;
588 while (getline(colorfile, line))
589 {
590 if (line.back() == '\r')
591 {
592 line.pop_back();
593 }
594
595 colors.push_back(line);
596 }
597
598 colorfile.close();
599 fractal.set_palette(colors[rand() % colors.size()]);
600
601 return fractal;
602}