diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2016-05-02 22:57:13 -0400 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2016-05-02 22:57:13 -0400 |
commit | 330f75e663c22e1198a92fd134865ada98c3957b (patch) | |
tree | b78f8aa762fcc93c14a0905546b694cddaaa3041 /fractal.cpp | |
download | infinite-330f75e663c22e1198a92fd134865ada98c3957b.tar.gz infinite-330f75e663c22e1198a92fd134865ada98c3957b.tar.bz2 infinite-330f75e663c22e1198a92fd134865ada98c3957b.zip |
Initial commit
Diffstat (limited to 'fractal.cpp')
-rw-r--r-- | fractal.cpp | 602 |
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 | |||
7 | Variation::Variation(Type _type, double _weight, std::vector<double> _params) : type(_type), weight(_weight), params(_params) | ||
8 | { | ||
9 | |||
10 | } | ||
11 | |||
12 | void 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 | |||
21 | void 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 | |||
27 | void 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 | |||
262 | Color Fractal::get_color(double c) const | ||
263 | { | ||
264 | int sc = std::min((int)floor(c * 256.0), 255); | ||
265 | return palette[sc]; | ||
266 | } | ||
267 | |||
268 | LogScale::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 | |||
282 | double 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 | |||
294 | template <class Container> | ||
295 | Container 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 | |||
317 | int 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 | |||
511 | Fractal 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 | } | ||