xref: /haiku/src/tests/kits/game/chart/ChartWindow.cpp (revision 204dee708a999d5a71d0cb9497650ee7cef85d0a)
1 /*
2 
3 	ChartWindow.cpp
4 
5 	by Pierre Raynaud-Richard.
6 
7 	Copyright 1998 Be Incorporated, All Rights Reserved.
8 
9 */
10 
11 #include "ChartWindow.h"
12 
13 #include <AppFileInfo.h>
14 #include <Application.h>
15 #include <Bitmap.h>
16 #include <Box.h>
17 #include <Button.h>
18 #include <ByteOrder.h>
19 #include <Catalog.h>
20 #include <CheckBox.h>
21 #include <Directory.h>
22 #include <Entry.h>
23 #include <File.h>
24 #include <FindDirectory.h>
25 #include <Menu.h>
26 #include <MenuBar.h>
27 #include <MenuField.h>
28 #include <MenuItem.h>
29 #include <Path.h>
30 #include <PlaySound.h>
31 #include <PopUpMenu.h>
32 #include <RadioButton.h>
33 #include <Screen.h>
34 #include <Slider.h>
35 #include <TextControl.h>
36 
37 #include <math.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 
42 #undef B_TRANSLATION_CONTEXT
43 #define B_TRANSLATION_CONTEXT "ChartWindow"
44 
45 /* pseudo-random generator parameters (not very good ones,
46    but good enough for what we do here). */
47 enum {
48 	CRC_START		= 0x29dec231,
49 	CRC_KEY			= 0x1789feb3
50 };
51 
52 #define	MAX_FONT_SIZE	12.0f
53 
54 /* various offset, width, height and position used to align and
55    set the various UI elements. */
56 enum {
57 	TOP_LEFT_LIMIT	= 26,
58 	H_BORDER		= 5,
59 	V_BORDER		= 2,
60 	ANIM_LABEL		= 52,
61 	ANIM_POPUP		= 42,
62 	DISP_LABEL		= 40,
63 	DISP_POPUP		= 42,
64 	BUTTON_WIDTH	= 50,
65 	BUTTON_OFFSET	= -100,
66 	SPACE_LABEL		= 40,
67 	SPACE_POPUP		= 53,
68 	INSTANT_LOAD	= 205,
69 	LEFT_WIDTH		= 96,
70 	LEFT_OFFSET		= 2,
71 	STATUS_BOX		= 98,
72 	STATUS_LABEL	= 13,
73 	STATUS_EDIT		= 25,
74 	STATUS_OFFSET	= 2,
75 	BOX_H_OFFSET	= 4,
76 	BOX_V_OFFSET	= 14,
77 	FULL_SCREEN		= 16,
78 	AUTO_DEMO		= 22,
79 	SECOND_THREAD	= 16,
80 	COLORS_BOX		= 146,
81 	COLORS_LABEL	= 16,
82 	COLORS_OFFSET	= 2,
83 	COLOR_CELL		= 8,
84 	SPECIAL_BOX		= 92,
85 	SPECIAL_LABEL	= 16,
86 	SPECIAL_OFFSET	= 2,
87 	STAR_DENSITY_H	= 160,
88 	STAR_DENSITY_V	= 34,
89 	REFRESH_RATE_H	= 320,
90 	REFRESH_RATE_V	= 34
91 };
92 
93 /* min, max and standard setting of the star count, also
94    called starfield density. */
95 enum {
96 	STAR_DENSITY_MIN		= 400,
97 	STAR_DENSITY_MAX		= 20000,
98 	STAR_DENSITY_DEFAULT	= 2000
99 };
100 
101 /* min, max and standard setting of the refresh rate. */
102 #define	REFRESH_RATE_MIN		0.6
103 #define	REFRESH_RATE_MAX		600.0
104 #define	REFRESH_RATE_DEFAULT	60.0
105 
106 /* private enums used to identify the 3 types of
107   programmable picture buttons. */
108 enum {
109 	COLOR_BUTTON_PICT	= 0,
110 	DENSITY_BUTTON_PICT	= 1,
111 	REFRESH_BUTTON_PICT	= 2
112 };
113 
114 /* min, max zoom (also default offscreen size), and max dimensions
115    of the content area of the window. */
116 enum {
117 	WINDOW_H_MIN		= 220,
118 	WINDOW_V_MIN		= 146,
119 	WINDOW_H_STD		= 800,
120 	WINDOW_V_STD		= 600,
121 	WINDOW_H_MAX		= 1920,
122 	WINDOW_V_MAX		= 1440,
123 
124 /* increment step used to dynamically resize the offscreen buffer */
125 	WINDOW_H_STEP		= 224,
126 	WINDOW_V_STEP		= 168
127 };
128 
129 /* time delay between refresh of the stat counters */
130 enum {
131 	STAT_DELAY			= 1000000
132 };
133 
134 /* ratio between the rear clipping depth and the front clipping
135    depth. */
136 #define		Z_CUT_RATIO		20.0
137 
138 /* prefered aspect ratio between horizontal and vertical
139    dimensions of the animation frame. */
140 #define		DH_REF			0.8
141 #define		DV_REF			0.6
142 
143 /* no comments (almost :-) */
144 #define		abs(x) 	(((x)>0)?(x):-(x))
145 
146 /* default background color for the UI. */
147 rgb_color	background_color = { 216, 216, 216, 255 };
148 
149 /* the 7 colors for stars. */
150 static rgb_color	color_list[7] = {
151 	{ 255, 160, 160, 255 },	/* red    */
152 	{ 160, 255, 160, 255 },	/* green  */
153 	{ 160, 160, 255, 255 },	/* blue   */
154 	{ 255, 255, 160, 255 },	/* yellow */
155 	{ 255, 208, 160, 255 },	/* orange */
156 	{ 255, 160, 255, 255 },	/* pink   */
157 	{ 255, 255, 255, 255 }	/* white  */
158 };
159 
160 /* the 8 levels of lighting, in 1/65536. */
161 static int32 light_gradient[8] = {
162 	0x2000,
163 	0x5000,
164 	0x7800,
165 	0x9800,
166 	0xb800,
167 	0xd000,
168 	0xe800,
169 	0x10000
170 };
171 
172 
173 //	#pragma mark helper classes
174 
175 
176 /* multiply a vector by a constant */
177 TPoint
178 TPoint::operator* (const float k) const
179 {
180 	TPoint v;
181 
182 	v.x = x*k;
183 	v.y = y*k;
184 	v.z = z*k;
185 	return v;
186 }
187 
188 /* substract 2 vectors */
189 TPoint
190 TPoint::operator- (const TPoint& v2) const
191 {
192 	TPoint v;
193 
194 	v.x = x-v2.x;
195 	v.y = y-v2.y;
196 	v.z = z-v2.z;
197 	return v;
198 }
199 
200 /* add 2 vectors */
201 TPoint TPoint::operator+ (const TPoint& v2) const {
202 	TPoint v;
203 
204 	v.x = x+v2.x;
205 	v.y = y+v2.y;
206 	v.z = z+v2.z;
207 	return v;
208 }
209 
210 /* vectorial product of 2 vectors */
211 TPoint
212 TPoint::operator^ (const TPoint& v2) const
213 {
214 	TPoint v;
215 
216 	v.x = y*v2.z - z*v2.y;
217 	v.y = z*v2.x - x*v2.z;
218 	v.z = x*v2.y - y*v2.x;
219 	return v;
220 }
221 
222 /* length of a vector */
223 float
224 TPoint::Length() const
225 {
226 	return sqrt(x*x + y*y + z*z);
227 }
228 
229 /* product of a vector by a matrix */
230 TPoint
231 TMatrix::operator* (const TPoint& v) const
232 {
233 	TPoint res;
234 
235 	res.x = m[0][0]*v.x + m[1][0]*v.y + m[2][0]*v.z;
236 	res.y = m[0][1]*v.x + m[1][1]*v.y + m[2][1]*v.z;
237 	res.z = m[0][2]*v.x + m[1][2]*v.y + m[2][2]*v.z;
238 	return res;
239 }
240 
241 /* extract the Nth vector/column of a matrix. */
242 TPoint
243 TMatrix::Axis(int32 index)
244 {
245 	TPoint v;
246 
247 	v.x = m[index][0];
248 	v.y = m[index][1];
249 	v.z = m[index][2];
250 	return v;
251 }
252 
253 /* as we use rotation matrix, the invert of the matrix
254    is equal to the transpose */
255 TMatrix
256 TMatrix::Transpose() const
257 {
258 	TMatrix inv;
259 
260 	inv.m[0][0] = m[0][0];
261 	inv.m[0][1] = m[1][0];
262 	inv.m[0][2] = m[2][0];
263 	inv.m[1][0] = m[0][1];
264 	inv.m[1][1] = m[1][1];
265 	inv.m[1][2] = m[2][1];
266 	inv.m[2][0] = m[0][2];
267 	inv.m[2][1] = m[1][2];
268 	inv.m[2][2] = m[2][2];
269 	return inv;
270 }
271 
272 /* set a spherical rotation matrix */
273 void
274 TMatrix::Set(const float alpha, const float theta, const float phi)
275 {
276 	float cD,sD,cI,sI,cA,sA;
277 
278 	/* trigonometry */
279 	cD = cos(alpha);
280 	sD = sin(alpha);
281 	cI = cos(theta);
282 	sI = sin(theta);
283 	cA = cos(phi);
284 	sA = sin(phi);
285 
286 	/* rotation matrix */
287 	m[0][0] = cD*cA+sD*sI*sA;
288 	m[1][0] = -sA*cI;
289 	m[2][0] = sD*cA-cD*sI*sA;
290 	m[0][1] = cD*sA-sD*sI*cA;
291 	m[1][1] = cI*cA;
292 	m[2][1] = sD*sA+cD*cA*sI;
293 	m[0][2] = -sD*cI;
294 	m[1][2] = -sI;
295 	m[2][2] = cD*cI;
296 }
297 
298 
299 //	#pragma mark -
300 
301 
302 /* this function will play a wav sound file, with the specified
303    following name, in the application folder. This is activated
304    when you press the button "Auto demo". */
305 void
306 LaunchSound()
307 {
308 /*
309 	BEntry			soundFile;
310 	app_info 		info;
311 	status_t		err;
312 	entry_ref		snd_ref;
313 	BDirectory		appFolder;
314 	sound_handle	sndhandle;
315 
316 	err = be_app->GetAppInfo(&info);
317 	BEntry appEntry(&info.ref);
318 	if (err != B_NO_ERROR)
319 		return;
320 	err = appEntry.GetParent(&appFolder);
321 	if (err != B_NO_ERROR)
322 		return;
323 	appFolder.FindEntry("demo.wav", &soundFile);
324 	err = soundFile.GetRef(&snd_ref);
325 	sndhandle = play_sound(&snd_ref, true, true, true);
326 */
327 }
328 
329 /* return the version_info of a file, described by its name
330    and its generic folder (in find_directory syntax). */
331 status_t
332 get_file_version_info(directory_which dir,
333 	char *filename, version_info *info)
334 {
335 	BPath 			path;
336 	BFile			file;
337 	status_t		res;
338 	BAppFileInfo	appinfo;
339 
340 	/* find the directory */
341 	if ((res = find_directory(dir, &path)) != B_NO_ERROR)
342 		return res;
343 
344 	/* find the file */
345 	path.Append(filename);
346 	file.SetTo(path.Path(), O_RDONLY);
347 	if ((res = file.InitCheck()) != B_NO_ERROR)
348 		return res;
349 
350 	/* get the version_info */
351 	if ((res = appinfo.SetTo(&file)) != B_NO_ERROR)
352 		return res;
353 	return appinfo.GetVersionInfo(info, B_APP_VERSION_KIND);
354 }
355 
356 
357 //	#pragma mark -
358 
359 
360 ChartWindow::ChartWindow(BRect frame, const char *name)
361 	: BDirectWindow(frame, name, B_TITLED_WINDOW, 0)
362 {
363 	float h, v, h2, v2;
364 	int32 colors[3];
365 	BRect r;
366 	BMenu *menu;
367 	BButton *button;
368 	BCheckBox *check_box, *full_screen;
369 	BMenuItem *item;
370 	BMenuField *popup;
371 	BStringView *string;
372 	BRadioButton *radio;
373 
374 	// we're not font-sensitive, so we make sure we don't look too ugly
375 	BFont font;
376 	if (font.Size() > MAX_FONT_SIZE)
377 		font.SetSize(MAX_FONT_SIZE);
378 	BFont boldFont(be_bold_font);
379 	if (boldFont.Size() > MAX_FONT_SIZE)
380 		boldFont.SetSize(MAX_FONT_SIZE);
381 
382 	/* offset the content area frame in window relative coordinate */
383 	frame.OffsetTo(0.0, 0.0);
384 
385 	/* init the pattern anti-aliasing tables. */
386 	InitPatterns();
387 
388 	/* set window size limits */
389 	SetSizeLimits(WINDOW_H_MIN, WINDOW_H_MAX, WINDOW_V_MIN, WINDOW_V_MAX);
390 	SetZoomLimits(WINDOW_H_STD, WINDOW_V_STD);
391 
392 	/* initial bitmap buffer */
393 	fOffscreen = NULL;
394 	fMaxWidth = WINDOW_H_STD - LEFT_WIDTH;
395 	fMaxHeight = WINDOW_V_STD - TOP_LEFT_LIMIT;
396 
397 	/* initialise the default setting state */
398 	for (int32 i = 0; i < 7; i++)
399 		fCurrentSettings.colors[i] = false;
400 	fCurrentSettings.colors[1] = true;
401 	fCurrentSettings.colors[2] = true;
402 	fCurrentSettings.colors[3] = true;
403 	fCurrentSettings.fullscreen_mode = WINDOW_MODE;
404 	fCurrentSettings.special = SPECIAL_NONE;
405 	fCurrentSettings.display = DISPLAY_OFF;
406 	fCurrentSettings.animation = ANIMATION_OFF;
407 	fCurrentSettings.back_color.red = 0;
408 	fCurrentSettings.back_color.green = 0;
409 	fCurrentSettings.back_color.blue = 0;
410 	fCurrentSettings.back_color.alpha = 255;
411 	fCurrentSettings.star_density = STAR_DENSITY_DEFAULT;
412 	fCurrentSettings.refresh_rate = REFRESH_RATE_DEFAULT;
413 	BScreen	screen(this);
414 	fCurrentSettings.depth	= screen.ColorSpace();
415 	fCurrentSettings.width = (int32)frame.right+1-LEFT_WIDTH;
416 	fCurrentSettings.height = (int32)frame.bottom+1-TOP_LEFT_LIMIT;
417 	fPreviousFullscreenMode = WINDOW_MODE;
418 	fNextSettings.Set(&fCurrentSettings);
419 
420 	/* initialise various global parameters */
421 	fInstantLoadLevel = 0;
422 	fSecondThreadThreshold = 0.5;
423 	fLastDynamicDelay = 0.0;
424 	fCrcAlea = CRC_START;
425 
426 	/* initialise the starfield and the special structs */
427 	fStars.list = (star*)malloc(sizeof(star)*STAR_DENSITY_MAX);
428 	fSpecials.list = (star*)malloc(sizeof(star)*SPECIAL_COUNT_MAX);
429 	fSpecialList = (special*)malloc(sizeof(special)*SPECIAL_COUNT_MAX);
430 	InitStars(SPACE_CHAOS);
431 	fStars.count = fCurrentSettings.star_density;
432 	fStars.erase_count = 0;
433 	InitSpecials(SPECIAL_NONE);
434 	fSpecials.erase_count = 0;
435 	colors[0] = 1;
436 	colors[1] = 2;
437 	colors[2] = 3;
438 	SetStarColors(colors, 3);
439 
440 	/* set camera default position and rotation */
441 	fCameraAlpha = 0.2;
442 	fCameraTheta = 0.0;
443 	fCameraPhi = 0.0;
444 	fCamera.Set(fCameraAlpha, fCameraTheta, fCameraPhi);
445 	fCameraInvert = fCamera.Transpose();
446 	fOrigin.x = 0.5;
447 	fOrigin.y = 0.5;
448 	fOrigin.z = 0.1;
449 
450 	/* initialise camera animation */
451 	fTrackingTarget = -1;
452 	fSpeed = 0.0115;
453 	fTargetSpeed = fSpeed;
454 
455 	/* initialise the view coordinate system */
456 	InitGeometry();
457 	SetGeometry(fCurrentSettings.width, fCurrentSettings.height);
458 	SetCubeOffset();
459 
460 	/* init the direct buffer in a valid state */
461 	fDirectBuffer.buffer_width = fCurrentSettings.width;
462 	fDirectBuffer.buffer_height = fCurrentSettings.height;
463 	fDirectBuffer.clip_list_count = 1;
464 	fDirectBuffer.clip_bounds.top = 0;
465 	fDirectBuffer.clip_bounds.left = 0;
466 	fDirectBuffer.clip_bounds.right = -1;
467 	fDirectBuffer.clip_bounds.bottom = -1;
468 	fDirectBuffer.clip_list[0].top = 0;
469 	fDirectBuffer.clip_list[0].left = 0;
470 	fDirectBuffer.clip_list[0].right = -1;
471 	fDirectBuffer.clip_list[0].bottom = -1;
472 	fDirectConnected = false;
473 
474 	/* build the UI content of the window */
475 
476 	/* top line background */
477 	r.Set(0.0, 0.0, frame.right, TOP_LEFT_LIMIT - 1);
478 	fTopView = new BView(r, "top view", B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW);
479 	fTopView->SetViewColor(background_color);
480 	AddChild(fTopView);
481 
482 	h = 2;
483 	v = V_BORDER;
484 
485 		/* instant load vue-meter */
486 		r.Set(h, v, h+INSTANT_LOAD-1, v + (TOP_LEFT_LIMIT - 1 - 2*V_BORDER));
487 		fInstantLoad = new InstantView(r);
488 		fTopView->AddChild(fInstantLoad);
489 		fInstantLoad->SetViewColor(0, 0, 0);
490 
491 	h += INSTANT_LOAD+H_BORDER;
492 
493 		/* camera animation popup */
494 		menu = new BPopUpMenu(B_TRANSLATE("Off"));
495 		item = new BMenuItem(B_TRANSLATE("Off"), new BMessage(ANIM_OFF_MSG));
496 		item->SetTarget(this);
497 		menu->AddItem(item);
498 		item = new BMenuItem(B_TRANSLATE("Slow rotation"),
499 			new BMessage(ANIM_SLOW_ROT_MSG));
500 		item->SetTarget(this);
501 		menu->AddItem(item);
502 		item = new BMenuItem(B_TRANSLATE("Slow motion"),
503 			new BMessage(ANIM_SLOW_MOVE_MSG));
504 		item->SetTarget(this);
505 		menu->AddItem(item);
506 		item = new BMenuItem(B_TRANSLATE("Fast motion"),
507 			new BMessage(ANIM_FAST_MOVE_MSG));
508 		item->SetTarget(this);
509 		menu->AddItem(item);
510 		item = new BMenuItem(B_TRANSLATE("Free motion"),
511 			new BMessage(ANIM_FREE_MOVE_MSG));
512 		item->SetTarget(this);
513 		menu->AddItem(item);
514 
515 		r.Set(h, v, h+ANIM_LABEL+ANIM_POPUP-1, v +
516 			(TOP_LEFT_LIMIT - 1 - 2*V_BORDER));
517 		popup = new BMenuField(r, "", B_TRANSLATE("Animation:"), menu);
518 		popup->SetFont(&font);
519 		popup->MenuBar()->SetFont(&font);
520 		popup->Menu()->SetFont(&font);
521 		popup->ResizeToPreferred();
522 		popup->SetDivider(popup->StringWidth(popup->Label()) + 4.0f);
523 		fTopView->AddChild(popup);
524 
525 	h += ANIM_LABEL + ANIM_POPUP +
526 		 popup->StringWidth(B_TRANSLATE("Slow rotation"));
527 
528 		/* display mode popup */
529 		menu = new BPopUpMenu(B_TRANSLATE("Off"));
530 		item = new BMenuItem(B_TRANSLATE("Off"), new BMessage(DISP_OFF_MSG));
531 		item->SetTarget(this);
532 		menu->AddItem(item);
533 		item = new BMenuItem(B_TRANSLATE("LineArray"),
534 			new BMessage(DISP_LINE_MSG));
535 		item->SetTarget(this);
536 		item->SetEnabled(false);
537 		menu->AddItem(item);
538 		item = new BMenuItem(B_TRANSLATE("DrawBitmap"),
539 			new BMessage(DISP_BITMAP_MSG));
540 		item->SetTarget(this);
541 		menu->AddItem(item);
542 		item = new BMenuItem(B_TRANSLATE("DirectWindow"),
543 			new BMessage(DISP_DIRECT_MSG));
544 		item->SetTarget(this);
545 		item->SetEnabled(BDirectWindow::SupportsWindowMode());
546 		menu->AddItem(item);
547 
548 		r.Set(h, v, h+DISP_LABEL+DISP_POPUP-1, v +
549 			(TOP_LEFT_LIMIT - 1 - 2*V_BORDER));
550 		popup = new BMenuField(r, "", B_TRANSLATE("Display:"), menu);
551 		popup->SetFont(&font);
552 		popup->MenuBar()->SetFont(&font);
553 		popup->Menu()->SetFont(&font);
554 		popup->ResizeToPreferred();
555 		popup->SetDivider(popup->StringWidth(popup->Label()) + 4.0f);
556 		fTopView->AddChild(popup);
557 
558 	h += DISP_LABEL + DISP_POPUP +
559 		popup->StringWidth(B_TRANSLATE("DirectWindow")) + H_BORDER;
560 
561 		/* create the offwindow (invisible) button on the left side.
562 		   this will be used to record the content of the Picture
563 		   button. */
564 		r.Set(0, 0, BUTTON_WIDTH-1, TOP_LEFT_LIMIT - 1 - 2*V_BORDER);
565 		fOffwindowButton = new BButton(r, "", "", NULL);
566 		fOffwindowButton->Hide();
567 		AddChild(fOffwindowButton);
568 		fOffwindowButton->ResizeTo(r.Width(), r.Height());
569 
570 		/* refresh rate picture button */
571 		r.Set(h, v, h+BUTTON_WIDTH-1, v + (TOP_LEFT_LIMIT - 1 - 2*V_BORDER));
572 		fRefreshButton = new BPictureButton(r, "",
573 										  ButtonPicture(false, REFRESH_BUTTON_PICT),
574 										  ButtonPicture(true, REFRESH_BUTTON_PICT),
575 										  new BMessage(OPEN_REFRESH_MSG));
576 		fRefreshButton->SetViewColor(B_TRANSPARENT_32_BIT);
577 		fRefreshButton->ResizeToPreferred();
578 		fTopView->AddChild(fRefreshButton);
579 
580 	h += BUTTON_WIDTH+2*H_BORDER;
581 
582 		/* background color button */
583 		r.Set(h, v, h+BUTTON_WIDTH-1, v + (TOP_LEFT_LIMIT - 1 - 2*V_BORDER));
584 		fColorButton = new BPictureButton(r, "",
585 										  ButtonPicture(false, COLOR_BUTTON_PICT),
586 										  ButtonPicture(true, COLOR_BUTTON_PICT),
587 										  new BMessage(OPEN_COLOR_MSG));
588 		fColorButton->SetViewColor(B_TRANSPARENT_32_BIT);
589 		fColorButton->ResizeToPreferred();
590 		fTopView->AddChild(fColorButton);
591 
592 	h += BUTTON_WIDTH+2*H_BORDER;
593 
594 		/* star density button */
595 		r.Set(h, v, h+BUTTON_WIDTH-1, v + (TOP_LEFT_LIMIT - 1 - 2*V_BORDER));
596 		fDensityButton = new BPictureButton(r, "",
597 											ButtonPicture(false, DENSITY_BUTTON_PICT),
598 											ButtonPicture(true, DENSITY_BUTTON_PICT),
599 											new BMessage(OPEN_DENSITY_MSG));
600 		fDensityButton->SetViewColor(B_TRANSPARENT_32_BIT);
601 		fDensityButton->ResizeToPreferred();
602 		fTopView->AddChild(fDensityButton);
603 
604 	h += BUTTON_WIDTH+H_BORDER;
605 
606 		/* starfield type popup */
607 		menu = new BPopUpMenu(B_TRANSLATE("Chaos"));
608 		item = new BMenuItem(B_TRANSLATE("Chaos"),
609 			new BMessage(SPACE_CHAOS_MSG));
610 		item->SetTarget(this);
611 		menu->AddItem(item);
612 		item = new BMenuItem(B_TRANSLATE("Amas"), new BMessage(SPACE_AMAS_MSG));
613 		item->SetTarget(this);
614 		menu->AddItem(item);
615 		item = new BMenuItem(B_TRANSLATE("Spiral"),
616 			new BMessage(SPACE_SPIRAL_MSG));
617 		item->SetTarget(this);
618 		menu->AddItem(item);
619 
620 		r.Set(h, v, h+SPACE_LABEL+SPACE_POPUP-1, v +
621 			(TOP_LEFT_LIMIT - 1 - 2*V_BORDER));
622 		popup = new BMenuField(r, "", B_TRANSLATE("Space:"), menu);
623 		popup->SetFont(&font);
624 		popup->MenuBar()->SetFont(&font);
625 		popup->Menu()->SetFont(&font);
626 		popup->ResizeToPreferred();
627 		popup->SetDivider(popup->StringWidth(popup->Label()) + 4.0f);
628 		fTopView->AddChild(popup);
629 
630 	h += SPACE_LABEL+SPACE_POPUP+2*H_BORDER;
631 
632 	/* left column gray background */
633 	r.Set(0.0, TOP_LEFT_LIMIT, LEFT_WIDTH - 1, frame.bottom);
634 	fLeftView = new BView(r, "top view", B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM, B_WILL_DRAW);
635 	fLeftView->SetViewColor(background_color);
636 	AddChild(fLeftView);
637 
638 	h2 = LEFT_OFFSET;
639 	v2 = LEFT_OFFSET;
640 	h = h2;
641 	v = v2;
642 
643 		/* status box */
644 		r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2, v+STATUS_BOX-1);
645 		fStatusBox = new BBox(r);
646 		fStatusBox->SetFont(&boldFont);
647 		fStatusBox->SetLabel(B_TRANSLATE("Status"));
648 		fLeftView->AddChild(fStatusBox);
649 		float boxWidth, boxHeight;
650 		fStatusBox->GetPreferredSize(&boxWidth, &boxHeight);
651 		boxWidth += r.left;
652 
653 		h = BOX_H_OFFSET;
654 		v = BOX_V_OFFSET;
655 
656 			/* frames per second title string */
657 			r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1,
658 				v+STATUS_LABEL-1);
659 			string = new BStringView(r, "", B_TRANSLATE("Frames/s"));
660 			string->SetFont(&font);
661 			string->SetAlignment(B_ALIGN_CENTER);
662 			fStatusBox->AddChild(string);
663 
664 		v += STATUS_LABEL+STATUS_OFFSET;
665 
666 			/* frames per second display string */
667 			r.Set(h-1, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET,
668 				v+STATUS_EDIT-1);
669 			fFramesView = new BStringView(r, "", "0.0");
670 			fFramesView->SetAlignment(B_ALIGN_RIGHT);
671 			fFramesView->SetFont(be_bold_font);
672 			fFramesView->SetFontSize(24.0);
673 			fFramesView->SetViewColor(B_TRANSPARENT_32_BIT);
674 			fStatusBox->AddChild(fFramesView);
675 
676 		v += STATUS_EDIT+STATUS_OFFSET;
677 
678 			/* CPU load pourcentage title string */
679 			r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1,
680 				v+STATUS_LABEL-1);
681 			string = new BStringView(r, "", B_TRANSLATE("CPU load"));
682 			string->SetAlignment(B_ALIGN_CENTER);
683 			string->SetFont(&font);
684 			fStatusBox->AddChild(string);
685 
686 		v += STATUS_LABEL+STATUS_OFFSET;
687 
688 			/* CPU load pourcentage display string */
689 			r.Set(h-1, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET,
690 				v+STATUS_EDIT-1);
691 			fCpuLoadView = new BStringView(r, "", "0.0");
692 			fCpuLoadView->SetAlignment(B_ALIGN_RIGHT);
693 			fCpuLoadView->SetFont(be_bold_font);
694 			fCpuLoadView->SetFontSize(24.0);
695 			fCpuLoadView->SetViewColor(B_TRANSPARENT_32_BIT);
696 			fStatusBox->AddChild(fCpuLoadView);
697 
698 	v2 += STATUS_BOX+LEFT_OFFSET*2;
699 	h = h2;
700 	v = v2;
701 
702 		/* Fullscreen mode check box */
703 		r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-1, v+FULL_SCREEN-1);
704 		full_screen = new BCheckBox(r, "", B_TRANSLATE("Full screen"),
705 			new BMessage(FULL_SCREEN_MSG));
706 		full_screen->SetTarget(this);
707 		full_screen->SetFont(&font);
708 		full_screen->ResizeToPreferred();
709 
710 		float width, height;
711 		full_screen->GetPreferredSize(&width, &height);
712 		boxWidth = max_c(width + r.left, boxWidth);
713 		fLeftView->AddChild(full_screen);
714 
715 	v2 += FULL_SCREEN+LEFT_OFFSET*2;
716 	h = h2;
717 	v = v2;
718 
719 		/* Automatic demonstration activation button */
720 		r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-1, v+AUTO_DEMO-1);
721 		button = new BButton(r, "", B_TRANSLATE("Auto demo"),
722 			new BMessage(AUTO_DEMO_MSG));
723 		button->SetTarget(this);
724 		button->ResizeToPreferred();
725 		button->GetPreferredSize(&width, &height);
726 		boxWidth = max_c(width + r.left, boxWidth);
727 		fLeftView->AddChild(button);
728 
729 	v2 += AUTO_DEMO+LEFT_OFFSET*2;
730 	h = h2;
731 	v = v2;
732 
733 		/* Enabling second thread check box */
734 		r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-1, v+SECOND_THREAD-1);
735 		check_box = new BCheckBox(r, "", B_TRANSLATE("2 threads"),
736 			new BMessage(SECOND_THREAD_MSG));
737 		check_box->SetTarget(this);
738 		check_box->SetFont(&font);
739 		check_box->ResizeToPreferred();
740 		fLeftView->AddChild(check_box);
741 
742 	v2 += SECOND_THREAD+LEFT_OFFSET*2 + 2;
743 	h = h2;
744 	v = v2;
745 
746 		/* Star color selection box */
747 		r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2, v+COLORS_BOX-1);
748 		fColorsBox = new BBox(r);
749 		fColorsBox->SetLabel(B_TRANSLATE("Colors"));
750 		fColorsBox->SetFont(&boldFont);
751 		fLeftView->AddChild(fColorsBox);
752 
753 		h = BOX_H_OFFSET;
754 		v = BOX_V_OFFSET;
755 
756 			/* star color red check box */
757 			r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1,
758 				v+COLORS_LABEL-1);
759 			check_box = new BCheckBox(r, "", B_TRANSLATE("Red"),
760 				new BMessage(COLORS_RED_MSG));
761 			check_box->SetFont(&font);
762 			check_box->ResizeToPreferred();
763 			fColorsBox->AddChild(check_box);
764 
765 		v += COLORS_LABEL+COLORS_OFFSET;
766 
767 			/* star color green check box */
768 			r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1,
769 				v+COLORS_LABEL-1);
770 			check_box = new BCheckBox(r, "", B_TRANSLATE("Green"),
771 				new BMessage(COLORS_GREEN_MSG));
772 			check_box->SetValue(1);
773 			check_box->SetFont(&font);
774 			check_box->ResizeToPreferred();
775 			fColorsBox->AddChild(check_box);
776 
777 		v += COLORS_LABEL+COLORS_OFFSET;
778 
779 			/* star color blue check box */
780 			r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1,
781 				v+COLORS_LABEL-1);
782 			check_box = new BCheckBox(r, "", B_TRANSLATE("Blue"),
783 				new BMessage(COLORS_BLUE_MSG));
784 			check_box->SetValue(1);
785 			check_box->SetFont(&font);
786 			check_box->ResizeToPreferred();
787 			fColorsBox->AddChild(check_box);
788 
789 		v += COLORS_LABEL+COLORS_OFFSET;
790 
791 			/* star color yellow check box */
792 			r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1,
793 				v+COLORS_LABEL-1);
794 			check_box = new BCheckBox(r, "", B_TRANSLATE("Yellow"),
795 				new BMessage(COLORS_YELLOW_MSG));
796 			check_box->SetValue(1);
797 			check_box->SetFont(&font);
798 			check_box->ResizeToPreferred();
799 			fColorsBox->AddChild(check_box);
800 
801 		v += COLORS_LABEL+COLORS_OFFSET;
802 
803 			/* star color orange check box */
804 			r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1,
805 				v+COLORS_LABEL-1);
806 			check_box = new BCheckBox(r, "", B_TRANSLATE("Orange"),
807 				new BMessage(COLORS_ORANGE_MSG));
808 			check_box->SetFont(&font);
809 			check_box->ResizeToPreferred();
810 			fColorsBox->AddChild(check_box);
811 
812 		v += COLORS_LABEL+COLORS_OFFSET;
813 
814 			/* star color pink check box */
815 			r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1,
816 				v+COLORS_LABEL-1);
817 			check_box = new BCheckBox(r, "", B_TRANSLATE("Pink"),
818 				new BMessage(COLORS_PINK_MSG));
819 			check_box->SetFont(&font);
820 			check_box->ResizeToPreferred();
821 			fColorsBox->AddChild(check_box);
822 
823 		v += COLORS_LABEL+COLORS_OFFSET;
824 
825 			/* star color white check box */
826 			r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1,
827 				v+COLORS_LABEL-1);
828 			check_box = new BCheckBox(r, "", B_TRANSLATE("White"),
829 				new BMessage(COLORS_WHITE_MSG));
830 			check_box->SetFont(&font);
831 			check_box->ResizeToPreferred();
832 			fColorsBox->AddChild(check_box);
833 
834 	v2 += COLORS_BOX+LEFT_OFFSET*2;
835 	h = h2;
836 	v = v2;
837 
838 		/* Special type selection box */
839 		r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2, v+SPECIAL_BOX-1);
840 		fSpecialBox = new BBox(r);
841 		fSpecialBox->SetFont(&boldFont);
842 		fSpecialBox->SetLabel(B_TRANSLATE("Special"));
843 		fLeftView->AddChild(fSpecialBox);
844 
845 		h = BOX_H_OFFSET;
846 		v = BOX_V_OFFSET;
847 
848 			/* no special radio button */
849 			r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1,
850 				v+COLORS_LABEL-1);
851 			radio = new BRadioButton(r, "", B_TRANSLATE("None"),
852 				new BMessage(SPECIAL_NONE_MSG));
853 			radio->SetValue(1);
854 			radio->SetFont(&font);
855 			radio->ResizeToPreferred();
856 			fSpecialBox->AddChild(radio);
857 
858 		v += COLORS_LABEL+COLORS_OFFSET;
859 
860 			/* comet special animation radio button */
861 			r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1,
862 				v+COLORS_LABEL-1);
863 			radio = new BRadioButton(r, "", B_TRANSLATE("Comet"),
864 				new BMessage(SPECIAL_COMET_MSG));
865 			radio->SetFont(&font);
866 			radio->ResizeToPreferred();
867 			fSpecialBox->AddChild(radio);
868 
869 		v += COLORS_LABEL+COLORS_OFFSET;
870 
871 			/* novas special animation radio button */
872 			r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1,
873 				v+COLORS_LABEL-1);
874 			radio = new BRadioButton(r, "", B_TRANSLATE("Novas"),
875 				new BMessage(SPECIAL_NOVAS_MSG));
876 			radio->SetFont(&font);
877 			radio->ResizeToPreferred();
878 			fSpecialBox->AddChild(radio);
879 
880 		v += COLORS_LABEL+COLORS_OFFSET;
881 
882 			/* space batle special animation radio button (not implemented) */
883 			r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1,
884 				v+COLORS_LABEL-1);
885 			radio = new BRadioButton(r, "", B_TRANSLATE("Battle"),
886 				new BMessage(SPECIAL_BATTLE_MSG));
887 			radio->SetEnabled(false);
888 			radio->SetFont(&font);
889 			radio->ResizeToPreferred();
890 			fSpecialBox->AddChild(radio);
891 
892 	// Note: direct window mode uses LEFT_WIDTH to calculate
893 	// the left border of the animation view, so we use it here too.
894 	//fLeftView->ResizeTo(max_c(boxWidth + 2, fLeftView->Bounds().Width()), fLeftView->Bounds().Height());
895 	/* animation area */
896 	r.Set(LEFT_WIDTH, TOP_LEFT_LIMIT, frame.right, frame.bottom);
897 	fChartView = new ChartView(r);
898 	fChartView->SetViewColor(0, 0, 0);
899 	AddChild(fChartView);
900 
901 	/* allocate the semaphores */
902 	fDrawingLock = create_sem(1, "chart locker");
903 	fSecondThreadLock = create_sem(0, "chart second locker");
904 	fSecondThreadRelease = create_sem(0, "chart second release");
905 
906 	/* spawn the asynchronous animation threads */
907 	fKillThread = false;
908 	fAnimationThread = spawn_thread(ChartWindow::Animation, "chart animation",
909 								B_NORMAL_PRIORITY,
910 								(void*)this);
911 
912 	fSecondAnimationThread = spawn_thread(ChartWindow::Animation2, "chart animation2",
913 								B_NORMAL_PRIORITY,
914 								(void*)this);
915 	resume_thread(fSecondAnimationThread);
916 	resume_thread(fAnimationThread);
917 }
918 
919 
920 ChartWindow::~ChartWindow()
921 {
922 	status_t result;
923 
924 	/* setting this flag force both animation threads to quit */
925 	fKillThread = true;
926 
927 	/* wait for the two animation threads to quit */
928 	wait_for_thread(fAnimationThread, &result);
929 	wait_for_thread(fSecondAnimationThread, &result);
930 
931 	/* free the offscreen bitmap if any */
932 	delete fOffscreen;
933 
934 	/* release the semaphores used for synchronisation */
935 	delete_sem(fDrawingLock);
936 	delete_sem(fSecondThreadLock);
937 	delete_sem(fSecondThreadRelease);
938 
939 	/* free the buffers used to store the starlists */
940 	free(fStars.list);
941 	free(fSpecials.list);
942 	free(fSpecialList);
943 }
944 
945 
946 //	#pragma mark Standard window members
947 
948 
949 bool
950 ChartWindow::QuitRequested()
951 {
952 	be_app->PostMessage(B_QUIT_REQUESTED);
953 	return BWindow::QuitRequested();
954 }
955 
956 
957 void
958 ChartWindow::MessageReceived(BMessage *message)
959 {
960 	int32			index, color;
961 	BHandler		*handler;
962 	BCheckBox		*check_box;
963 	BSlider			*slider;
964 
965 	message->FindPointer("source", (void**)&handler);
966 	switch(message->what) {
967 		/* This is a key part of the architecture. MessageReceived is
968 		   called whenever the user interact with a UI element to change
969 		   a setting. The window is locked at this point, so changing
970 		   the setting of the engine at that point would be dangerous.
971 		   We could easily goofed and create a bad dependencies between
972 		   the Window locking mechanism and DirectConnected, that
973 		   would generate a deadlock and force the app_server to kill
974 		   the application. Bad business. To avoid that, we keep two
975 		   different engine setting. One that is currently used by the
976 		   animation engine, the other one that will retain all the
977 		   changes generated by the user until the engine is ready to
978 		   use them. So message received will write into that setting
979 		   state and the engine will read it from time to time. Both
980 		   access can be done asynchronously as all intermediate state
981 		   generated by the MessageReceived write are valid (we don't
982 		   need to make those transactions atomic). */
983 		case ANIM_OFF_MSG :
984 		case ANIM_SLOW_ROT_MSG :
985 		case ANIM_SLOW_MOVE_MSG :
986 		case ANIM_FAST_MOVE_MSG :
987 		case ANIM_FREE_MOVE_MSG :
988 			fNextSettings.animation = ANIMATION_OFF + (message->what - ANIM_OFF_MSG);
989 			break;
990 		case DISP_OFF_MSG :
991 		case DISP_BITMAP_MSG :
992 		case DISP_DIRECT_MSG :
993 			fNextSettings.display = DISPLAY_OFF + (message->what - DISP_OFF_MSG);
994 			break;
995 		case SPACE_CHAOS_MSG :
996 		case SPACE_AMAS_MSG :
997 		case SPACE_SPIRAL_MSG :
998 			fNextSettings.space_model = SPACE_CHAOS + (message->what - SPACE_CHAOS_MSG);
999 			break;
1000 		case FULL_SCREEN_MSG :
1001 			check_box = dynamic_cast<BCheckBox*>(handler);
1002 			if (check_box == NULL)
1003 				break;
1004 
1005 			if (check_box->Value())
1006 				fNextSettings.fullscreen_mode = FULLSCREEN_MODE;
1007 			else
1008 				fNextSettings.fullscreen_mode = WINDOW_MODE;
1009 			break;
1010 		case AUTO_DEMO_MSG :
1011 			fNextSettings.fullscreen_mode = FULLDEMO_MODE;
1012 			fNextSettings.animation = ANIMATION_FREE_MOVE;
1013 			fNextSettings.special = SPECIAL_COMET;
1014 			LaunchSound();
1015 			break;
1016 		case BACK_DEMO_MSG :
1017 			fNextSettings.fullscreen_mode = fPreviousFullscreenMode;
1018 			break;
1019 		case SECOND_THREAD_MSG :
1020 			check_box = dynamic_cast<BCheckBox*>(handler);
1021 			if (check_box == NULL)
1022 				break;
1023 
1024 			fNextSettings.second_thread =  (check_box->Value()?true:false);
1025 			break;
1026 		case COLORS_RED_MSG :
1027 		case COLORS_GREEN_MSG :
1028 		case COLORS_BLUE_MSG :
1029 		case COLORS_YELLOW_MSG :
1030 		case COLORS_ORANGE_MSG :
1031 		case COLORS_PINK_MSG :
1032 		case COLORS_WHITE_MSG :
1033 			index = message->what - COLORS_RED_MSG;
1034 			check_box = dynamic_cast<BCheckBox*>(handler);
1035 			if (check_box == NULL)
1036 				break;
1037 
1038 			fNextSettings.colors[index] = (check_box->Value()?true:false);
1039 			break;
1040 		case SPECIAL_NONE_MSG :
1041 		case SPECIAL_COMET_MSG :
1042 		case SPECIAL_NOVAS_MSG :
1043 		case SPECIAL_BATTLE_MSG :
1044 			fNextSettings.special = SPECIAL_NONE + (message->what - SPECIAL_NONE_MSG);
1045 			break;
1046 		case COLOR_PALETTE_MSG :
1047 			message->FindInt32("be:value", &color);
1048 			fNextSettings.back_color.red = (color >> 24);
1049 			fNextSettings.back_color.green = (color >> 16);
1050 			fNextSettings.back_color.blue = (color >> 8);
1051 			fNextSettings.back_color.alpha = color;
1052 			break;
1053 		case STAR_DENSITY_MSG :
1054 			slider = dynamic_cast<BSlider*>(handler);
1055 			if (slider == NULL)
1056 				break;
1057 
1058 			fNextSettings.star_density = slider->Value();
1059 			break;
1060 		case REFRESH_RATE_MSG :
1061 			slider = dynamic_cast<BSlider*>(handler);
1062 			if (slider == NULL)
1063 				break;
1064 
1065 			fNextSettings.refresh_rate = exp(slider->Value()*0.001*(log(REFRESH_RATE_MAX/REFRESH_RATE_MIN)))*
1066 									REFRESH_RATE_MIN;
1067 			break;
1068 		/* open the three floating window used to do live setting of
1069 		   some advanced parameters. Those windows will return live
1070 		   feedback that will be executed by some of the previous
1071 		   messages. */
1072 		case OPEN_COLOR_MSG :
1073 			OpenColorPalette(BPoint(200.0, 200.0));
1074 			break;
1075 		case OPEN_DENSITY_MSG :
1076 			OpenStarDensity(BPoint(280.0, 280.0));
1077 			break;
1078 		case OPEN_REFRESH_MSG :
1079 			OpenRefresh(BPoint(240.0, 340.0));
1080 			break;
1081 		/* let other messages pass through... */
1082 		default :
1083 			BDirectWindow::MessageReceived(message);
1084 			break;
1085 	}
1086 }
1087 
1088 
1089 void
1090 ChartWindow::ScreenChanged(BRect screen_size, color_space depth)
1091 {
1092 	/* this is the same principle than the one described for
1093 	   MessageReceived, to inform the engine that the depth of
1094 	   the screen changed (needed only for offscreen bitmap.
1095 	   In DirectWindow, you get a direct notification). */
1096 	fNextSettings.depth = BScreen(this).ColorSpace();
1097 }
1098 
1099 
1100 void
1101 ChartWindow::FrameResized(float new_width, float new_height)
1102 {
1103 	/* this is the same principle than the one described for
1104 	   MessageReceived, to inform the engine that the window
1105 	   size changed (needed only for offscreen bitmap. In
1106 	   DirectWindow, you get a direct notification). */
1107 	fNextSettings.width = (int32)Frame().Width()+1-LEFT_WIDTH;
1108 	fNextSettings.height = (int32)Frame().Height()+1-TOP_LEFT_LIMIT;
1109 }
1110 
1111 
1112 //	#pragma mark User Interface related stuff...
1113 
1114 
1115 /* loop through the window list of the application, looking for
1116    a window with a specified name. */
1117 BWindow	*
1118 ChartWindow::GetAppWindow(const char *name)
1119 {
1120 	int32		index;
1121 	BWindow		*window;
1122 
1123 	for (index = 0;; index++) {
1124 		window = be_app->WindowAt(index);
1125 		if (window == NULL)
1126 			break;
1127 		if (window->LockWithTimeout(200000) == B_OK) {
1128 			// skip the w> prefix in window's name.
1129 			if (strcmp(window->Name() + 2, name) == 0) {
1130 				window->Unlock();
1131 				break;
1132 			}
1133 			window->Unlock();
1134 		}
1135 	}
1136 	return window;
1137 }
1138 
1139 /* this function return a picture (in active or inactive state) of
1140    a standard BButton with some specific content draw in the middle.
1141    button_type indicate what special content should be used. */
1142 BPicture *
1143 ChartWindow::ButtonPicture(bool active, int32 button_type)
1144 {
1145 	char		word[6];
1146 	int32		value;
1147 	BRect		r;
1148 	BPoint		delta;
1149 	BPicture	*pict;
1150 
1151 
1152 	/* create and open the picture */
1153 	pict = new BPicture();
1154 	r = fOffwindowButton->Bounds();
1155 	fOffwindowButton->SetValue(active);
1156 	fOffwindowButton->BeginPicture(pict);
1157 	/* draw the standard BButton in whatever state is required. */
1158 	fOffwindowButton->Draw(r);
1159 	if (button_type == COLOR_BUTTON_PICT) {
1160 		/* this button just contains a rectangle of the current background
1161 		   color, with a one pixel black border. */
1162 		r.InsetBy(6.0, 4.0);
1163 		fOffwindowButton->SetHighColor(0, 0, 0);
1164 		fOffwindowButton->StrokeRect(r);
1165 		r.InsetBy(1.0, 1.0);
1166 		fOffwindowButton->SetHighColor(fCurrentSettings.back_color);
1167 		fOffwindowButton->FillRect(r);
1168 	}
1169 	else if (button_type == DENSITY_BUTTON_PICT) {
1170 		/* this button just contains a big string (using a bigger font size
1171 		   than what a standard BButton would allow) with the current value
1172 		   of the star density pourcentage. */
1173 		value = (fCurrentSettings.star_density*100 + STAR_DENSITY_MAX/2) / STAR_DENSITY_MAX;
1174 		sprintf(word, "%3ld", value);
1175 	draw_string:
1176 		fOffwindowButton->SetFont(be_bold_font);
1177 		fOffwindowButton->SetFontSize(14.0);
1178 		delta.x = BUTTON_WIDTH/2-(fOffwindowButton->StringWidth(word) * 0.5);
1179 		delta.y = (TOP_LEFT_LIMIT-2*V_BORDER)/2 + 6.0;
1180 		fOffwindowButton->DrawString(word, delta);
1181 	}
1182 	else {
1183 		/* this button just contains a big string (using a bigger font size
1184 		   than what a standard BButton would allow) with the current value
1185 		   of the target refresh rate in frames per second. */
1186 		sprintf(word, "%3.1f", fCurrentSettings.refresh_rate + 0.05);
1187 		goto draw_string;
1188 	}
1189 	/* close and return the picture */
1190 	return fOffwindowButton->EndPicture();
1191 }
1192 
1193 /* Create a floating window including a slightly modified version of
1194    BColorControl, ChartColorControl, that will return live feedback
1195    as the same time the user will change the color setting of the
1196    background. */
1197 void
1198 ChartWindow::OpenColorPalette(BPoint here)
1199 {
1200 	BRect frame;
1201 	BPoint point;
1202 
1203 	BWindow *window = GetAppWindow(B_TRANSLATE("Space color"));
1204 	if (window == NULL) {
1205 		frame.Set(here.x, here.y, here.x + 199.0, here.y + 99.0);
1206 		window = new BWindow(frame, B_TRANSLATE("Space color"),
1207 							 B_FLOATING_WINDOW_LOOK,
1208 							 B_FLOATING_APP_WINDOW_FEEL,
1209 							 B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK | B_NOT_RESIZABLE);
1210 		point.Set(0, 0);
1211 		BColorControl *colorControl = new ChartColorControl(point,
1212 			new BMessage(COLOR_PALETTE_MSG));
1213 		colorControl->SetViewColor(background_color);
1214 		colorControl->SetTarget(NULL, this);
1215 		colorControl->SetValue(fCurrentSettings.back_color);
1216 		colorControl->ResizeToPreferred();
1217 		window->ResizeTo(colorControl->Bounds().Width(), colorControl->Bounds().Height());
1218 		window->AddChild(colorControl);
1219 		window->Show();
1220 	}
1221 	window->Activate();
1222 }
1223 
1224 /* Create a floating window including a BSlider, that will return
1225    live feedback when the user will change the star density of the
1226    starfield */
1227 void
1228 ChartWindow::OpenStarDensity(BPoint here)
1229 {
1230 	BWindow	*window = GetAppWindow(B_TRANSLATE("Star density"));
1231 	if (window == NULL) {
1232 		BRect frame(here.x, here.y, here.x + STAR_DENSITY_H-1,
1233 			here.y + STAR_DENSITY_V-1);
1234 		window = new BWindow(frame, B_TRANSLATE("Star density"),
1235 							 B_FLOATING_WINDOW_LOOK,
1236 							 B_FLOATING_APP_WINDOW_FEEL,
1237 							 B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK);
1238 		frame.OffsetTo(0.0, 0.0);
1239 		BSlider	*slider = new BSlider(frame, "", NULL, new BMessage(STAR_DENSITY_MSG),
1240 							 STAR_DENSITY_MIN, STAR_DENSITY_MAX);
1241 		slider->SetViewColor(background_color);
1242 		slider->SetTarget(NULL, this);
1243 		slider->SetValue(fCurrentSettings.star_density);
1244 		slider->SetModificationMessage(new BMessage(STAR_DENSITY_MSG));
1245 		slider->SetLimitLabels(B_TRANSLATE(" 5% (low)"),
1246 			B_TRANSLATE("(high) 100% "));
1247 		slider->ResizeToPreferred();
1248 		window->ResizeTo(slider->Bounds().Width(), slider->Bounds().Height());
1249 		window->AddChild(slider);
1250 		window->Show();
1251 	}
1252 	window->Activate();
1253 }
1254 
1255 /* Create a floating window including a BSlider, that will return
1256    live feedback when the user will change the target refresh rate
1257    of the animation */
1258 void
1259 ChartWindow::OpenRefresh(BPoint here)
1260 {
1261 	BWindow *window = GetAppWindow(B_TRANSLATE("Refresh rate"));
1262 	if (window == NULL) {
1263 		BRect frame(here.x, here.y, here.x + REFRESH_RATE_H-1,
1264 			here.y + REFRESH_RATE_V-1);
1265 		window = new BWindow(frame, B_TRANSLATE("Refresh rate"),
1266 							 B_FLOATING_WINDOW_LOOK,
1267 							 B_FLOATING_APP_WINDOW_FEEL,
1268 							 B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK);
1269 		frame.OffsetTo(0.0, 0.0);
1270 		BSlider *slider = new BSlider(frame, "", NULL, new BMessage(REFRESH_RATE_MSG), 0, 1000);
1271 		slider->SetViewColor(background_color);
1272 		slider->SetTarget(NULL, this);
1273 		slider->SetValue((int32)(1000 * log(fCurrentSettings.refresh_rate / REFRESH_RATE_MIN) /
1274 						log(REFRESH_RATE_MAX/REFRESH_RATE_MIN)));
1275 		slider->SetModificationMessage(new BMessage(REFRESH_RATE_MSG));
1276 		slider->SetLimitLabels(B_TRANSLATE(" 0.6 f/s  (logarythmic scale)"),
1277 			B_TRANSLATE("600.0 f/s"));
1278 		slider->ResizeToPreferred();
1279 		window->ResizeTo(slider->Bounds().Width(), slider->Bounds().Height());
1280 		window->AddChild(slider);
1281 		window->Show();
1282 	}
1283 	window->Activate();
1284 }
1285 
1286 /* This update the state of the frames per second vue-meter in a lazy way. */
1287 void
1288 ChartWindow::DrawInstantLoad(float frame_per_second)
1289 {
1290 	int32 i;
1291 	bigtime_t	timeout;
1292 
1293 	int32 level = (int32)((frame_per_second + 6.0) * (1.0/12.0));
1294 	if (level > 50)
1295 		level = 50;
1296 
1297 	/* if the load level is inchanged, nothing more to do... */
1298 	if (level == fInstantLoadLevel)
1299 		return;
1300 
1301 	/* We need to lock the window to be able to draw that. But as some
1302 	   BControl are still synchronous, if the user is still tracking them,
1303 	   the window can stay block for a long time. It's not such a big deal
1304 	   when using the offscreen buffer as we won't be able to draw it in
1305 	   any case. But in DirectWindow mode, we're not limited by that so
1306 	   it would be stupid to block the engine loop here. That's why in
1307 	   that case, we will try to lock the window with a timeout of 0us. */
1308 	if (fCurrentSettings.display == DISPLAY_BITMAP)
1309 		timeout = 100000;
1310 	else
1311 		timeout = 0;
1312 	if (LockWithTimeout(timeout) != B_OK)
1313 		return;
1314 
1315 	/* the new level is higher than the previous. We need to draw more
1316 	   colored bars. */
1317 	if (level > fInstantLoadLevel) {
1318 		for (i = fInstantLoadLevel; i < level; i++) {
1319 			if (i < fInstantLoad->step)
1320 				fInstantLoad->SetHighColor(255, 90, 90);
1321 			else if ((i / fInstantLoad->step) & 1)
1322 				fInstantLoad->SetHighColor(90, 255, 90);
1323 			else
1324 				fInstantLoad->SetHighColor(40, 200, 40);
1325 			fInstantLoad->FillRect(BRect(3 + i * 4, 2, 5 + i * 4, 19));
1326 		}
1327 	}
1328 	/* the level is lower than before, we need to erase some bars. */
1329 	else {
1330 		fInstantLoad->SetHighColor(0, 0, 0);
1331 		for (i = level; i < fInstantLoadLevel; i++)
1332 			fInstantLoad->FillRect(BRect(3 + i * 4, 2, 5 +i * 4, 19));
1333 	}
1334 	/* we want that drawing to be completed as soon as possible */
1335 	Flush();
1336 
1337 	fInstantLoadLevel = level;
1338 	Unlock();
1339 }
1340 
1341 
1342 void
1343 ChartWindow::PrintStatNumbers(float fps)
1344 {
1345 	char		text_frames[6];
1346 	char		text_cpu_load[6];
1347 	float		frame_rate, load;
1348 	bigtime_t	timeout;
1349 
1350 	/* rules use to determine the stat numbers : if the target framerate
1351 	   is greater than the simulate one, then we consider that 100.0 cpu
1352 	   was used, and we only got the simulate framerate. */
1353 	if (fps <= fCurrentSettings.refresh_rate) {
1354 		load = 100.0;
1355 		frame_rate = fps + 0.05;
1356 	}
1357 	/* if the target framerate is less than the simulate one, then we
1358 	   calculate what fraction of the cpu would have been required to
1359 	   deliver the target framerate, and we said that the target framerate
1360 	   was delivered. */
1361 	else {
1362 		load = (100.0*fCurrentSettings.refresh_rate)/fps + 0.05;
1363 		frame_rate = fCurrentSettings.refresh_rate + 0.05;
1364 	}
1365 
1366 	/* convert numbers in strings */
1367 	sprintf(text_cpu_load, "%3.1f", load);
1368 	sprintf(text_frames, "%3.1f", frame_rate);
1369 
1370 	/* same remark than for DrawInstantLoad. We want to avoid to
1371 	   block if using DirectWindow mode. */
1372 	if (fCurrentSettings.display == DISPLAY_BITMAP)
1373 		timeout = 100000;
1374 	else
1375 		timeout = 0;
1376 
1377 	if (LockWithTimeout(timeout) == B_OK) {
1378 		fFramesView->SetText(text_frames);
1379 		fCpuLoadView->SetText(text_cpu_load);
1380 		Unlock();
1381 	}
1382 }
1383 
1384 
1385 //	#pragma mark Engine setting related functions.
1386 
1387 
1388 void
1389 ChartWindow::InitGeometry()
1390 {
1391 	/* calculate some parameters required for the 3d processing */
1392 	float dz = sqrt(1.0 - (DH_REF*DH_REF + DV_REF*DV_REF) * (0.5 + 0.5/Z_CUT_RATIO) * (0.5 + 0.5/Z_CUT_RATIO));
1393 	fDepthRef = dz / (1.0 - 1.0/Z_CUT_RATIO);
1394 
1395 	/* set the position of the pyramid of vision, so that it was always
1396 	   possible to include it into a 1x1x1 cube parallel to the 3 main
1397 	   axis. */
1398 	fGeometry.z_max = fDepthRef;
1399 	fGeometry.z_min = fDepthRef/Z_CUT_RATIO;
1400 
1401 	/* used for lighting processing */
1402 	fGeometry.z_max_square = fGeometry.z_max * fGeometry.z_max;
1403 
1404 	/* preprocess that for the fast clipping based on the pyramid of vision */
1405 	fGeometry.xz_max = (0.5*DH_REF)/fGeometry.z_max;
1406 	fGeometry.xz_min = -fGeometry.xz_max;
1407 	fGeometry.yz_max = (0.5*DV_REF)/fGeometry.z_max;
1408 	fGeometry.yz_min = -fGeometry.yz_max;
1409 }
1410 
1411 /* second part of the asynchronous setting mechanism. This will be
1412    called once during every loop of the animation engine, at a time
1413    when the engine is not using the setting for realtime processing.
1414    Each setting will be checked for potential change, and action
1415    will be taken if needed. The window can be locked at that time
1416    because the structure of the animation engine loop guarantees
1417    that DirectConnected can not stay blocked at the same time that
1418    this method is executed. */
1419 void
1420 ChartWindow::ChangeSetting(setting new_set)
1421 {
1422 	int32			i, color_count, old_step;
1423 	int32			color_index[7];
1424 
1425 	/* check for change of window/fullscreen/fullscreen demo mode */
1426 	if (fCurrentSettings.fullscreen_mode != new_set.fullscreen_mode) {
1427 		switch (new_set.fullscreen_mode) {
1428 		case WINDOW_MODE :
1429 			fPreviousFullscreenMode = WINDOW_MODE;
1430 			ResizeTo(fPreviousFrame.Width(), fPreviousFrame.Height());
1431 			MoveTo(fPreviousFrame.left, fPreviousFrame.top);
1432 			break;
1433 		case FULLSCREEN_MODE :
1434 			{
1435 				fPreviousFullscreenMode = FULLSCREEN_MODE;
1436 				if (fCurrentSettings.fullscreen_mode == WINDOW_MODE)
1437 					fPreviousFrame = Frame();
1438 				BScreen	a_screen(this);
1439 				MoveTo(a_screen.Frame().left, a_screen.Frame().top);
1440 				ResizeTo(a_screen.Frame().Width(), a_screen.Frame().Height());
1441 			}
1442 			break;
1443 		case FULLDEMO_MODE :
1444 			{
1445 				fPreviousFullscreenMode = fCurrentSettings.fullscreen_mode;
1446 				if (fCurrentSettings.fullscreen_mode == WINDOW_MODE)
1447 					fPreviousFrame = Frame();
1448 				BScreen	b_screen(this);
1449 				ResizeTo(b_screen.Frame().Width() + LEFT_WIDTH, b_screen.Frame().Height() + TOP_LEFT_LIMIT);
1450 				MoveTo(b_screen.Frame().left - LEFT_WIDTH, b_screen.Frame().top - TOP_LEFT_LIMIT);
1451 			}
1452 			break;
1453 		}
1454 	}
1455 
1456 	/* check for change in the target refresh rate */
1457 	if (fCurrentSettings.refresh_rate != new_set.refresh_rate) {
1458 		fCurrentSettings.refresh_rate = new_set.refresh_rate;
1459 		old_step = fInstantLoad->step;
1460 		fInstantLoad->step = (int32)((fCurrentSettings.refresh_rate+6.0)/12.0);
1461 		if (fInstantLoad->step < 1)
1462 			fInstantLoad->step = 1;
1463 		if (LockWithTimeout(200000) == B_OK) {
1464 			if (old_step != fInstantLoad->step)
1465 				fInstantLoad->Invalidate();
1466 			fRefreshButton->SetEnabledOff(ButtonPicture(false, REFRESH_BUTTON_PICT));
1467 			fRefreshButton->SetEnabledOn(ButtonPicture(true, REFRESH_BUTTON_PICT));
1468 			fRefreshButton->Invalidate();
1469 			Unlock();
1470 		}
1471 		if (fCurrentSettings.animation != ANIMATION_OFF)
1472 			fFrameDelay = (bigtime_t)(1000000.0/new_set.refresh_rate);
1473 	}
1474 
1475 	/* check for change in the star colors list */
1476 	for (i=0; i<7; i++)
1477 		if (fCurrentSettings.colors[i] != new_set.colors[i]) {
1478 			/* if any, get the list of usable color index... */
1479 			color_count = 0;
1480 			for (i=0; i<7; i++)
1481 				if (new_set.colors[i])
1482 					color_index[color_count++] = i;
1483 			/* check that at least one color is enabled */
1484 			if (color_count == 0)
1485 				color_index[color_count++] = 6;
1486 			/* set a new color distribution in the starfield */
1487 			SetStarColors(color_index, color_count);
1488 			break;
1489 		}
1490 
1491 	/* check for change of the special effect setting */
1492 	if (new_set.special != fCurrentSettings.special)
1493 		InitSpecials(new_set.special);
1494 
1495 	/* check for change of the display method */
1496 	if (new_set.display != fCurrentSettings.display) {
1497 		if (new_set.display == DISPLAY_BITMAP) {
1498 			/* check the settings of the offscreen bitmap */
1499 			CheckBitmap(new_set.depth, new_set.width, new_set.height);
1500 			/* synchronise the camera geometry and the offscreen buffer geometry */
1501 			SetGeometry(fBitmapBuffer.buffer_width, fBitmapBuffer.buffer_height);
1502 			/* reset the offscreen background and cancel the erasing */
1503 			SetBitmapBackGround();
1504 			fStars.erase_count = 0;
1505 			fSpecials.erase_count = 0;
1506 		}
1507 		if (new_set.display == DISPLAY_DIRECT) {
1508 			/* this need to be atomic in regard of DirectConnected */
1509 			while (acquire_sem(fDrawingLock) == B_INTERRUPTED)
1510 				;
1511 			/* synchronise the camera geometry and the direct buffer geometry */
1512 			SetGeometry(fDirectBuffer.buffer_width, fDirectBuffer.buffer_height);
1513 			/* cancel erasing of stars not in visible part of the direct window */
1514 			RefreshClipping(&fDirectBuffer, &fStars);
1515 			RefreshClipping(&fDirectBuffer, &fSpecials);
1516 			release_sem(fDrawingLock);
1517 		}
1518 	}
1519 
1520 	/* check for change of the animation mode. */
1521 	if (new_set.animation != fCurrentSettings.animation) {
1522 		/* when there is no camera animation, we loop only
1523 		   10 times per second. */
1524 		if (new_set.animation == ANIMATION_OFF)
1525 			fFrameDelay = 100000;
1526 		else
1527 			fFrameDelay = (bigtime_t)(1000000.0/new_set.refresh_rate);
1528 		/* reset the free camera animation context for a fresh start */
1529 		if (new_set.animation == ANIMATION_FREE_MOVE) {
1530 			fDynamicAlpha = 0.0;
1531 			fDynamicTheta = 0.0;
1532 			fDynamicPhi = 0.0;
1533 			fCountAlpha = 0;
1534 			fCountTheta = 0;
1535 			fCountPhi = 0;
1536 		}
1537 	}
1538 
1539 	/* check for change of starfield model */
1540 	if (new_set.space_model != fCurrentSettings.space_model) {
1541 		/* Generate a new starfield. Also reset the special animation */
1542 		InitStars(new_set.space_model);
1543 		InitSpecials(new_set.special);
1544 	}
1545 
1546 	/* check for change of the background color */
1547 	if ((new_set.back_color.red != fCurrentSettings.back_color.red) ||
1548 		(new_set.back_color.green != fCurrentSettings.back_color.green) ||
1549 		(new_set.back_color.blue != fCurrentSettings.back_color.blue)) {
1550 		if (LockWithTimeout(200000) == B_OK) {
1551 			BScreen		screen(this);
1552 			/* set the background color and it's 8 bits index equivalent */
1553 			fCurrentSettings.back_color = new_set.back_color;
1554 			fBackColorIndex = screen.IndexForColor(new_set.back_color);
1555 			/* set the nackground color of the view (directwindow mode) */
1556 			fChartView->SetViewColor(new_set.back_color);
1557 			/* change the color of the picture button used in the UI */
1558 			fColorButton->SetEnabledOff(ButtonPicture(false, COLOR_BUTTON_PICT));
1559 			fColorButton->SetEnabledOn(ButtonPicture(true, COLOR_BUTTON_PICT));
1560 			fColorButton->Invalidate();
1561 			/* update all dependencies in the offscreen buffer descriptor */
1562 			SetColorSpace(&fBitmapBuffer, fBitmapBuffer.depth);
1563 			/* update all dependencies in the directwindow buffer descriptor */
1564 			while (acquire_sem(fDrawingLock) == B_INTERRUPTED)
1565 				;
1566 			SetColorSpace(&fDirectBuffer, fDirectBuffer.depth);
1567 			release_sem(fDrawingLock);
1568 			/* in offscreen mode, erase the background and cancel star erasing */
1569 			if (new_set.display == DISPLAY_BITMAP) {
1570 				SetBitmapBackGround();
1571 				fStars.erase_count = 0;
1572 				fSpecials.erase_count = 0;
1573 			}
1574 			/* in directwindow mode, just force an update */
1575 			else
1576 				fChartView->Invalidate();
1577 			Unlock();
1578 		}
1579 	}
1580 
1581 	/* check for change of the star animation density */
1582 	if (new_set.star_density != fCurrentSettings.star_density) {
1583 		if (LockWithTimeout(200000) == B_OK) {
1584 			fCurrentSettings.star_density = new_set.star_density;
1585 			/* change the picture button used in the UI */
1586 			fDensityButton->SetEnabledOff(ButtonPicture(false, DENSITY_BUTTON_PICT));
1587 			fDensityButton->SetEnabledOn(ButtonPicture(true, DENSITY_BUTTON_PICT));
1588 			fDensityButton->Invalidate();
1589 			Unlock();
1590 		}
1591 		fStars.count = new_set.star_density;
1592 	}
1593 
1594 	/* check for change in the buffer format for the offscreen bitmap.
1595 	   DirectWindow depth change are always handle in realtime */
1596 	if (new_set.depth != fCurrentSettings.depth) {
1597 		CheckBitmap(new_set.depth, new_set.width, new_set.height);
1598 		/* need to reset the buffer if it's currently used for display */
1599 		if (new_set.display == DISPLAY_BITMAP) {
1600 			SetBitmapBackGround();
1601 			fStars.erase_count = 0;
1602 			fSpecials.erase_count = 0;
1603 		}
1604 	}
1605 
1606 	/* check for change in the drawing area of the offscreen bitmap */
1607 	if ((new_set.width != fCurrentSettings.width) || (new_set.height != fCurrentSettings.height)) {
1608 		CheckBitmap(new_set.depth, new_set.width, new_set.height);
1609 		fBitmapBuffer.buffer_width = new_set.width;
1610 		fBitmapBuffer.buffer_height = new_set.height;
1611 		if (new_set.display == DISPLAY_BITMAP)
1612 			SetGeometry(fBitmapBuffer.buffer_width, fBitmapBuffer.buffer_height);
1613 		SetBitmapClipping(new_set.width, new_set.height);
1614 	}
1615 
1616 	/* copy the new state as the new current state */
1617 	fCurrentSettings.Set(&new_set);
1618 }
1619 
1620 /* Initialise the starfield in the different modes */
1621 void
1622 ChartWindow::InitStars(int32 space_model)
1623 {
1624 	star		*s;
1625 	int32		step;
1626 	int32		amas_select[32];
1627 	float		dx, dy, dz, dist, fact, alpha, r;
1628 	float		factor[8];
1629 	uint32		i, index, i_step;
1630 	TPoint		amas[8];
1631 
1632 	switch (space_model) {
1633 	/* Create a random starfield */
1634 	case SPACE_CHAOS :
1635 		FillStarList(fStars.list, STAR_DENSITY_MAX);
1636 		fKeyPointCount = 0;
1637 		break;
1638 
1639 	/* Create a starfield with big concentration of stars (amas) */
1640 	case SPACE_AMAS :
1641 	case SPACE_SPIRAL :
1642 		/* pick 8 random position for the amas */
1643 		FillStarList(fStars.list, 8);
1644 		for (i=0; i<8; i++) {
1645 			amas[i].x = fStars.list[i].x;
1646 			amas[i].y = fStars.list[i].y;
1647 			amas[i].z = fStars.list[i].z;
1648 			amas_select[i] = i;
1649 			factor[i] = ((float)(fCrcAlea&2047) + 0.5)*(1.0/128.0) + 16.0/3.0;
1650 			CrcStep();
1651 			CrcStep();
1652 		}
1653 		/* make each amas ramdomly smaller or bigger */
1654 		for (i=8; i<32; i++) {
1655 			amas_select[i] = (fCrcAlea & 7);
1656 			CrcStep();
1657 		}
1658 
1659 		/* create a random starfield */
1660 		FillStarList(fStars.list, STAR_DENSITY_MAX);
1661 
1662 		/* In spiral mode, only half the star will be put into the amas.
1663 		   the other half will be put into the spiral galaxy. */
1664 		if (space_model == SPACE_AMAS)
1665 			i_step = 1;
1666 		else
1667 			i_step = 2;
1668 		s = fStars.list;
1669 
1670 		for (i=0; i<STAR_DENSITY_MAX; i+=i_step) {
1671 			/* for every star, calculate its position relative to the
1672 			   center of the corresponding amas. */
1673 			index = amas_select[i&31];
1674 			dx = s->x-amas[index].x;
1675 			if (dx < -0.5) dx += 1.0;
1676 			if (dx > 0.5)  dx -= 1.0;
1677 			dy = s->y-amas[index].y;
1678 			if (dy < -0.5) dy += 1.0;
1679 			if (dy > 0.5)  dy -= 1.0;
1680 			dz = s->z-amas[index].z;
1681 			if (dz < -0.5) dz += 1.0;
1682 			if (dz > 0.5)  dz -= 1.0;
1683 
1684 			/* make the star randomly closer from its center, but keep
1685 			   it on the same orientation. */
1686 			step = 0;
1687 			dist = (abs(dx) + abs(dy) + abs(dz))*factor[index];
1688 			while (dist > 1.0) {
1689 				dist *= 0.5;
1690 				step++;
1691 			}
1692 
1693 			step -= (fCrcAlea&3);
1694 			CrcStep();
1695 			fact = 1.0;
1696 			for (;step>=0; step--)
1697 				fact *= 0.55;
1698 			dx *= fact;
1699 			dy *= fact;
1700 			dz *= fact;
1701 
1702 			/* put the star back in the [0-1]x[0-1]x[0-1] iteration of
1703 			   the cubic torus. */
1704 			s->x = amas[index].x + dx;
1705 			if (s->x >= 1.0) s->x -= 1.0;
1706 			if (s->x <= 0.0) s->x += 1.0;
1707 			s->y = amas[index].y + dy;
1708 			if (s->y >= 1.0) s->y -= 1.0;
1709 			if (s->y <= 0.0) s->y += 1.0;
1710 			s->z = amas[index].z + dz;
1711 			if (s->z >= 1.0) s->z -= 1.0;
1712 			if (s->z <= 0.0) s->z += 1.0;
1713 
1714 			s += i_step;
1715 		}
1716 
1717 		/* record the center of the amas as key points for the free
1718 		   camera animation mode. */
1719 		for (i=0; i<8; i++)
1720 			fKeyPoints[i] = amas[i];
1721 		fKeyPointCount = 8;
1722 
1723 		/* no further processing needed in amas only mode. */
1724 		if (space_model == SPACE_AMAS)
1725 			break;
1726 
1727 		/* in spiral mode, the second half of the star will be distributed
1728 		   on random spiral like galaxy. */
1729 		s = fStars.list+1;
1730 		for (i=1; i<STAR_DENSITY_MAX; i+=2) {
1731 			/* some random point (probability 50 %) will be move into a
1732 			   big amas at the center of the spiral galaxy. */
1733 			if (fCrcAlea & 2048) {
1734 				/* for every star, calculate its position relative to the
1735 				   center of the galaxy. */
1736 				dx = s->x - 0.5;
1737 				dy = s->y - 0.5;
1738 				dz = s->z - 0.5;
1739 
1740 				/* make the star randomly closer from its center, but keep
1741 				   it on the same orientation. */
1742 				step = 0;
1743 				dist = (dx*dx + dy*dy + dz*dz) * (32.0/0.75);
1744 				while (dist > 1.0) {
1745 					dist *= 0.5;
1746 					step++;
1747 				}
1748 
1749 				step -= (fCrcAlea&3);
1750 				CrcStep();
1751 				fact = 0.5;
1752 				for (;step>=0; step--)
1753 					fact *= 0.55;
1754 				dx *= fact;
1755 				dy *= fact;
1756 				dz *= fact;
1757 			}
1758 			else {
1759 				/* other star are put at a random place somewhere on one of
1760 				   teh two spiral arms... */
1761 				alpha = 3.4 * s->x * (s->x*0.5 + 1.0);
1762 				if (fCrcAlea & 64)
1763 					alpha += 3.14159;
1764 				r = s->x * 0.34 + 0.08;
1765 				r += (s->y-0.725 + 0.03 * (float)(fCrcAlea & 15))*0.04*(1.2+r);
1766 				r *= 0.5;
1767 				dx = (s->z-0.8 + 0.04 * (float)(fCrcAlea & 15)) * (2.0 - abs(s->y - 0.5)) * (0.025*0.5);
1768 				dy = cos(alpha) * r;
1769 				dz = sin(alpha) * r;
1770 			}
1771 			CrcStep();
1772 
1773 			/* put the star back in the [0-1]x[0-1]x[0-1] iteration of
1774 			   the cubic torus. */
1775 			s->x = 0.5 + dx;
1776 			s->y = 0.5 + dy;
1777 			s->z = 0.5 + dz;
1778 			s += 2;
1779 		}
1780 
1781 		/* add the center of the galaxy to the key point list for free camera
1782 		   animation mode */
1783 		fKeyPoints[8].x = 0.5;
1784 		fKeyPoints[8].y = 0.5;
1785 		fKeyPoints[8].z = 0.5;
1786 		/* add seven other galaxy star to the key point list */
1787 		for (i=9; i<16; i++) {
1788 			fKeyPoints[i].x = fStars.list[i*(STAR_DENSITY_MAX/18)].x;
1789 			fKeyPoints[i].y = fStars.list[i*(STAR_DENSITY_MAX/18)].y;
1790 			fKeyPoints[i].z = fStars.list[i*(STAR_DENSITY_MAX/18)].z;
1791 		}
1792 		fKeyPointCount = 16;
1793 		break;
1794 	}
1795 
1796 	/* In all starfield modes, for all stars, peek a random brightness level */
1797 	for (i=0; i<STAR_DENSITY_MAX; i++) {
1798 		fStars.list[i].size = (float)((fCrcAlea&15)+17)*(1.0/56.0);
1799 		if ((fCrcAlea & 0xc0) == 0)
1800 			fStars.list[i].size *= 2.0;
1801 		if ((fCrcAlea & 0x3f00) == 0)
1802 			fStars.list[i].size *= 3.0;
1803 		CrcStep();
1804 	}
1805 }
1806 
1807 /* Fill a list of star with random position in the [0-1]x[0-1]x[0-1] cube */
1808 void
1809 ChartWindow::FillStarList(star *list, int32 count)
1810 {
1811 	int32		i;
1812 
1813 	for (i=0; i<count; i++) {
1814 		list[i].x = ((float)(fCrcAlea&2047) + 0.5)*(1.0/2048.0);
1815 		CrcStep();
1816 	}
1817 	for (i=0; i<count; i++) {
1818 		list[i].y = ((float)(fCrcAlea&2047) + 0.5)*(1.0/2048.0);
1819 		CrcStep();
1820 	}
1821 	for (i=0; i<count; i++) {
1822 		list[i].z = ((float)(fCrcAlea&2047) + 0.5)*(1.0/2048.0);
1823 		CrcStep();
1824 	}
1825 }
1826 
1827 /* initialise anything needed to enable a specific special animation */
1828 void
1829 ChartWindow::InitSpecials(int32 code)
1830 {
1831 	int			i, j;
1832 	float		alpha, ksin, kcos, coeff;
1833 	TPoint		dx, dy;
1834 	TMatrix		matrix;
1835 
1836 	switch (code) {
1837 	/* turn special animation off */
1838 	case SPECIAL_NONE :
1839 		fSpecials.count = 0;
1840 		break;
1841 
1842 	/* Initialise the pixel-comet animation */
1843 	case SPECIAL_COMET :
1844 		/* Get a bunchof random values by getting some radom stars */
1845 		fSpecials.count = 512;
1846 		FillStarList(fSpecials.list, 4);
1847 		/* for both comets... */
1848 		for (j=0; j<2; j++) {
1849 			/* select the initial position of the comet head */
1850 			fComet[j].x = fSpecials.list[j].x;
1851 			fComet[j].y = fSpecials.list[j].y;
1852 			fComet[j].z = fSpecials.list[j].z;
1853 			fSpecials.list[0].size = 1.4;
1854 
1855 			/* select the speed vector of the comet */
1856 			matrix.Set(fSpecials.list[j+2].x * 6.28319, fSpecials.list[j+2].y * 3.14159 - 1.5708, 0.0);
1857 			fDeltaComet[j] = matrix.Axis(0) * 0.0015;
1858 			dx = matrix.Axis(1);
1859 			dy = matrix.Axis(2);
1860 
1861 			for (i=j+2; i<fSpecials.count; i+=2) {
1862 				/* make the pixel invisible at first */
1863 				fSpecials.list[i].x = -10.0;
1864 				fSpecials.list[i].y = 0.0;
1865 				fSpecials.list[i].z = 0.0;
1866 				/* spread the initial count on a linear scale (to make pixel
1867 				   appear progressively */
1868 				fSpecialList[i].comet.count = i/2;
1869 				/* spread the pixel trace count randomly on a [93-124] range */
1870 				fSpecialList[i].comet.count0 = (fCrcAlea & 31) + 93;
1871 				CrcStep();
1872 				/* pick a random ejection angle */
1873 				alpha = ((fCrcAlea>>8) & 1023) * (6.283159/1024.0);
1874 				CrcStep();
1875 
1876 				/* pick a random ejection speed */
1877 				coeff = 0.000114 + 0.0000016 * (float)((fCrcAlea>>17) & 31);
1878 				if ((fCrcAlea & 7) > 4) coeff *= 0.75;
1879 				if ((fCrcAlea & 7) == 7) coeff *= 0.65;
1880 				CrcStep();
1881 
1882 				/* calculate the ejection speed vector */
1883 				ksin = sin(alpha) * coeff;
1884 				kcos = cos(alpha) * coeff;
1885 				fSpecialList[i].comet.dx = dx.x * kcos + dy.x * ksin;
1886 				fSpecialList[i].comet.dy = dx.y * kcos + dy.y * ksin;
1887 				fSpecialList[i].comet.dz = dx.z * kcos + dy.z * ksin;
1888 			}
1889 		}
1890 		break;
1891 
1892 	/* Add a list of random star (used for nova effect by modifying their
1893 	   brightness level in real time) close from the first stars of the
1894 	   starfield. */
1895 	case SPECIAL_NOVAS :
1896 		fSpecials.count = 96;
1897 		for (i=0; i<fSpecials.count; i++) {
1898 			fSpecialList[i].nova.count = i + 40;
1899 			fSpecialList[i].nova.count0 = (fCrcAlea & 63) + 28;
1900 			CrcStep();
1901 			fSpecials.list[i].x = fStars.list[i].x + (fCrcAlea & 1)*0.02 - 0.01;
1902 			CrcStep();
1903 			fSpecials.list[i].y = fStars.list[i].y + (fCrcAlea & 1)*0.02 - 0.01;
1904 			CrcStep();
1905 			fSpecials.list[i].z = fStars.list[i].z + (fCrcAlea & 1)*0.02 - 0.01;
1906 			CrcStep();
1907 			fSpecials.list[i].size = 0.0;
1908 		}
1909 		break;
1910 
1911 	/* not implemented */
1912 	case SPECIAL_BATTLE :
1913 		fSpecials.count = 0;
1914 		break;
1915 	}
1916 }
1917 
1918 /* select a color for each star (and special animation point) by
1919    looping through the color index list. */
1920 void
1921 ChartWindow::SetStarColors(int32 *color_list, int32 color_count)
1922 {
1923 	int32		i, index;
1924 
1925 	index = 0;
1926 	for (i=0; i<STAR_DENSITY_MAX; i++) {
1927 		fStars.list[i].color_type = color_list[index];
1928 		index++;
1929 		if (index >= color_count)
1930 			index = 0;
1931 	}
1932 	for (i=0; i<SPECIAL_COUNT_MAX; i++) {
1933 		fSpecials.list[i].color_type = color_list[index];
1934 		index++;
1935 		if (index >= color_count)
1936 			index = 0;
1937 	}
1938 }
1939 
1940 
1941 void
1942 ChartWindow::SetGeometry(int32 dh, int32 dv)
1943 {
1944 	float zoom;
1945 
1946 	/* calculate the zoom factor for the 3d projection */
1947 	fGeometry.zoom_factor = (float)dh*(fDepthRef/DH_REF);
1948 	zoom = (float)dv*(fDepthRef/DV_REF);
1949 	if (zoom > fGeometry.zoom_factor)
1950 		fGeometry.zoom_factor = zoom;
1951 
1952 	/* offset of the origin in the view area */
1953 	fGeometry.offset_h = (float)dh * 0.5;
1954 	fGeometry.offset_v = (float)dv * 0.5;
1955 
1956 	/* sub-pixel precision double-sampling */
1957 	fGeometry.zoom_factor *= 2.0;
1958 	fGeometry.offset_h = fGeometry.offset_h * 2.0 - 1.0;
1959 	fGeometry.offset_v = fGeometry.offset_v * 2.0 - 1.0;
1960 }
1961 
1962 
1963 void
1964 ChartWindow::SetColorSpace(buffer *buf, color_space depth)
1965 {
1966 	bool swap_needed;
1967 	int32 red_shift = 0, green_shift = 0;
1968 	int32 blue_shift = 0, alpha_shift = 0;
1969 	int32 step_doubling = 0;
1970 
1971 	int32 red_divide_shift = 0, green_divide_shift = 0;
1972 	int32 blue_divide_shift = 0, alpha_divide_shift = 0;
1973 	int32 i;
1974 	uint32 color;
1975 	uint32 *col;
1976 	BScreen screen(this);
1977 	rgb_color ref_color;
1978 
1979 	/* depending the colorspace of the target buffer, set parameters used
1980 	   to encode the RGBA information for various color information in
1981 	   the right format. */
1982 	buf->depth = depth;
1983 	switch (depth) {
1984 		case B_RGBA32_BIG :
1985 		case B_RGB32_BIG :
1986 		case B_RGBA32 :
1987 		case B_RGB32 :
1988 			buf->depth_mode = PIXEL_4_BYTES;
1989 			buf->bytes_per_pixel = 4;
1990 			red_shift = 16;
1991 			green_shift = 8;
1992 			blue_shift = 0;
1993 			alpha_shift = 24;
1994 			red_divide_shift = 0;
1995 			green_divide_shift = 0;
1996 			blue_divide_shift = 0;
1997 			alpha_divide_shift = 0;
1998 			step_doubling = 32;
1999 			break;
2000 		case B_RGB16_BIG :
2001 		case B_RGB16 :
2002 			buf->depth_mode = PIXEL_2_BYTES;
2003 			buf->bytes_per_pixel = 2;
2004 			red_shift = 11;
2005 			red_divide_shift = 3;
2006 			green_shift = 5;
2007 			green_divide_shift = 2;
2008 			blue_shift = 0;
2009 			blue_divide_shift = 3;
2010 			alpha_shift = 32;
2011 			alpha_divide_shift = 8;
2012 			step_doubling = 16;
2013 			break;
2014 		case B_RGB15 :
2015 		case B_RGBA15 :
2016 		case B_RGB15_BIG :
2017 		case B_RGBA15_BIG :
2018 			buf->depth_mode = PIXEL_2_BYTES;
2019 			buf->bytes_per_pixel = 2;
2020 			red_shift = 10;
2021 			red_divide_shift = 3;
2022 			green_shift = 5;
2023 			green_divide_shift = 3;
2024 			blue_shift = 0;
2025 			blue_divide_shift = 3;
2026 			alpha_shift = 15;
2027 			alpha_divide_shift = 7;
2028 			step_doubling = 16;
2029 			break;
2030 		case B_CMAP8 :
2031 		default:
2032 			buf->depth_mode = PIXEL_1_BYTE;
2033 			buf->bytes_per_pixel = 1;
2034 			break;
2035 	}
2036 
2037 	/* Check if the endianess of the buffer is different from the
2038 	   endianess use by the processor to encode the color information */
2039 	switch (depth) {
2040 		case B_RGBA32_BIG :
2041 		case B_RGB32_BIG :
2042 		case B_RGB16_BIG :
2043 		case B_RGB15_BIG :
2044 		case B_RGBA15_BIG :
2045 			swap_needed = true;
2046 			break;
2047 		case B_RGBA32 :
2048 		case B_RGB32 :
2049 		case B_RGB16 :
2050 		case B_RGB15 :
2051 		case B_RGBA15 :
2052 		case B_CMAP8 :
2053 		default:
2054 			swap_needed = false;
2055 			break;
2056 	}
2057 
2058 #if B_HOST_IS_BENDIAN
2059 	swap_needed = ~swap_needed;
2060 #endif
2061 	/* fill the color tables (8 light level for 7 colors, and also encode
2062 	   the background color */
2063 	col = buf->colors[0];
2064 	switch (buf->depth_mode) {
2065 		case PIXEL_1_BYTE :
2066 			/* 8 bits, indexed mode */
2067 			for (i=0; i<7*8; i++) {
2068 				ref_color = color_list[i>>3];
2069 				ref_color.red   = (ref_color.red*light_gradient[i&7])>>16;
2070 				ref_color.green = (ref_color.green*light_gradient[i&7])>>16;
2071 				ref_color.blue  = (ref_color.blue*light_gradient[i&7])>>16;
2072 				color = screen.IndexForColor(ref_color);
2073 				col[i] = (color<<24) | (color<<16) | (color<<8) | color;
2074 			}
2075 			color = screen.IndexForColor(fCurrentSettings.back_color);
2076 			buf->back_color = (color<<24) | (color<<16) | (color<<8) | color;
2077 			break;
2078 		case PIXEL_2_BYTES :
2079 		case PIXEL_4_BYTES :
2080 			/* 15, 16 or 32 bytes, RGB modes. Those modes just directly encode
2081 			   part of the bits of the initial rgba_color, at the right bit
2082 			   position */
2083 			for (i=0; i<7*8; i++) {
2084 				ref_color = color_list[i>>3];
2085 				ref_color.red   = (ref_color.red*light_gradient[i&7])>>16;
2086 				ref_color.green = (ref_color.green*light_gradient[i&7])>>16;
2087 				ref_color.blue  = (ref_color.blue*light_gradient[i&7])>>16;
2088 				color = ((uint8)ref_color.red >> red_divide_shift) << red_shift;
2089 				color |= ((uint8)ref_color.green >> green_divide_shift) << green_shift;
2090 				color |= ((uint8)ref_color.blue >> blue_divide_shift) << blue_shift;
2091 				color |= ((uint8)ref_color.alpha >> alpha_divide_shift) << alpha_shift;
2092 				col[i] = (color<<step_doubling) | color;
2093 			}
2094 			color = ((uint8)fCurrentSettings.back_color.red >> red_divide_shift) << red_shift;
2095 			color |= ((uint8)fCurrentSettings.back_color.green >> green_divide_shift) << green_shift;
2096 			color |= ((uint8)fCurrentSettings.back_color.blue >> blue_divide_shift) << blue_shift;
2097 			color |= ((uint8)fCurrentSettings.back_color.alpha >> alpha_divide_shift) << alpha_shift;
2098 			buf->back_color = (color<<step_doubling) | color;
2099 			break;
2100 	}
2101 
2102 	/* do the endianess swap if needed */
2103 	if (swap_needed) {
2104 		col = buf->colors[0];
2105 		for (i = 0; i < 7*8; i++) {
2106 			B_SWAP_INT32(col[i]);
2107 		}
2108 		B_SWAP_INT32(buf->back_color);
2109 	}
2110 }
2111 
2112 
2113 /*!
2114 	For each different offset used to access a pixel of the star matrix,
2115 	create a buffer pointer based on the main buffer pointer offset by
2116 	the pixel matrix offset. That way, any pixel of the matrix can be
2117 	address later by just picking the right pointer and indexing it by
2118 	the global star offset
2119 */
2120 void
2121 ChartWindow::SetPatternBits(buffer *buf)
2122 {
2123 	for (int32 i=0; i<32; i++) {
2124 		buf->pattern_bits[i] = (void*)((char*)buf->bits +
2125 			buf->bytes_per_row * pattern_dv[i] +
2126 			buf->bytes_per_pixel * pattern_dh[i]);
2127 	}
2128 }
2129 
2130 
2131 //	#pragma mark Engine processing related functions.
2132 
2133 
2134 /*!
2135 	That's the main thread controling the animation and synchronising
2136 	the engine state with the changes coming from the UI.
2137 */
2138 long
2139 ChartWindow::Animation(void *data)
2140 {
2141 	int32			i, cur_4_frames_index, cur_last_fps, count_fps;
2142 	float			time_factor = 0, total_fps;
2143 	float			last_fps[4];
2144 	bigtime_t		next_stat;
2145 	bigtime_t		timer, time_left, current;
2146 	bigtime_t		before_frame, after_frame, fps;
2147 	bigtime_t		last_4_frames[4];
2148 	ChartWindow		*w;
2149 
2150 	w = (ChartWindow*)data;
2151 
2152 	/* init refresh rate control */
2153 	timer = system_time();
2154 	w->fFrameDelay = 100000;
2155 
2156 	/* init performance timing control variables */
2157 	next_stat = timer + STAT_DELAY;
2158 	cur_4_frames_index = 0;
2159 	cur_last_fps = 0;
2160 	for (i=0; i<4; i++)
2161 		last_fps[i] = 0.0;
2162 	total_fps = 0.0;
2163 	count_fps = 0;
2164 
2165 	/* here start the loop doing all the good stuff */
2166 	while (!w->fKillThread) {
2167 
2168 		/* start the performance mesurement here */
2169 		before_frame = system_time();
2170 
2171 		/* credit the timer by the current delay between frame */
2172 		timer += w->fFrameDelay;
2173 
2174 		/* change the settings, if needed */
2175 		w->ChangeSetting(w->fNextSettings);
2176 
2177 		/* draw the next frame */
2178 		if (w->fCurrentSettings.display == DISPLAY_BITMAP) {
2179 			w->RefreshStars(&w->fBitmapBuffer, time_factor * 2.4);
2180 			if (w->LockWithTimeout(200000) == B_OK) {
2181 				w->fChartView->DrawBitmap(w->fOffscreen);
2182 				w->Unlock();
2183 			}
2184 		}
2185 		else if (w->fCurrentSettings.display == DISPLAY_DIRECT) {
2186 			/* This part get the drawing-lock to guarantee that the
2187 			   directbuffer context won't change during the drawing
2188 			   operations. During that period, no Window should be
2189 			   done to avoid any potential deadlock. */
2190 			while (acquire_sem(w->fDrawingLock) == B_INTERRUPTED)
2191 				;
2192 
2193 			if (w->fDirectConnected)
2194 				w->RefreshStars(&w->fDirectBuffer, time_factor * 2.4);
2195 
2196 			release_sem(w->fDrawingLock);
2197 		}
2198 
2199 		/* do the camera animation */
2200 		w->CameraAnimation(time_factor);
2201 
2202 		/* end the performance mesurement here */
2203 		after_frame = system_time();
2204 
2205 		/* performance timing calculation here (if display enabled). */
2206 		if (w->fCurrentSettings.display != DISPLAY_OFF) {
2207 			/* record frame duration into a 2 levels 4 entries ring buffer */
2208 			last_4_frames[cur_4_frames_index] = after_frame - before_frame;
2209 			cur_4_frames_index++;
2210 			if (cur_4_frames_index == 4) {
2211 				cur_4_frames_index = 0;
2212 				last_fps[cur_last_fps++ & 3] =
2213 					last_4_frames[0]+last_4_frames[1]+last_4_frames[2]+last_4_frames[3];
2214 				/* the instant load is calculated based on the average duration
2215 				   of the last 16 frames. */
2216 				fps = (bigtime_t) (16e6 / (last_fps[0]+last_fps[1]+last_fps[2]+last_fps[3]));
2217 				w->DrawInstantLoad(fps);
2218 
2219 				total_fps += fps;
2220 				count_fps += 1;
2221 
2222 				/* The statistic numbers are based on the ratio between the real
2223 				   duration and the frame count during a period of approximately
2224 				   STAT_DELAY microseconds. */
2225 				if (after_frame > next_stat) {
2226 					w->PrintStatNumbers(total_fps/(float)count_fps);
2227 					next_stat = after_frame+STAT_DELAY;
2228 					total_fps = 0.0;
2229 					count_fps = 0;
2230 				}
2231 			}
2232 		}
2233 
2234 		/* do a pause if necessary */
2235 		current = system_time();
2236 		time_left = timer-current;
2237 		if (time_left > 2000) {
2238 			snooze(time_left);
2239 			time_left = 0;
2240 		}
2241 		else if (time_left < -5000)
2242 			timer = current;
2243 
2244 		/* this factor controls the dynamic timing configuration, that
2245 		   slow down or speed up the whole animation step to compensate
2246 		   for varaiation of the framerate. */
2247 		time_factor = (float)(system_time() - before_frame) * (1.0/4e4);
2248 	}
2249 	return 0;
2250 }
2251 
2252 /* This is the second thread doing star animation. It's just a poor
2253    slave of the Animation thread. It's directly synchronised with its
2254    master, and will only do some star animation processing whenever
2255    its master allows him to do so. */
2256 long
2257 ChartWindow::Animation2(void *data)
2258 {
2259 	ChartWindow *w = (ChartWindow*)data;
2260 	while (!w->fKillThread) {
2261 		/* This thread need to both wait for its master to unblock
2262 		   him to do some real work, or for the main control to
2263 		   set the fKillThread flag, asking it to quit. */
2264 		status_t status;
2265 		do {
2266 			status = acquire_sem_etc(w->fSecondThreadLock, 1, B_TIMEOUT, 500000);
2267 			if (w->fKillThread)
2268 				return 0;
2269 		} while (status == B_TIMED_OUT || status == B_INTERRUPTED);
2270 
2271 		/* the duration of the processing is needed to control the
2272 		   dynamic load split (see RefreshStar) */
2273 		bigtime_t before = system_time();
2274 		RefreshStarPacket(w->fSecondThreadBuffer, &w->fStars2, &w->fGeometry);
2275 		RefreshStarPacket(w->fSecondThreadBuffer, &w->fSpecials2, &w->fGeometry);
2276 		bigtime_t after = system_time();
2277 
2278 		w->fSecondThreadDelay = after-before;
2279 
2280 		release_sem(w->fSecondThreadRelease);
2281 	}
2282 	return 0;
2283 }
2284 
2285 
2286 void
2287 ChartWindow::SetCubeOffset()
2288 {
2289 	int32		i;
2290 	TPoint		min, max, dx, dy, dz, p1;
2291 
2292 	/* calculate the shortest aligned cube encapsulating the pyramid
2293 	   of vision, by calculating the min and max on the 3 main axis
2294 	   of the coordinates of the 8 extremities of the pyramid of
2295 	   vision (as limited by its 4 sides and the rear and front
2296 	   cut plan) */
2297 	min.x = min.y = min.z = 10.0;
2298 	max.x = max.y = max.z = -10.0;
2299 
2300 	dx = fCamera.Axis(0)*(DH_REF*0.5);
2301 	dy = fCamera.Axis(1)*(DV_REF*0.5);
2302 	dz = fCamera.Axis(2)*fDepthRef;
2303 
2304 	for (i=0; i<8; i++) {
2305 		/* left side / right side */
2306 		if (i&1) p1 = dz + dx;
2307 		else	 p1 = dz - dx;
2308 		/* top side / bottom side */
2309 		if (i&2) p1 = p1 + dy;
2310 		else	 p1 = p1 - dy;
2311 		/* rear cut plan / front cut plan */
2312 		if (i&4) p1 = p1 * (1.0 / Z_CUT_RATIO);
2313 		/* relative to the position of the camera */
2314 		p1 = p1 + fOrigin;
2315 
2316 		if (min.x > p1.x) min.x = p1.x;
2317 		if (min.y > p1.y) min.y = p1.y;
2318 		if (min.z > p1.z) min.z = p1.z;
2319 		if (max.x < p1.x) max.x = p1.x;
2320 		if (max.y < p1.y) max.y = p1.y;
2321 		if (max.z < p1.z) max.z = p1.z;
2322 	}
2323 
2324 	/* offset the camera origin by +1 or -1 on any axis (which
2325 	   doesn't change its relative position in the cubic torus
2326 	   as the cubic torus repeat itself identicaly for any move
2327 	   of +1 or -1 on any axis), to get the bounding cube into
2328 	   [0-2[ x [0-2[ x [0-2[. As the pyramid of vision is just
2329 	   small enough to gurantee that its bounding box will never
2330 	   be larger than 1 on any axis, it's always possible. */
2331 	while (min.x < 0.0) {
2332 		min.x += 1.0;
2333 		max.x += 1.0;
2334 		fOrigin.x += 1.0;
2335 	}
2336 	while (min.y < 0.0) {
2337 		min.y += 1.0;
2338 		max.y += 1.0;
2339 		fOrigin.y += 1.0;
2340 	}
2341 	while (min.z < 0.0) {
2342 		min.z += 1.0;
2343 		max.z += 1.0;
2344 		fOrigin.z += 1.0;
2345 	}
2346 	while (max.x >= 2.0) {
2347 		min.x -= 1.0;
2348 		max.x -= 1.0;
2349 		fOrigin.x -= 1.0;
2350 	}
2351 	while (max.y >= 2.0) {
2352 		min.y -= 1.0;
2353 		max.y -= 1.0;
2354 		fOrigin.y -= 1.0;
2355 	}
2356 	while (max.z >= 2.0) {
2357 		min.z -= 1.0;
2358 		max.z -= 1.0;
2359 		fOrigin.z -= 1.0;
2360 	}
2361 
2362 	/* set the cutting plans. For example, if the bouding box of
2363 	   the pyramid of vision of the camera imcludes only X in
2364 	   [0.43 ; 1.37], we know that points with X in [0 ; 0.4] are
2365 	   not visible from the camera. So we will offset them by +1
2366 	   in [1.0 ; 1.4] where they will be visible. Same process
2367 	   on other axis. That way, we have to test every star of the
2368 	   starfield in one position and only one. */
2369 	fCut.x = (min.x + max.x - 1.0) * 0.5;
2370 	fCut.y = (min.y + max.y - 1.0) * 0.5;
2371 	fCut.z = (min.z + max.z - 1.0) * 0.5;
2372 
2373 	/* Make sure those new settings are copied into the struct
2374 	   used by the embedded C-engine. */
2375 	SyncGeo();
2376 }
2377 
2378 /* move the camera around, as defined by the animation popup.
2379    This is adjusted by a time factor to compensate for change
2380    in the framerate. */
2381 void
2382 ChartWindow::CameraAnimation(float time_factor)
2383 {
2384 	TPoint			move;
2385 
2386 	switch (fCurrentSettings.animation) {
2387 	/* Slow rotation around the "center" of the visible area. */
2388 	case ANIMATION_ROTATE :
2389 		/* turn around a point at 0.45 in front of the camera */
2390 		move = fCamera.Axis(2);
2391 		move = move * 0.45;
2392 		fOrigin = fOrigin + move;
2393 
2394 		/* turn around the alpha angle of the spheric rotation
2395 		   matrix */
2396 		fCameraAlpha += 0.011*time_factor;
2397 		if (fCameraAlpha > 2*3.14159)
2398 			fCameraAlpha -= 2*3.14159;
2399 
2400 		/* set the other two angles close from hardcoded values */
2401 		if (fCameraTheta < 0.18)
2402 			fCameraTheta += 0.003*time_factor;
2403 		if (fCameraTheta > 0.22)
2404 			fCameraTheta -= 0.003*time_factor;
2405 
2406 		if (fCameraPhi < -0.02)
2407 			fCameraPhi += 0.003*time_factor;
2408 		if (fCameraPhi > 0.02)
2409 			fCameraPhi -= 0.003*time_factor;
2410 
2411 		fCamera.Set(fCameraAlpha, fCameraTheta, fCameraPhi);
2412 		fCameraInvert = fCamera.Transpose();
2413 		move = fCamera.Axis(2);
2414 		move = move * -0.45;
2415 		fOrigin = fOrigin + move;
2416 		/* As we moved or rotated the camera, we need to process
2417 		   again the parameters specific to the pyramid of vision. */
2418 		SetCubeOffset();
2419 		break;
2420 
2421 	case ANIMATION_SLOW_MOVE :
2422 		/* Just move forward, at slow speed */
2423 		move = fCamera.Axis(2);
2424 		move = move * 0.006*time_factor;
2425 		fOrigin = fOrigin + move;
2426 		SetCubeOffset();
2427 		break;
2428 
2429 	case ANIMATION_FAST_MOVE :
2430 		/* Just move forward, at fast speed */
2431 		move = fCamera.Axis(2);
2432 		move = move * 0.018*time_factor;
2433 		fOrigin = fOrigin + move;
2434 		SetCubeOffset();
2435 		break;
2436 
2437 	case ANIMATION_FREE_MOVE :
2438 		/* go into advanced selection process no more than once every
2439 		   0.5 time unit (average time). */
2440 		fLastDynamicDelay += time_factor;
2441 		if (fLastDynamicDelay > 0.5) {
2442 			fLastDynamicDelay -= 0.5;
2443 			if (fLastDynamicDelay > 0.2)
2444 				fLastDynamicDelay = 0.2;
2445 
2446 			/* if we're not following any target, then just turn
2447 			   randomly (modifying only the direction of the
2448 			   acceleration) */
2449 			if (fTrackingTarget < 0) {
2450 				if ((fCrcAlea & 0x4200) == 0) {
2451 					if (fCrcAlea & 0x8000)
2452 						fCountAlpha += 1 - (fCountAlpha/4);
2453 					else
2454 						fCountAlpha += -1 - (fCountAlpha/4);
2455 					CrcStep();
2456 					if (fCrcAlea & 0x8000)
2457 						fCountTheta += 1 - (fCountTheta/4);
2458 					else
2459 						fCountTheta += -1 - (fCountTheta/4);
2460 					CrcStep();
2461 					if (fCrcAlea & 0x8000)
2462 						fCountPhi += 1 - (fCountPhi/4);
2463 					else
2464 						fCountPhi += -1 - (fCountPhi/4);
2465 					CrcStep();
2466 				}
2467 				CrcStep();
2468 			}
2469 			/* if following a target, try to turn in its direction */
2470 			else
2471 				FollowTarget();
2472 
2473 			/* Change target everyonce in a while... */
2474 			if ((fCrcAlea & 0xf80) == 0)
2475 				SelectNewTarget();
2476 
2477 			/* depending the direction of acceleration, increase or
2478 			   reduce the angular speed of the 3 spherical angles. */
2479 			if (fCountAlpha < 0)
2480 				fDynamicAlpha += -0.0005 - fDynamicAlpha * 0.025;
2481 			else if (fCountAlpha > 0)
2482 				fDynamicAlpha += 0.0005 - fDynamicAlpha * 0.025;
2483 
2484 			if (fCountTheta < 0)
2485 				fDynamicTheta += -0.0002 - fDynamicTheta * 0.025;
2486 			else if (fCountTheta > 0)
2487 				fDynamicTheta += 0.0002 - fDynamicTheta * 0.025;
2488 
2489 			if (fCountPhi < 0)
2490 				fDynamicPhi += -0.00025 - fDynamicPhi * 0.025;
2491 			else if (fCountPhi >0)
2492 				fDynamicPhi += 0.00025 - fDynamicPhi * 0.025;
2493 		}
2494 
2495 		/* turn the camera following the specified angular speed */
2496 		fCameraAlpha += fDynamicAlpha*time_factor;
2497 		if (fCameraAlpha < 0.0)
2498 			fCameraAlpha += 2*3.14159;
2499 		else if (fCameraAlpha > 2*3.14159)
2500 			fCameraAlpha -= 2*3.14159;
2501 
2502 		fCameraTheta += fDynamicTheta*time_factor;
2503 		if (fCameraTheta < 0.0)
2504 			fCameraTheta += 2*3.14159;
2505 		else if (fCameraTheta > 2*3.14159)
2506 			fCameraTheta -= 2*3.14159;
2507 
2508 		fCameraPhi += fDynamicPhi*time_factor;
2509 		if (fCameraPhi < 0.0)
2510 			fCameraPhi += 2*3.14159;
2511 		else if (fCameraPhi > 2*3.14159)
2512 			fCameraPhi -= 2*3.14159;
2513 
2514 		/* Set the new rotation matrix of the camera */
2515 		fCamera.Set(fCameraAlpha, fCameraTheta, fCameraPhi);
2516 		fCameraInvert = fCamera.Transpose();
2517 
2518 		/* move the camera forward at medium speed */
2519 		move = fCamera.Axis(2);
2520 		move = move * 0.0115*time_factor;
2521 		fOrigin = fOrigin + move;
2522 		SetCubeOffset();
2523 		break;
2524 	}
2525 }
2526 
2527 
2528 void
2529 ChartWindow::SelectNewTarget()
2530 {
2531 	float		ratio, ratio_min;
2532 	float		dist, lateral, axial, ftmp;
2533 	int32		i, index_min;
2534 	TPoint		axis, pt, vect;
2535 
2536 	axis = fCamera.Axis(2);
2537 	ratio_min = 1e6;
2538 	index_min = -3;
2539 
2540 	for (i=-2; i<fKeyPointCount; i++) {
2541 		/* if they're used, the comets are two good potential
2542 		   targets. */
2543 		if (i < 0) {
2544 			if (fCurrentSettings.special == SPECIAL_COMET)
2545 				pt = fComet[i+2];
2546 			else
2547 				continue;
2548 		}
2549 		/* other potential targets are the key_points defined
2550 		   in the star field. */
2551 		else
2552 			pt = fKeyPoints[i];
2553 
2554 		/* Qualify the interest of the potential target in
2555 		   relationship with its distance and its proximity to
2556 		   the axis of the camera. */
2557 		if (pt.x < fCut.x)
2558 			pt.x += 1.0;
2559 		if (pt.y < fCut.y)
2560 			pt.y += 1.0;
2561 		if (pt.z < fCut.z)
2562 			pt.z += 1.0;
2563 		pt = pt - fOrigin;
2564 		dist = pt.Length();
2565 		ftmp = 1.0/dist;
2566 		pt.x *= ftmp;
2567 		pt.y *= ftmp;
2568 		pt.z *= ftmp;
2569 		vect = pt ^ axis;
2570 		lateral = axis.Length();
2571 		axial = pt.x*axis.x + pt.y*axis.y + pt.z*axis.z;
2572 		ratio = (lateral/axial) * sqrt(dist);
2573 
2574 		/* keep track of the best possible choice */
2575 		if ((dist > 0.05) && (ratio < ratio_min)) {
2576 			ratio_min = ratio;
2577 			index_min = i;
2578 		}
2579 	}
2580 
2581 	/* record what target has been chosen */
2582 	fTrackingTarget = index_min+2;
2583 }
2584 
2585 /* Try to change the angular acceleration to aim in direction
2586    of the current target. */
2587 void
2588 ChartWindow::FollowTarget()
2589 {
2590 	float		x0, y0, x, y, z, cphi, sphi;
2591 	TPoint		pt;
2592 
2593 	/* get the target point */
2594 	if (fTrackingTarget < 2)
2595 		pt = fComet[fTrackingTarget];
2596 	else
2597 		pt = fKeyPoints[fTrackingTarget-2];
2598 	/* move it in the right iteration of the cubic torus (the
2599 	   one iteration that is the most likely to be close from
2600 	   the pyramid of vision. */
2601 	if (pt.x < fCut.x)
2602 		pt.x += 1.0;
2603 	if (pt.y < fCut.y)
2604 		pt.y += 1.0;
2605 	if (pt.z < fCut.z)
2606 		pt.z += 1.0;
2607 	/* convert the target coordinates in the camera referential */
2608 	pt = pt - fOrigin;
2609 	x = fCameraInvert.m[0][0]*pt.x + fCameraInvert.m[1][0]*pt.y + fCameraInvert.m[2][0]*pt.z;
2610 	y = fCameraInvert.m[0][1]*pt.x + fCameraInvert.m[1][1]*pt.y + fCameraInvert.m[2][1]*pt.z;
2611 	z = fCameraInvert.m[0][2]*pt.x + fCameraInvert.m[1][2]*pt.y + fCameraInvert.m[2][2]*pt.z;
2612 	if (z <= 0.001) {
2613 		/* need to do a U-turn (better to do it using theta). */
2614 		fCountAlpha = 0;
2615 		fCountTheta = -1;
2616 		fCountPhi = 0;
2617 	}
2618 	else {
2619 		/* need to do a direction adjustement (play with
2620 		   alpha and theta) */
2621 		cphi = cos(fCameraPhi);
2622 		sphi = sin(fCameraPhi);
2623 		x0 = x*cphi - y*sphi;
2624 		y0 = x*sphi + y*cphi;
2625 
2626 		/* need to move first on the left/right axis */
2627 		if (abs(x0) > abs(y0)) {
2628 			if (x0 > 0)
2629 				fCountAlpha = -1;
2630 			else
2631 				fCountAlpha = 1;
2632 			fCountTheta = 0;
2633 			fCountPhi = 0;
2634 		}
2635 		/* need to move first on the top/bottom axis */
2636 		else {
2637 			if (y0 > 0)
2638 				fCountTheta = -1;
2639 			else
2640 				fCountTheta = 1;
2641 			fCountAlpha = 0;
2642 			fCountPhi = 0;
2643 		}
2644 	}
2645 }
2646 
2647 /* Do whatever special processing is required to do special
2648    animation. This used a time_step (or time_factor) to
2649    compensate for change in the framerate of the animation. */
2650 void
2651 ChartWindow::AnimSpecials(float time_step)
2652 {
2653 	int			i, j;
2654 	star		*s;
2655 	float		delta;
2656 	special		*sp;
2657 
2658 	switch (fCurrentSettings.special) {
2659 	case SPECIAL_COMET :
2660 		/* for both comets... */
2661 		for (j=0; j<2; j++) {
2662 			/* move the comet forward, at its specific speed */
2663 			fComet[j] = fComet[j] + fDeltaComet[j] * time_step;
2664 			/* Insure that the comet stays in the [0-1]x[0-1]x[0-1]
2665 			   iteration of the cubic torus. */
2666 			if (fComet[j].x < 0.0) fComet[j].x += 1.0;
2667 			else if (fComet[j].x > 1.0) fComet[j].x -= 1.0;
2668 			if (fComet[j].y < 0.0) fComet[j].y += 1.0;
2669 			else if (fComet[j].y > 1.0) fComet[j].y -= 1.0;
2670 			if (fComet[j].z < 0.0) fComet[j].z += 1.0;
2671 			else if (fComet[j].z > 1.0) fComet[j].z -= 1.0;
2672 			/* set the position of the star used to represent the
2673 			   head of the comet. */
2674 			fSpecials.list[j].x = fComet[j].x;
2675 			fSpecials.list[j].y = fComet[j].y;
2676 			fSpecials.list[j].z = fComet[j].z;
2677 
2678 			/* for other point, the ones that are ejected from the
2679 			   comet, depending for allow long they have been ejected... */
2680 			s = fSpecials.list+j+2;
2681 			sp = fSpecialList+j+2;
2682 			for (i=j+2; i<fSpecials.count; i+=2) {
2683 				sp->comet.count -= (int32)time_step;
2684 				/* they are reset and reejected again, just a little in
2685 				   the back of the head of the comet */
2686 				if (sp->comet.count <= 0.0) {
2687 					delta = (0.6 + (float)(fCrcAlea & 31) * (1.0/32.0)) * time_step;
2688 					s->x = fComet[j].x + 6.0 * sp->comet.dx - fDeltaComet[j].x * delta;
2689 					s->y = fComet[j].y + 6.0 * sp->comet.dy - fDeltaComet[j].y * delta;
2690 					s->z = fComet[j].z + 6.0 * sp->comet.dz - fDeltaComet[j].z * delta;
2691 					s->size = 0.6;
2692 					sp->comet.count = (int32)(sp->comet.count0 + (fCrcAlea & 63));
2693 					CrcStep();
2694 				}
2695 				/* or they just move at their own (ejection) speed */
2696 				else {
2697 					s->x += sp->comet.dx * time_step;
2698 					s->y += sp->comet.dy * time_step;
2699 					s->z += sp->comet.dz * time_step;
2700 					s->size *= (1.0 - 0.031 * time_step + 0.001 * time_step * time_step);
2701 				}
2702 				sp+=2;
2703 				s+=2;
2704 			}
2705 		}
2706 		break;
2707 
2708 	case SPECIAL_NOVAS :
2709 		/* Novas are just stars (usualy invisible) that periodically
2710 		   become much brighter during a suddent flash, then disappear
2711 		   again until their next cycle */
2712 		sp = fSpecialList;
2713 		for (i=0; i<fSpecials.count; i++) {
2714 			sp->nova.count -= time_step;
2715 			if (sp->nova.count <= 0.0) {
2716 				fSpecials.list[i].x -= 10.0;
2717 				sp->nova.count = sp->nova.count0 + (fCrcAlea & 31);
2718 				CrcStep();
2719 			}
2720 			else if (sp->nova.count < 16.0) {
2721 				if (fSpecials.list[i].x < 0.0)
2722 					fSpecials.list[i].x += 10.0;
2723 				fSpecials.list[i].size = sp->nova.count;
2724 			}
2725 			sp++;
2726 		}
2727 		break;
2728 
2729 	case SPECIAL_BATTLE :
2730 		/* not implemented */
2731 		break;
2732 	}
2733 }
2734 
2735 
2736 /* Sync the embedded camera state with the window class camera
2737    state (before calling the embedded C-engine in ChartRender.c */
2738 void
2739 ChartWindow::SyncGeo()
2740 {
2741 	fGeometry.x = fOrigin.x;
2742 	fGeometry.y = fOrigin.y;
2743 	fGeometry.z = fOrigin.z;
2744 	fGeometry.cutx = fCut.x;
2745 	fGeometry.cuty = fCut.y;
2746 	fGeometry.cutz = fCut.z;
2747 	memcpy(fGeometry.m, fCameraInvert.m, sizeof(float)*9);
2748 }
2749 
2750 
2751 void
2752 ChartWindow::RefreshStars(buffer *buf, float time_step)
2753 {
2754 	/* do the specials animation (single-threaded) */
2755 	AnimSpecials(time_step);
2756 
2757 	/* do the projection, clipping, erase and redraw
2758 	   of all stars. This operation is done by the
2759 	   embedded C-engine. This code only control the
2760 	   dynamic load split between the two threads, when
2761 	   needed. */
2762 	if (fCurrentSettings.second_thread) {
2763 		int32 star_threshold = (int32)((float)fStars.count * fSecondThreadThreshold + 0.5);
2764 		int32 special_threshold = (int32)((float)fSpecials.count * fSecondThreadThreshold + 0.5);
2765 
2766 		/* split the work load (star and special animation)
2767 		   between the two threads, proportionnaly to the
2768 		   last split factor determined during the last
2769 		   cycle. */
2770 		star_packet stars1;
2771 		stars1.list = fStars.list;
2772 		stars1.count = star_threshold;
2773 		stars1.erase_count = star_threshold;
2774 		if (stars1.erase_count > fStars.erase_count)
2775 			stars1.erase_count = fStars.erase_count;
2776 
2777 		fStars2.list = fStars.list + star_threshold;
2778 		fStars2.count = fStars.count - star_threshold;
2779 		fStars2.erase_count = fStars.erase_count - star_threshold;
2780 		if (fStars2.erase_count < 0)
2781 			fStars2.erase_count = 0;
2782 
2783 		star_packet specials1;
2784 		specials1.list = fSpecials.list;
2785 		specials1.count = special_threshold;
2786 		specials1.erase_count = special_threshold;
2787 		if (specials1.erase_count > fSpecials.erase_count)
2788 			specials1.erase_count = fSpecials.erase_count;
2789 
2790 		fSpecials2.list = fSpecials.list + special_threshold;
2791 		fSpecials2.count = fSpecials.count - special_threshold;
2792 		fSpecials2.erase_count = fSpecials.erase_count - special_threshold;
2793 		if (fSpecials2.erase_count < 0)
2794 			fSpecials2.erase_count = 0;
2795 
2796 		fSecondThreadBuffer = buf;
2797 
2798 		/* release the slave thread */
2799 		release_sem(fSecondThreadLock);
2800 
2801 		/* do its own part (time it) */
2802 		bigtime_t before = system_time();
2803 		RefreshStarPacket(buf, &stars1, &fGeometry);
2804 		RefreshStarPacket(buf, &specials1, &fGeometry);
2805 		bigtime_t after = system_time();
2806 
2807 		/* wait for completion of the second thread */
2808 		while (acquire_sem(fSecondThreadRelease) == B_INTERRUPTED)
2809 			;
2810 
2811 		/* calculate the new optimal split ratio depending
2812 		   of the previous one and the time used by both
2813 		   threads to do their work. */
2814 		float ratio = ((float)fSecondThreadDelay/(float)(after-before)) *
2815 				(fSecondThreadThreshold/(1.0-fSecondThreadThreshold));
2816 		fSecondThreadThreshold = ratio / (1.0+ratio);
2817 
2818 	} else {
2819 		 /* In single-threaded mode, nothing fancy to be done. */
2820 		RefreshStarPacket(buf, &fStars, &fGeometry);
2821 		RefreshStarPacket(buf, &fSpecials, &fGeometry);
2822 	}
2823 
2824 	/* All the stars that were drawn will have to be erased during
2825 	   the next frame. */
2826 	fStars.erase_count = fStars.count;
2827 	fSpecials.erase_count = fSpecials.count;
2828 }
2829 
2830 
2831 //	#pragma mark Offscreen bitmap configuration related functions.
2832 
2833 
2834 void
2835 ChartWindow::CheckBitmap(color_space depth, int32 width, int32 height)
2836 {
2837 	color_space		cur_depth;
2838 
2839 	if (LockWithTimeout(200000) != B_OK)
2840 		return;
2841 	/* If there was no offscreen before, or if it was too small
2842 	   or in the wrong depth, then... */
2843 	if (fOffscreen == NULL)
2844 		cur_depth = B_NO_COLOR_SPACE;
2845 	else
2846 		cur_depth = fBitmapBuffer.depth;
2847 	if ((cur_depth != depth) || (width > fMaxWidth) || (height > fMaxHeight)) {
2848 		/* We free the old one if needed... */
2849 		if (fOffscreen)
2850 			delete fOffscreen;
2851 		/* We chose a new size (resizing are done by big step to
2852 		   avoid resizing to often)... */
2853 		while ((width > fMaxWidth) || (height > fMaxHeight)) {
2854 			fMaxWidth += WINDOW_H_STEP;
2855 			fMaxHeight += WINDOW_V_STEP;
2856 		}
2857 		/* And we try to allocate a new BBitmap at the new size. */
2858 		fOffscreen = new BBitmap(BRect(0, 0, fMaxWidth-1, fMaxHeight-1), depth);
2859 		if (!fOffscreen->IsValid()) {
2860 			/* If we failed, the offscreen is released and the buffer
2861 			   clipping is set as empty. */
2862 			delete fOffscreen;
2863 			fOffscreen = NULL;
2864 			fBitmapBuffer.depth = B_NO_COLOR_SPACE;
2865 			fBitmapBuffer.clip_bounds.top = 0;
2866 			fBitmapBuffer.clip_bounds.left = 0;
2867 			fBitmapBuffer.clip_bounds.right = -1;
2868 			fBitmapBuffer.clip_bounds.bottom = -1;
2869 		}
2870 		else {
2871 			/* If we succeed, then initialise the generic buffer
2872 			   descriptor, we set the clipping to the required size,
2873 			   and we set the buffer background color. */
2874 			fBitmapBuffer.bits = fOffscreen->Bits();
2875 			fBitmapBuffer.bytes_per_row = fOffscreen->BytesPerRow();
2876 			fBitmapBuffer.buffer_width = fCurrentSettings.width;
2877 			fBitmapBuffer.buffer_height = fCurrentSettings.height;
2878 			SetColorSpace(&fBitmapBuffer, fOffscreen->ColorSpace());
2879 			SetPatternBits(&fBitmapBuffer);
2880 			SetBitmapClipping(fCurrentSettings.width, fCurrentSettings.height);
2881 			SetBitmapBackGround();
2882 		}
2883 	}
2884 	Unlock();
2885 }
2886 
2887 
2888 void
2889 ChartWindow::SetBitmapClipping(int32 width, int32 height)
2890 {
2891 	/* Set the bitmap buffer clipping to the required size of
2892 	   the buffer (even if the allocated buffer is larger) */
2893 	fBitmapBuffer.clip_list_count = 1;
2894 	fBitmapBuffer.clip_bounds.top = 0;
2895 	fBitmapBuffer.clip_bounds.left = 0;
2896 	fBitmapBuffer.clip_bounds.right = width-1;
2897 	fBitmapBuffer.clip_bounds.bottom = height-1;
2898 	fBitmapBuffer.clip_list[0].top = fBitmapBuffer.clip_bounds.top;
2899 	fBitmapBuffer.clip_list[0].left = fBitmapBuffer.clip_bounds.left;
2900 	fBitmapBuffer.clip_list[0].right = fBitmapBuffer.clip_bounds.right;
2901 	fBitmapBuffer.clip_list[0].bottom = fBitmapBuffer.clip_bounds.bottom;
2902 }
2903 
2904 
2905 void
2906 ChartWindow::SetBitmapBackGround()
2907 {
2908 	int32		i, count;
2909 	uint32		*bits;
2910 	uint32		color;
2911 
2912 	/* set the bitmap buffer to the right background color */
2913 	bits = (uint32*)fOffscreen->Bits();
2914 	count = fOffscreen->BitsLength()/4;
2915 	color = fBitmapBuffer.back_color;
2916 
2917 	for (i=0; i<count; i++)
2918 		bits[i] = color;
2919 }
2920 
2921 
2922 //	#pragma mark DirectWindow related functions.
2923 
2924 
2925 void
2926 ChartWindow::DirectConnected(direct_buffer_info *info)
2927 {
2928 	/* block the animation thread. */
2929 	while (acquire_sem(fDrawingLock) == B_INTERRUPTED)
2930 		;
2931 	/* update the direct screen infos. */
2932 	SwitchContext(info);
2933 	/* unblock the animation thread. */
2934 	release_sem(fDrawingLock);
2935 }
2936 
2937 /* This function update the internal graphic context of the ChartWindow
2938    object to reflect the infos send through the DirectConnected API.
2939    It also update the state of stars (and erase some of them) to
2940    insure a clean transition during resize. As this function is called
2941    in DirectConnected, it's a bad idea to do any heavy drawing (long)
2942    operation. But as we only update the stars (the background will be
2943    update a little later by the view system), it's not a big deal. */
2944 void
2945 ChartWindow::SwitchContext(direct_buffer_info *info)
2946 {
2947 	uint32 i, j;
2948 
2949 	/* you need to use that mask to read the buffer state. */
2950 	switch (info->buffer_state & B_DIRECT_MODE_MASK) {
2951 	/* start a direct screen connection. */
2952 	case B_DIRECT_START :
2953 		/* set the status as connected, and continue as a modify */
2954 		fDirectConnected = true;
2955 
2956 	/* change the state of a direct screen connection. */
2957 	case B_DIRECT_MODIFY :
2958 		/* update the description of the abstract buffer representing
2959 		   the direct window connection. DirectConnected returns the
2960 		   description of the full content area. As we want to use
2961 		   only the animation view part of the window, we will need
2962 		   to compensate for that when update the descriptor. */
2963 
2964 		/* This calculate the base address of the animation view, taking into
2965 		   account the base address of the screen buffer, the position of the
2966 		   window and the position of the view in the window */
2967 		fDirectBuffer.bits = (void*)((char*)info->bits +
2968 			(info->window_bounds.top + TOP_LEFT_LIMIT) * info->bytes_per_row +
2969 			(info->window_bounds.left + LEFT_WIDTH) * (info->bits_per_pixel>>3));
2970 		/* Bytes per row and pixel-format are the same than the window values */
2971 		fDirectBuffer.bytes_per_row = info->bytes_per_row;
2972 		SetColorSpace(&fDirectBuffer, info->pixel_format);
2973 		SetPatternBits(&fDirectBuffer);
2974 
2975 		/* the width and height of the animation view are linked to the width
2976 		   and height of the window itself, reduced by the size of the borders
2977 		   reserved for the UI. */
2978 		fDirectBuffer.buffer_width =
2979 			info->window_bounds.right-info->window_bounds.left+1 - LEFT_WIDTH;
2980 		fDirectBuffer.buffer_height =
2981 			info->window_bounds.bottom-info->window_bounds.top+1 - TOP_LEFT_LIMIT;
2982 
2983 		/* Now, we go through the clipping list and "clip" the clipping
2984 		   rectangle to the animation view boundary. */
2985 		j = 0;
2986 		for (i=0; i<info->clip_list_count; i++) {
2987 			fDirectBuffer.clip_list[j].top = info->clip_list[i].top - info->window_bounds.top;
2988 			if (fDirectBuffer.clip_list[j].top < TOP_LEFT_LIMIT)
2989 				fDirectBuffer.clip_list[j].top = TOP_LEFT_LIMIT;
2990 			fDirectBuffer.clip_list[j].left = info->clip_list[i].left - info->window_bounds.left;
2991 			if (fDirectBuffer.clip_list[j].left < LEFT_WIDTH)
2992 				fDirectBuffer.clip_list[j].left = LEFT_WIDTH;
2993 			fDirectBuffer.clip_list[j].right = info->clip_list[i].right - info->window_bounds.left;
2994 			fDirectBuffer.clip_list[j].bottom = info->clip_list[i].bottom - info->window_bounds.top;
2995 
2996 			/* All clipped rectangle that are not empty are recorded in
2997 			   the buffer clipping list. We keep only the 64 first (as
2998 			   a reasonnable approximation of most cases), but the rectangle
2999 			   list could easily be made dynamic if needed. Those clipping
3000 			   rectangle are offset to animation view coordinates */
3001 			if ((fDirectBuffer.clip_list[j].top <= fDirectBuffer.clip_list[j].bottom) &&
3002 				(fDirectBuffer.clip_list[j].left <= fDirectBuffer.clip_list[j].right)) {
3003 				fDirectBuffer.clip_list[j].top -= TOP_LEFT_LIMIT;
3004 				fDirectBuffer.clip_list[j].left -= LEFT_WIDTH;
3005 				fDirectBuffer.clip_list[j].right -= LEFT_WIDTH;
3006 				fDirectBuffer.clip_list[j].bottom -= TOP_LEFT_LIMIT;
3007 				j++;
3008 				if (j == 64)
3009 					break;
3010 			}
3011 		}
3012 		/* record the count of clipping rect in the new clipping list (less
3013 		   or equal to the window clipping list count, as some rectangle can
3014 		   be made invisible by the extra animation view clipping */
3015 		fDirectBuffer.clip_list_count = j;
3016 
3017 		/* the bounding box of the clipping list need to be calculated again
3018 		   from scratch. Clipping the bounding box of the window clipping
3019 		   region to the animation view can give us an incorrect (larger)
3020 		   bounding box. Remember that the bounding box of a region is
3021 		   required to be minimal */
3022 		fDirectBuffer.clip_bounds.top = 20000;
3023 		fDirectBuffer.clip_bounds.left = 20000;
3024 		fDirectBuffer.clip_bounds.right = -20000;
3025 		fDirectBuffer.clip_bounds.bottom = -20000;
3026 
3027 		for (i=0; i<fDirectBuffer.clip_list_count; i++) {
3028 			if (fDirectBuffer.clip_bounds.top > fDirectBuffer.clip_list[i].top)
3029 				fDirectBuffer.clip_bounds.top = fDirectBuffer.clip_list[i].top;
3030 			if (fDirectBuffer.clip_bounds.left > fDirectBuffer.clip_list[i].left)
3031 				fDirectBuffer.clip_bounds.left = fDirectBuffer.clip_list[i].left;
3032 			if (fDirectBuffer.clip_bounds.right < fDirectBuffer.clip_list[i].right)
3033 				fDirectBuffer.clip_bounds.right = fDirectBuffer.clip_list[i].right;
3034 			if (fDirectBuffer.clip_bounds.bottom < fDirectBuffer.clip_list[i].bottom)
3035 				fDirectBuffer.clip_bounds.bottom = fDirectBuffer.clip_list[i].bottom;
3036 		}
3037 
3038 		/* If the bounding box is empty, nothing is visible and all erasing
3039 		   should be canceled */
3040 		if ((fDirectBuffer.clip_bounds.top > fDirectBuffer.clip_bounds.bottom) ||
3041 			(fDirectBuffer.clip_bounds.left > fDirectBuffer.clip_bounds.right)) {
3042 			fStars.erase_count = 0;
3043 			goto nothing_visible;
3044 		}
3045 
3046 		if (fCurrentSettings.display == DISPLAY_DIRECT) {
3047 			/* When the direct display mode is used, the geometry changes
3048 			   need to be immediatly applied to the engine. */
3049 			SetGeometry(fDirectBuffer.buffer_width, fDirectBuffer.buffer_height);
3050 			/* if the buffer was reset then
3051 			   we cancel the erasing of the stars for the next frame. */
3052 			if (info->buffer_state & B_BUFFER_RESET) {
3053 				fStars.erase_count = 0;
3054 			}
3055 			/* In the other case, we need to cancel the erasing of star that
3056 			   were drawn at the previous frame, but are no longer visible */
3057 			else if (info->buffer_state & B_CLIPPING_MODIFIED) {
3058 				RefreshClipping(&fDirectBuffer, &fStars);
3059 				RefreshClipping(&fDirectBuffer, &fSpecials);
3060 			}
3061 		}
3062 		break;
3063 
3064 	/* stop a direct screen connection */
3065 	case B_DIRECT_STOP :
3066 		/* set the status as not connected */
3067 		fDirectConnected = false;
3068 	nothing_visible:
3069 		/* set an empty clipping */
3070 		fDirectBuffer.clip_list_count = 1;
3071 		fDirectBuffer.clip_bounds.top = 0;
3072 		fDirectBuffer.clip_bounds.left = 0;
3073 		fDirectBuffer.clip_bounds.right = -1;
3074 		fDirectBuffer.clip_bounds.bottom = -1;
3075 		fDirectBuffer.clip_list[0].top = 0;
3076 		fDirectBuffer.clip_list[0].left = 0;
3077 		fDirectBuffer.clip_list[0].right = -1;
3078 		fDirectBuffer.clip_list[0].bottom = -1;
3079 		break;
3080 	}
3081 }
3082 
3083 
3084 /*! copy a setting into another */
3085 void
3086 ChartWindow::setting::Set(setting *master)
3087 {
3088 	memcpy(this, master, sizeof(setting));
3089 }
3090 
3091 
3092 /*! Pseudo-random generator increment function.	*/
3093 void
3094 ChartWindow::CrcStep()
3095 {
3096 	fCrcAlea <<= 1;
3097 	if (fCrcAlea < 0)
3098 		fCrcAlea ^= CRC_KEY;
3099 }
3100