xref: /haiku/src/add-ons/screen_savers/spider/SpiderSaver.cpp (revision e6b30aee0fd7a23d6a6baab9f3718945a0cd838a)
1 /*
2  * Copyright 2007, Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  */
8 #include "SpiderSaver.h"
9 
10 #include <math.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <Bitmap.h>
16 #include <Message.h>
17 #include <Menu.h>
18 #include <MenuField.h>
19 #include <MenuItem.h>
20 #include <Slider.h>
21 #include <StringView.h>
22 
23 #include "Polygon.h"
24 #include "PolygonQueue.h"
25 
26 enum {
27 	MSG_QUEUE_NUMBER			= 'qunm',
28 	MSG_POLY_NUMBER				= 'plnm',
29 	MSG_QUEUE_DEPTH				= 'qudp',
30 	MSG_COLOR					= 'colr',
31 };
32 
33 #define MIN_POLY_POINTS 3
34 #define MAX_POLY_POINTS 10
35 #define MIN_QUEUE_DEPTH 40
36 #define MAX_QUEUE_DEPTH 160
37 #define MAX_QUEUE_NUMBER 40
38 
39 enum {
40 	RED							= 1,
41 	GREEN						= 2,
42 	BLUE						= 3,
43 	YELLOW						= 4,
44 	PURPLE						= 5,
45 	CYAN						= 6,
46 	GRAY						= 7,
47 };
48 
49 // MAIN INSTANTIATION FUNCTION
50 extern "C" _EXPORT BScreenSaver*
51 instantiate_screen_saver(BMessage *message, image_id image)
52 {
53 	return new SpiderSaver(message, image);
54 }
55 
56 // constructor
57 SpiderSaver::SpiderSaver(BMessage *message, image_id id)
58 	: BScreenSaver(message, id),
59 	  fBackBitmap(NULL),
60 	  fBackView(NULL),
61 	  fQueues(new PolygonQueue*[MAX_QUEUE_NUMBER]),
62 	  fQueueNumber(20),
63 	  fMaxPolyPoints(MAX_POLY_POINTS),
64 	  fMaxQueueDepth(MAX_QUEUE_DEPTH),
65 	  fColor(RED)
66 {
67 	for (int32 i = 0; i < MAX_QUEUE_NUMBER; i++)
68 		fQueues[i] = NULL;
69 	if (message) {
70 		int32 value;
71 		if (message->FindInt32("queue number", &value) == B_OK)
72 			fQueueNumber = value;
73 		if (message->FindInt32("poly points", &value) == B_OK)
74 			fMaxPolyPoints = value;
75 		if (message->FindInt32("queue depth", &value) == B_OK)
76 			fMaxQueueDepth = value;
77 		if (message->FindInt32("color", &value) == B_OK)
78 			fColor = value;
79 	}
80 	srand48((long int)system_time());
81 }
82 
83 // destructor
84 SpiderSaver::~SpiderSaver()
85 {
86 	_Cleanup();
87 	delete[] fQueues;
88 }
89 
90 // StartConfig
91 void
92 SpiderSaver::StartConfig(BView *view)
93 {
94 	SpiderView* configView = new SpiderView(view->Bounds(), this,
95 											fQueueNumber, fMaxPolyPoints,
96 											fMaxQueueDepth, fColor);
97 	view->AddChild(configView);
98 }
99 
100 // StartSaver
101 status_t
102 SpiderSaver::StartSaver(BView *v, bool preview)
103 {
104 	SetTickSize(50000);
105 
106 	fPreview = preview;
107 	fBounds = v->Bounds();
108 	_Init(fBounds);
109 
110 	return B_OK;
111 }
112 
113 // StopSaver
114 void
115 SpiderSaver::StopSaver()
116 {
117 	_Cleanup();
118 }
119 
120 // Draw
121 void
122 SpiderSaver::Draw(BView *view, int32 frame)
123 {
124 	fLocker.Lock();
125 	for (uint32 i = 0; i < fQueueNumber; i++) {
126 		if (fQueues[i])
127 			fQueues[i]->Step();
128 	}
129 	if (fBackView) {
130 		if (fBackBitmap->Lock()) {
131 			_DrawInto(fBackView);
132 			fBackView->Sync();
133 			fBackBitmap->Unlock();
134 		}
135 		view->DrawBitmap(fBackBitmap, BPoint(0.0, 0.0));
136 	}
137 	fLocker.Unlock();
138 }
139 
140 // SaveState
141 status_t
142 SpiderSaver::SaveState(BMessage* into) const
143 {
144 	if (into) {
145 		into->AddInt32("queue number", (int32)fQueueNumber);
146 		into->AddInt32("poly points", (int32)fMaxPolyPoints);
147 		into->AddInt32("queue depth", (int32)fMaxQueueDepth);
148 		into->AddInt32("color", (int32)fColor);
149 		return B_OK;
150 	}
151 	return B_BAD_VALUE;
152 }
153 
154 // SetQueueNumber
155 void
156 SpiderSaver::SetQueueNumber(uint32 number)
157 {
158 	fLocker.Lock();
159 	_Cleanup();
160 	fQueueNumber = number;
161 	_Init(fBounds);
162 	fLocker.Unlock();
163 }
164 
165 // SetQueueDepth
166 void
167 SpiderSaver::SetQueueDepth(uint32 maxDepth)
168 {
169 	fLocker.Lock();
170 	_Cleanup();
171 	fMaxQueueDepth = maxDepth;
172 	_Init(fBounds);
173 	fLocker.Unlock();
174 }
175 
176 // SetPolyPoints
177 void
178 SpiderSaver::SetPolyPoints(uint32 maxPoints)
179 {
180 	fLocker.Lock();
181 	_Cleanup();
182 	fMaxPolyPoints = maxPoints;
183 	_Init(fBounds);
184 	fLocker.Unlock();
185 }
186 
187 // SetColor
188 void
189 SpiderSaver::SetColor(uint32 color)
190 {
191 	fLocker.Lock();
192 	_Cleanup();
193 	fColor = color;
194 	_Init(fBounds);
195 	fLocker.Unlock();
196 }
197 
198 // _Init
199 void
200 SpiderSaver::_Init(BRect bounds)
201 {
202 	_AllocBackBitmap(bounds.Width(), bounds.Height());
203 	uint32 minPoints = fMaxPolyPoints / 2;
204 	uint32 maxPoints = fMaxPolyPoints;
205 	uint32 minQueueDepth = fMaxQueueDepth / 2;
206 	uint32 maxQueueDepth = fMaxQueueDepth;
207 	if (fPreview) {
208 		minQueueDepth /= 4;
209 		maxQueueDepth /= 4;
210 	}
211 	for (uint32 i = 0; i < fQueueNumber; i++)
212 		fQueues[i] = new PolygonQueue(new Polygon(bounds, minPoints
213 															+ lrand48()
214 															% (maxPoints
215 															- minPoints)),
216 									  minQueueDepth + lrand48() % (maxQueueDepth
217 									  - minQueueDepth));
218 }
219 
220 // _Cleanup
221 void
222 SpiderSaver::_Cleanup()
223 {
224 	_FreeBackBitmap();
225 	for (int32 i = 0; i < MAX_QUEUE_NUMBER; i++) {
226 		delete fQueues[i];
227 		fQueues[i] = NULL;
228 	}
229 }
230 
231 // _AllocBackBitmap
232 void
233 SpiderSaver::_AllocBackBitmap(float width, float height)
234 {
235 	// sanity check
236 	if (width <= 0.0 || height <= 0.0)
237 		return;
238 
239 	BRect b(0.0, 0.0, width, height);
240 	fBackBitmap = new BBitmap(b, B_RGB32, true);
241 	if (!fBackBitmap)
242 		return;
243 	if (fBackBitmap->IsValid()) {
244 		fBackView = new BView(b, 0, B_FOLLOW_NONE, B_WILL_DRAW);
245 		fBackBitmap->AddChild(fBackView);
246 		memset(fBackBitmap->Bits(), 0, fBackBitmap->BitsLength());
247 	} else {
248 		_FreeBackBitmap();
249 		fprintf(stderr, "SpiderSaver::_AllocBackBitmap(): bitmap invalid\n");
250 	}
251 }
252 
253 // _FreeBackBitmap
254 void
255 SpiderSaver::_FreeBackBitmap()
256 {
257 	if (fBackBitmap) {
258 		delete fBackBitmap;
259 		fBackBitmap = NULL;
260 		fBackView = NULL;
261 	}
262 }
263 
264 // _DrawInto
265 void
266 SpiderSaver::_DrawInto(BView *view)
267 {
268 	for (uint32 i = 0; i < fQueueNumber; i++) {
269 		switch (fColor) {
270 			case GREEN:
271 				view->SetHighColor(1, 2, 1, 255);
272 				break;
273 			case BLUE:
274 				view->SetHighColor(1, 1, 2, 255);
275 				break;
276 			case YELLOW:
277 				view->SetHighColor(2, 2, 1, 255);
278 				break;
279 			case PURPLE:
280 				view->SetHighColor(2, 1, 2, 255);
281 				break;
282 			case CYAN:
283 				view->SetHighColor(1, 2, 2, 255);
284 				break;
285 			case GRAY:
286 				view->SetHighColor(2, 2, 2, 255);
287 				break;
288 			case RED:
289 			default:
290 				view->SetHighColor(2, 1, 1, 255);
291 				break;
292 		}
293 		if (Polygon* p = fQueues[i]->Head()) {
294 			view->SetDrawingMode(B_OP_ADD);
295 			_DrawPolygon(p, view);
296 		}
297 		if (Polygon* p = fQueues[i]->Tail()) {
298 			view->SetDrawingMode(B_OP_SUBTRACT);
299 			_DrawPolygon(p, view);
300 		}
301 	}
302 }
303 
304 // _DrawPolygon
305 void
306 SpiderSaver::_DrawPolygon(Polygon* polygon, BView *view)
307 {
308 	int32 pointCount = polygon->CountPoints();
309 	if (pointCount > 1) {
310 		BPoint p = polygon->PointAt(0);
311 		view->MovePenTo(p);
312 		for (int32 i = 1; i < pointCount; i++)
313 			view->StrokeLine(polygon->PointAt(i));
314 		view->StrokeLine(p);
315 	}
316 }
317 
318 // constructor
319 SpiderView::SpiderView(BRect frame, SpiderSaver* saver,
320 					   uint32 queueNumber, uint32 maxPolyPoints,
321 					   uint32 maxQueueDepth, uint32 color)
322 	: BView(frame, "spider view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED),
323 	  fSaver(saver)
324 {
325 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
326 
327 	frame.OffsetTo(0.0, 0.0);
328 	frame.InsetBy(10.0, 5.0);
329 
330 	float viewHeight = floorf(frame.Height() / 5.0);
331 
332 	// title stuff
333 	font_height fh;
334 	be_bold_font->GetHeight(&fh);
335 	float fontHeight = fh.ascent + fh.descent + 5.0;
336 	frame.bottom = frame.top + fontHeight;
337 	BStringView* title = new BStringView(frame, B_EMPTY_STRING, "Spider by stippi");
338 	title->SetFont(be_bold_font);
339 	AddChild(title);
340 
341 	be_plain_font->GetHeight(&fh);
342 	fontHeight = fh.ascent + fh.descent + 5.0;
343 	frame.top = frame.bottom;
344 	frame.bottom = frame.top + fontHeight;
345 	title = new BStringView(frame, B_EMPTY_STRING, "for  bonefish");
346 	BFont font(be_plain_font);
347 	font.SetShear(110.0);
348 	title->SetFont(&font);
349 	title->SetAlignment(B_ALIGN_CENTER);
350 	AddChild(title);
351 
352 	// controls
353 	frame.top = 10.0;
354 	frame.bottom = frame.top + viewHeight;
355 	frame.OffsetBy(0.0, viewHeight);
356 	fQueueNumberS = new BSlider(frame, "queue number", "Max Polygon Count",
357 								new BMessage(MSG_QUEUE_NUMBER),
358 								1, MAX_QUEUE_NUMBER);
359 	fQueueNumberS->SetHashMarks(B_HASH_MARKS_BOTTOM);
360 	fQueueNumberS->SetHashMarkCount((MAX_QUEUE_NUMBER - 1) / 2 + 1);
361 	fQueueNumberS->SetValue(queueNumber);
362 	AddChild(fQueueNumberS);
363 	frame.OffsetBy(0.0, viewHeight);
364 	fPolyNumberS = new BSlider(frame, "poly points", "Max Points per Polygon",
365 								new BMessage(MSG_POLY_NUMBER),
366 								MIN_POLY_POINTS, MAX_POLY_POINTS);
367 	fPolyNumberS->SetHashMarks(B_HASH_MARKS_BOTTOM);
368 	fPolyNumberS->SetHashMarkCount(MAX_POLY_POINTS - MIN_POLY_POINTS + 1);
369 	fPolyNumberS->SetValue(maxPolyPoints);
370 	AddChild(fPolyNumberS);
371 	frame.OffsetBy(0.0, viewHeight);
372 	fQueueDepthS = new BSlider(frame, "queue depth", "Trail Depth",
373 								new BMessage(MSG_QUEUE_DEPTH),
374 								MIN_QUEUE_DEPTH, MAX_QUEUE_DEPTH);
375 	fQueueDepthS->SetHashMarks(B_HASH_MARKS_BOTTOM);
376 	fQueueDepthS->SetHashMarkCount((MAX_QUEUE_DEPTH - MIN_QUEUE_DEPTH) / 4 + 1);
377 	fQueueDepthS->SetValue(maxQueueDepth);
378 	AddChild(fQueueDepthS);
379 
380 	BMenu* menu = new BMenu("Color");
381 	BMessage* message = new BMessage(MSG_COLOR);
382 	message->AddInt32("color", RED);
383 	BMenuItem* item = new BMenuItem("Red", message);
384 	if (color == RED)
385 		item->SetMarked(true);
386 	menu->AddItem(item);
387 	message = new BMessage(MSG_COLOR);
388 	message->AddInt32("color", GREEN);
389 	item = new BMenuItem("Green", message);
390 	if (color == GREEN)
391 		item->SetMarked(true);
392 	menu->AddItem(item);
393 	message = new BMessage(MSG_COLOR);
394 	message->AddInt32("color", BLUE);
395 	item = new BMenuItem("Blue", message);
396 	if (color == BLUE)
397 		item->SetMarked(true);
398 	menu->AddItem(item);
399 	message = new BMessage(MSG_COLOR);
400 	message->AddInt32("color", YELLOW);
401 	item = new BMenuItem("Yellow", message);
402 	if (color == YELLOW)
403 		item->SetMarked(true);
404 	menu->AddItem(item);
405 	message = new BMessage(MSG_COLOR);
406 	message->AddInt32("color", PURPLE);
407 	item = new BMenuItem("Purple", message);
408 	if (color == PURPLE)
409 		item->SetMarked(true);
410 	menu->AddItem(item);
411 	message = new BMessage(MSG_COLOR);
412 	message->AddInt32("color", CYAN);
413 	item = new BMenuItem("Cyan", message);
414 	if (color == CYAN)
415 		item->SetMarked(true);
416 	menu->AddItem(item);
417 	message = new BMessage(MSG_COLOR);
418 	message->AddInt32("color", GRAY);
419 	item = new BMenuItem("Gray", message);
420 	if (color == GRAY)
421 		item->SetMarked(true);
422 	menu->AddItem(item);
423 
424 	menu->SetLabelFromMarked(true);
425 	menu->SetRadioMode(true);
426 
427 	frame.OffsetBy(0.0, viewHeight);
428 	fColorMF = new BMenuField(frame, "color", "Color", menu);
429 	fColorMF->SetDivider(fColorMF->StringWidth("Color") + 5.0);
430 	AddChild(fColorMF);
431 }
432 
433 // destructor
434 SpiderView::~SpiderView()
435 {
436 }
437 
438 // AttachedToWindow
439 void
440 SpiderView::AttachedToWindow()
441 {
442 	fQueueNumberS->SetTarget(this);
443 	fPolyNumberS->SetTarget(this);
444 	fQueueDepthS->SetTarget(this);
445 	fColorMF->Menu()->SetTargetForItems(this);
446 }
447 
448 // MessageReceived
449 void
450 SpiderView::MessageReceived(BMessage* message)
451 {
452 	switch (message->what) {
453 		case MSG_QUEUE_NUMBER:
454 			fSaver->SetQueueNumber(fQueueNumberS->Value());
455 			break;
456 		case MSG_POLY_NUMBER:
457 			fSaver->SetPolyPoints(fPolyNumberS->Value());
458 			break;
459 		case MSG_QUEUE_DEPTH:
460 			fSaver->SetQueueDepth(fQueueDepthS->Value());
461 			break;
462 		case MSG_COLOR: {
463 			uint32 color;
464 			if (message->FindInt32("color", (int32*)&color) == B_OK)
465 				fSaver->SetColor(color);
466 			break;
467 		}
468 		default:
469 			BView::MessageReceived(message);
470 			break;
471 	}
472 }
473 
474