libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / apps / mandelbrot / FractalEngine.cpp
blobe0387f5c941bf4489eae7a0f956d270ea4e09316
1 /*
2 * Copyright 2016, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
5 * Authors:
6 * Augustin Cavalier <waddlesplash>
7 * kerwizzy
8 */
9 #include "FractalEngine.h"
11 #include <algorithm>
12 #include <math.h>
14 #include <Bitmap.h>
15 #include <String.h>
17 #include "Colorsets.h"
20 FractalEngine::FractalEngine(BHandler* parent, BLooper* looper)
22 BLooper("FractalEngine"),
23 fMessenger(parent, looper),
24 fBitmapStandby(NULL),
25 fBitmapDisplay(NULL),
26 fIterations(1024),
27 fWidth(0), fHeight(0),
28 fRenderBuffer(NULL),
29 fRenderBufferLen(0),
30 fColorset(Colorset_Royal)
32 fDoSet = &FractalEngine::DoSet_Mandelbrot;
34 fRenderSem = create_sem(0, "RenderSem");
35 fRenderFinishedSem = create_sem(0, "RenderFinishedSem");
36 system_info info;
37 get_system_info(&info);
38 fThreadCount = info.cpu_count;
39 if (fThreadCount >= 4)
40 fThreadCount = 4;
41 for (uint8 i = 0; i < fThreadCount; i++) {
42 fRenderThreads[i] = spawn_thread(&FractalEngine::RenderThread,
43 BString().SetToFormat("RenderThread%d", i).String(),
44 B_NORMAL_PRIORITY, this);
45 resume_thread(fRenderThreads[i]);
50 FractalEngine::~FractalEngine()
55 void FractalEngine::MessageReceived(BMessage* msg)
57 switch (msg->what) {
58 case MSG_CHANGE_SET:
59 switch (msg->GetUInt8("set", 0)) {
60 case 0: fDoSet = &FractalEngine::DoSet_Mandelbrot; break;
61 case 1: fDoSet = &FractalEngine::DoSet_BurningShip; break;
62 case 2: fDoSet = &FractalEngine::DoSet_Tricorn; break;
63 case 3: fDoSet = &FractalEngine::DoSet_Julia; break;
64 case 4: fDoSet = &FractalEngine::DoSet_OrbitTrap; break;
65 case 5: fDoSet = &FractalEngine::DoSet_Multibrot; break;
67 break;
68 case MSG_SET_PALETTE:
69 switch (msg->GetUInt8("palette", 0)) {
70 case 0: fColorset = Colorset_Royal; break;
71 case 1: fColorset = Colorset_DeepFrost; break;
72 case 2: fColorset = Colorset_Frost; break;
73 case 3: fColorset = Colorset_Fire; break;
74 case 4: fColorset = Colorset_Midnight; break;
75 case 5: fColorset = Colorset_Grassland; break;
76 case 6: fColorset = Colorset_Lightning; break;
77 case 7: fColorset = Colorset_Spring; break;
78 case 8: fColorset = Colorset_HighContrast; break;
80 break;
81 case MSG_SET_ITERATIONS:
82 fIterations = msg->GetUInt16("iterations", 0);
83 break;
85 case MSG_RESIZE: {
86 delete fBitmapStandby;
87 // We don't delete the "display" bitmap; the viewer now owns it
88 delete fRenderBuffer;
90 fWidth = msg->GetUInt16("width", 320);
91 fHeight = msg->GetUInt16("height", 240);
92 BRect rect(0, 0, fWidth - 1, fHeight - 1);
93 fBitmapStandby = new BBitmap(rect, B_RGB24);
94 fBitmapDisplay = new BBitmap(rect, B_RGB24);
95 fRenderBufferLen = fWidth * fHeight * 3;
96 fRenderBuffer = new uint8[fRenderBufferLen];
97 break;
99 case MSG_RENDER: {
100 // Render to "standby" bitmap
101 Render(msg->GetDouble("locationX", 0), msg->GetDouble("locationY", 0),
102 msg->GetDouble("size", 0.005));
103 BMessage message(MSG_RENDER_COMPLETE);
104 message.AddPointer("bitmap", (const void*)fBitmapStandby);
105 fMessenger.SendMessage(&message);
106 std::swap(fBitmapStandby, fBitmapDisplay);
107 break;
110 default:
111 BLooper::MessageReceived(msg);
112 break;
117 void FractalEngine::Render(double locationX, double locationY, double size)
119 fLocationX = locationX;
120 fLocationY = locationY;
121 fSize = size;
122 for (uint8 i = 0; i < fThreadCount; i++)
123 release_sem(fRenderSem);
124 for (uint8 i = 0; i < fThreadCount; i++)
125 acquire_sem(fRenderFinishedSem);
127 fBitmapStandby->ImportBits(fRenderBuffer, fRenderBufferLen, fWidth * 3,
128 0, B_RGB24_BIG);
132 status_t FractalEngine::RenderThread(void* data)
134 FractalEngine* engine = static_cast<FractalEngine*>(data);
135 thread_id self = find_thread(NULL);
136 uint8 threadNum = 0;
137 for (uint8 i = 0; i < engine->fThreadCount; i++) {
138 if (engine->fRenderThreads[i] == self) {
139 threadNum = i;
140 break;
144 while (true) {
145 acquire_sem(engine->fRenderSem);
147 uint16 halfWidth = engine->fWidth / 2;
148 uint16 halfHeight = engine->fHeight / 2;
149 const uint32 startY = (engine->fHeight / engine->fThreadCount) * threadNum,
150 endY = (engine->fHeight / engine->fThreadCount) * (threadNum + 1);
151 for (uint32 x = 0; x < engine->fWidth; x++) {
152 for (uint32 y = startY; y < endY; y++) {
153 engine->RenderPixel(x, y,
154 (x * engine->fSize + engine->fLocationX) - (halfWidth * engine->fSize),
155 (y * -(engine->fSize) + engine->fLocationY) - (halfHeight * -(engine->fSize)));
159 release_sem(engine->fRenderFinishedSem);
161 return B_OK;
165 void FractalEngine::RenderPixel(uint32 x, uint32 y, double real,
166 double imaginary)
168 int32 iterToEscape = (this->*fDoSet)(real, imaginary);
169 uint16 loc = 0;
170 if (iterToEscape == -1) {
171 // Didn't escape.
172 loc = 999;
173 } else {
174 loc = 998 - (iterToEscape % 999);
177 uint32 offsetBase = fWidth * y * 3 + x * 3;
178 loc *= 3;
179 fRenderBuffer[offsetBase + 0] = fColorset[loc + 0];
180 fRenderBuffer[offsetBase + 1] = fColorset[loc + 1];
181 fRenderBuffer[offsetBase + 2] = fColorset[loc + 2];
185 // Magic numbers & other general constants
186 const double gJuliaA = 0;
187 const double gJuliaB = 1;
189 const uint8 gEscapeHorizon = 4;
192 int32 FractalEngine::DoSet_Mandelbrot(double real, double imaginary)
194 double zReal = 0;
195 double zImaginary = 0;
197 for (int32 i = 0; i < fIterations; i++) {
198 double zRealSq = zReal * zReal;
199 double zImaginarySq = zImaginary * zImaginary;
200 double nzReal = (zRealSq + (-1 * zImaginarySq));
202 zImaginary = 2 * (zReal * zImaginary);
203 zReal = nzReal;
205 zReal += real;
206 zImaginary += imaginary;
208 // If it is outside the 2 unit circle...
209 if ((zRealSq + zImaginarySq) > gEscapeHorizon) {
210 return i; // stop it from running longer
213 return -1;
217 int32 FractalEngine::DoSet_BurningShip(double real, double imaginary)
219 double zReal = 0;
220 double zImaginary = 0;
222 // It looks "upside down" otherwise.
223 imaginary = -imaginary;
225 for (int32 i = 0; i < fIterations; i++) {
226 zReal = fabs(zReal);
227 zImaginary = fabs(zImaginary);
229 double zRealSq = zReal * zReal;
230 double zImaginarySq = zImaginary * zImaginary;
231 double nzReal = (zRealSq + (-1 * zImaginarySq));
233 zImaginary = 2 * (zReal * zImaginary);
234 zReal = nzReal;
236 zReal += real;
237 zImaginary += imaginary;
239 // If it is outside the 2 unit circle...
240 if ((zRealSq + zImaginarySq) > gEscapeHorizon) {
241 return i; // stop it from running longer
244 return -1;
248 int32 FractalEngine::DoSet_Tricorn(double real, double imaginary)
250 double zReal = 0;
251 double zImaginary = 0;
253 real = -real;
255 for (int32 i = 0; i < fIterations; i++) {
256 double znRe = zImaginary * -1;
257 zImaginary = zReal * -1;
258 zReal = znRe; // Swap the real and complex parts each time.
260 double zRealSq = zReal * zReal;
261 double zImaginarySq = zImaginary * zImaginary;
262 double nzReal = (zRealSq + (-1 * zImaginarySq));
264 zImaginary = 2 * (zReal * zImaginary);
265 zReal = nzReal;
267 zReal += real;
268 zImaginary += imaginary;
270 // If it is outside the 2 unit circle...
271 if ((zRealSq + zImaginarySq) > gEscapeHorizon) {
272 return i; // stop it from running longer
275 return -1;
279 int32 FractalEngine::DoSet_Julia(double real, double imaginary)
281 double zReal = real;
282 double zImaginary = imaginary;
284 double muRe = gJuliaA;
285 double muIm = gJuliaB;
287 for (int32 i = 0; i < fIterations; i++) {
288 double zRealSq = zReal * zReal;
289 double zImaginarySq = zImaginary * zImaginary;
290 double nzReal = (zRealSq + (-1 * (zImaginarySq)));
292 zImaginary = 2 * (zReal * zImaginary);
293 zReal = nzReal;
295 zReal += muRe;
296 zImaginary += muIm;
298 // If it is outside the 2 unit circle...
299 if ((zRealSq + zImaginarySq) > gEscapeHorizon) {
300 return i; // stop it from running longer
303 return -1;
307 int32 FractalEngine::DoSet_OrbitTrap(double real, double imaginary)
309 double zReal = 0;
310 double zImaginary = 0;
312 double closest = 10000000;
313 double distance = 0;
314 double lineDist = 0;
316 for (int32 i = 0; i < fIterations; i++) {
317 double zRealSq = zReal * zReal;
318 double zImaginarySq = zImaginary * zImaginary;
319 double nzReal = (zRealSq + (-1 * zImaginarySq));
321 zImaginary = 2 * (zReal * zImaginary);
322 zReal = nzReal;
324 zReal += real;
325 zImaginary += imaginary;
327 distance = sqrt(zRealSq + zImaginarySq);
328 lineDist = fabs(zReal + zImaginary);
330 // If it is closer than ever before...
331 if (lineDist < closest)
332 closest = lineDist;
334 if (distance > gEscapeHorizon) {
335 return static_cast<int32>(floor(4 * log(4 / closest)));
338 return static_cast<int32>(floor(4 * log(4 / closest)));
342 int32 FractalEngine::DoSet_Multibrot(double real, double imaginary)
344 double zReal = 0;
345 double zImaginary = 0;
347 for (int32 i = 0; i < fIterations; i++) {
348 double zRealSq = zReal * zReal;
349 double zImaginarySq = zImaginary * zImaginary;
350 double nzReal = (zRealSq * zReal - 3 * zReal * zImaginarySq);
352 zImaginary = 3 * (zRealSq * zImaginary) - (zImaginarySq * zImaginary);
354 zReal = nzReal;
355 zReal += real;
356 zImaginary += imaginary;
358 // If it is outside the 2 unit circle...
359 if ((zRealSq + zImaginarySq) > gEscapeHorizon) {
360 return i; // stop it from running longer
363 return -1;