xref: /haiku/src/tests/kits/game/chart/ChartWindow.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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 			if (strcmp(window->Name(), name) == 0) {
1129 				window->Unlock();
1130 				break;
1131 			}
1132 			window->Unlock();
1133 		}
1134 	}
1135 	return window;
1136 }
1137 
1138 /* this function return a picture (in active or inactive state) of
1139    a standard BButton with some specific content draw in the middle.
1140    button_type indicate what special content should be used. */
1141 BPicture *
1142 ChartWindow::ButtonPicture(bool active, int32 button_type)
1143 {
1144 	char		word[6];
1145 	int32		value;
1146 	BRect		r;
1147 	BPoint		delta;
1148 	BPicture	*pict;
1149 
1150 
1151 	/* create and open the picture */
1152 	pict = new BPicture();
1153 	r = fOffwindowButton->Bounds();
1154 	fOffwindowButton->SetValue(active);
1155 	fOffwindowButton->BeginPicture(pict);
1156 	/* draw the standard BButton in whatever state is required. */
1157 	fOffwindowButton->Draw(r);
1158 	if (button_type == COLOR_BUTTON_PICT) {
1159 		/* this button just contains a rectangle of the current background
1160 		   color, with a one pixel black border. */
1161 		r.InsetBy(6.0, 4.0);
1162 		fOffwindowButton->SetHighColor(0, 0, 0);
1163 		fOffwindowButton->StrokeRect(r);
1164 		r.InsetBy(1.0, 1.0);
1165 		fOffwindowButton->SetHighColor(fCurrentSettings.back_color);
1166 		fOffwindowButton->FillRect(r);
1167 	}
1168 	else if (button_type == DENSITY_BUTTON_PICT) {
1169 		/* this button just contains a big string (using a bigger font size
1170 		   than what a standard BButton would allow) with the current value
1171 		   of the star density pourcentage. */
1172 		value = (fCurrentSettings.star_density*100 + STAR_DENSITY_MAX/2) / STAR_DENSITY_MAX;
1173 		sprintf(word, "%3ld", value);
1174 	draw_string:
1175 		fOffwindowButton->SetFont(be_bold_font);
1176 		fOffwindowButton->SetFontSize(14.0);
1177 		delta.x = BUTTON_WIDTH/2-(fOffwindowButton->StringWidth(word) * 0.5);
1178 		delta.y = (TOP_LEFT_LIMIT-2*V_BORDER)/2 + 6.0;
1179 		fOffwindowButton->DrawString(word, delta);
1180 	}
1181 	else {
1182 		/* this button just contains a big string (using a bigger font size
1183 		   than what a standard BButton would allow) with the current value
1184 		   of the target refresh rate in frames per second. */
1185 		sprintf(word, "%3.1f", fCurrentSettings.refresh_rate + 0.05);
1186 		goto draw_string;
1187 	}
1188 	/* close and return the picture */
1189 	return fOffwindowButton->EndPicture();
1190 }
1191 
1192 /* Create a floating window including a slightly modified version of
1193    BColorControl, ChartColorControl, that will return live feedback
1194    as the same time the user will change the color setting of the
1195    background. */
1196 void
1197 ChartWindow::OpenColorPalette(BPoint here)
1198 {
1199 	BRect frame;
1200 	BPoint point;
1201 
1202 	BWindow *window = GetAppWindow(B_TRANSLATE("Space color"));
1203 	if (window == NULL) {
1204 		frame.Set(here.x, here.y, here.x + 199.0, here.y + 99.0);
1205 		window = new BWindow(frame, B_TRANSLATE("Space color"),
1206 							 B_FLOATING_WINDOW_LOOK,
1207 							 B_FLOATING_APP_WINDOW_FEEL,
1208 							 B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK | B_NOT_RESIZABLE);
1209 		point.Set(0, 0);
1210 		BColorControl *colorControl = new ChartColorControl(point,
1211 			new BMessage(COLOR_PALETTE_MSG));
1212 		colorControl->SetViewColor(background_color);
1213 		colorControl->SetTarget(NULL, this);
1214 		colorControl->SetValue(fCurrentSettings.back_color);
1215 		colorControl->ResizeToPreferred();
1216 		window->ResizeTo(colorControl->Bounds().Width(), colorControl->Bounds().Height());
1217 		window->AddChild(colorControl);
1218 		window->Show();
1219 	}
1220 	window->Activate();
1221 }
1222 
1223 /* Create a floating window including a BSlider, that will return
1224    live feedback when the user will change the star density of the
1225    starfield */
1226 void
1227 ChartWindow::OpenStarDensity(BPoint here)
1228 {
1229 	BWindow	*window = GetAppWindow(B_TRANSLATE("Star density"));
1230 	if (window == NULL) {
1231 		BRect frame(here.x, here.y, here.x + STAR_DENSITY_H-1,
1232 			here.y + STAR_DENSITY_V-1);
1233 		window = new BWindow(frame, B_TRANSLATE("Star density"),
1234 							 B_FLOATING_WINDOW_LOOK,
1235 							 B_FLOATING_APP_WINDOW_FEEL,
1236 							 B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK);
1237 		frame.OffsetTo(0.0, 0.0);
1238 		BSlider	*slider = new BSlider(frame, "", NULL, new BMessage(STAR_DENSITY_MSG),
1239 							 STAR_DENSITY_MIN, STAR_DENSITY_MAX);
1240 		slider->SetViewColor(background_color);
1241 		slider->SetTarget(NULL, this);
1242 		slider->SetValue(fCurrentSettings.star_density);
1243 		slider->SetModificationMessage(new BMessage(STAR_DENSITY_MSG));
1244 		slider->SetLimitLabels(B_TRANSLATE(" 5% (low)"),
1245 			B_TRANSLATE("(high) 100% "));
1246 		slider->ResizeToPreferred();
1247 		window->ResizeTo(slider->Bounds().Width(), slider->Bounds().Height());
1248 		window->AddChild(slider);
1249 		window->Show();
1250 	}
1251 	window->Activate();
1252 }
1253 
1254 /* Create a floating window including a BSlider, that will return
1255    live feedback when the user will change the target refresh rate
1256    of the animation */
1257 void
1258 ChartWindow::OpenRefresh(BPoint here)
1259 {
1260 	BWindow *window = GetAppWindow(B_TRANSLATE("Refresh rate"));
1261 	if (window == NULL) {
1262 		BRect frame(here.x, here.y, here.x + REFRESH_RATE_H-1,
1263 			here.y + REFRESH_RATE_V-1);
1264 		window = new BWindow(frame, B_TRANSLATE("Refresh rate"),
1265 							 B_FLOATING_WINDOW_LOOK,
1266 							 B_FLOATING_APP_WINDOW_FEEL,
1267 							 B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK);
1268 		frame.OffsetTo(0.0, 0.0);
1269 		BSlider *slider = new BSlider(frame, "", NULL, new BMessage(REFRESH_RATE_MSG), 0, 1000);
1270 		slider->SetViewColor(background_color);
1271 		slider->SetTarget(NULL, this);
1272 		slider->SetValue((int32)(1000 * log(fCurrentSettings.refresh_rate / REFRESH_RATE_MIN) /
1273 						log(REFRESH_RATE_MAX/REFRESH_RATE_MIN)));
1274 		slider->SetModificationMessage(new BMessage(REFRESH_RATE_MSG));
1275 		slider->SetLimitLabels(B_TRANSLATE(" 0.6 f/s  (logarythmic scale)"),
1276 			B_TRANSLATE("600.0 f/s"));
1277 		slider->ResizeToPreferred();
1278 		window->ResizeTo(slider->Bounds().Width(), slider->Bounds().Height());
1279 		window->AddChild(slider);
1280 		window->Show();
1281 	}
1282 	window->Activate();
1283 }
1284 
1285 /* This update the state of the frames per second vue-meter in a lazy way. */
1286 void
1287 ChartWindow::DrawInstantLoad(float frame_per_second)
1288 {
1289 	int32 i;
1290 	bigtime_t	timeout;
1291 
1292 	int32 level = (int32)((frame_per_second + 6.0) * (1.0/12.0));
1293 	if (level > 50)
1294 		level = 50;
1295 
1296 	/* if the load level is inchanged, nothing more to do... */
1297 	if (level == fInstantLoadLevel)
1298 		return;
1299 
1300 	/* We need to lock the window to be able to draw that. But as some
1301 	   BControl are still synchronous, if the user is still tracking them,
1302 	   the window can stay block for a long time. It's not such a big deal
1303 	   when using the offscreen buffer as we won't be able to draw it in
1304 	   any case. But in DirectWindow mode, we're not limited by that so
1305 	   it would be stupid to block the engine loop here. That's why in
1306 	   that case, we will try to lock the window with a timeout of 0us. */
1307 	if (fCurrentSettings.display == DISPLAY_BITMAP)
1308 		timeout = 100000;
1309 	else
1310 		timeout = 0;
1311 	if (LockWithTimeout(timeout) != B_OK)
1312 		return;
1313 
1314 	/* the new level is higher than the previous. We need to draw more
1315 	   colored bars. */
1316 	if (level > fInstantLoadLevel) {
1317 		for (i = fInstantLoadLevel; i < level; i++) {
1318 			if (i < fInstantLoad->step)
1319 				fInstantLoad->SetHighColor(255, 90, 90);
1320 			else if ((i / fInstantLoad->step) & 1)
1321 				fInstantLoad->SetHighColor(90, 255, 90);
1322 			else
1323 				fInstantLoad->SetHighColor(40, 200, 40);
1324 			fInstantLoad->FillRect(BRect(3 + i * 4, 2, 5 + i * 4, 19));
1325 		}
1326 	}
1327 	/* the level is lower than before, we need to erase some bars. */
1328 	else {
1329 		fInstantLoad->SetHighColor(0, 0, 0);
1330 		for (i = level; i < fInstantLoadLevel; i++)
1331 			fInstantLoad->FillRect(BRect(3 + i * 4, 2, 5 +i * 4, 19));
1332 	}
1333 	/* we want that drawing to be completed as soon as possible */
1334 	Flush();
1335 
1336 	fInstantLoadLevel = level;
1337 	Unlock();
1338 }
1339 
1340 
1341 void
1342 ChartWindow::PrintStatNumbers(float fps)
1343 {
1344 	char		text_frames[6];
1345 	char		text_cpu_load[6];
1346 	float		frame_rate, load;
1347 	bigtime_t	timeout;
1348 
1349 	/* rules use to determine the stat numbers : if the target framerate
1350 	   is greater than the simulate one, then we consider that 100.0 cpu
1351 	   was used, and we only got the simulate framerate. */
1352 	if (fps <= fCurrentSettings.refresh_rate) {
1353 		load = 100.0;
1354 		frame_rate = fps + 0.05;
1355 	}
1356 	/* if the target framerate is less than the simulate one, then we
1357 	   calculate what fraction of the cpu would have been required to
1358 	   deliver the target framerate, and we said that the target framerate
1359 	   was delivered. */
1360 	else {
1361 		load = (100.0*fCurrentSettings.refresh_rate)/fps + 0.05;
1362 		frame_rate = fCurrentSettings.refresh_rate + 0.05;
1363 	}
1364 
1365 	/* convert numbers in strings */
1366 	sprintf(text_cpu_load, "%3.1f", load);
1367 	sprintf(text_frames, "%3.1f", frame_rate);
1368 
1369 	/* same remark than for DrawInstantLoad. We want to avoid to
1370 	   block if using DirectWindow mode. */
1371 	if (fCurrentSettings.display == DISPLAY_BITMAP)
1372 		timeout = 100000;
1373 	else
1374 		timeout = 0;
1375 
1376 	if (LockWithTimeout(timeout) == B_OK) {
1377 		fFramesView->SetText(text_frames);
1378 		fCpuLoadView->SetText(text_cpu_load);
1379 		Unlock();
1380 	}
1381 }
1382 
1383 
1384 //	#pragma mark Engine setting related functions.
1385 
1386 
1387 void
1388 ChartWindow::InitGeometry()
1389 {
1390 	/* calculate some parameters required for the 3d processing */
1391 	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));
1392 	fDepthRef = dz / (1.0 - 1.0/Z_CUT_RATIO);
1393 
1394 	/* set the position of the pyramid of vision, so that it was always
1395 	   possible to include it into a 1x1x1 cube parallel to the 3 main
1396 	   axis. */
1397 	fGeometry.z_max = fDepthRef;
1398 	fGeometry.z_min = fDepthRef/Z_CUT_RATIO;
1399 
1400 	/* used for lighting processing */
1401 	fGeometry.z_max_square = fGeometry.z_max * fGeometry.z_max;
1402 
1403 	/* preprocess that for the fast clipping based on the pyramid of vision */
1404 	fGeometry.xz_max = (0.5*DH_REF)/fGeometry.z_max;
1405 	fGeometry.xz_min = -fGeometry.xz_max;
1406 	fGeometry.yz_max = (0.5*DV_REF)/fGeometry.z_max;
1407 	fGeometry.yz_min = -fGeometry.yz_max;
1408 }
1409 
1410 /* second part of the asynchronous setting mechanism. This will be
1411    called once during every loop of the animation engine, at a time
1412    when the engine is not using the setting for realtime processing.
1413    Each setting will be checked for potential change, and action
1414    will be taken if needed. The window can be locked at that time
1415    because the structure of the animation engine loop guarantees
1416    that DirectConnected can not stay blocked at the same time that
1417    this method is executed. */
1418 void
1419 ChartWindow::ChangeSetting(setting new_set)
1420 {
1421 	int32			i, color_count, old_step;
1422 	int32			color_index[7];
1423 
1424 	/* check for change of window/fullscreen/fullscreen demo mode */
1425 	if (fCurrentSettings.fullscreen_mode != new_set.fullscreen_mode) {
1426 		switch (new_set.fullscreen_mode) {
1427 		case WINDOW_MODE :
1428 			fPreviousFullscreenMode = WINDOW_MODE;
1429 			ResizeTo(fPreviousFrame.Width(), fPreviousFrame.Height());
1430 			MoveTo(fPreviousFrame.left, fPreviousFrame.top);
1431 			break;
1432 		case FULLSCREEN_MODE :
1433 			{
1434 				fPreviousFullscreenMode = FULLSCREEN_MODE;
1435 				if (fCurrentSettings.fullscreen_mode == WINDOW_MODE)
1436 					fPreviousFrame = Frame();
1437 				BScreen	a_screen(this);
1438 				MoveTo(a_screen.Frame().left, a_screen.Frame().top);
1439 				ResizeTo(a_screen.Frame().Width(), a_screen.Frame().Height());
1440 			}
1441 			break;
1442 		case FULLDEMO_MODE :
1443 			{
1444 				fPreviousFullscreenMode = fCurrentSettings.fullscreen_mode;
1445 				if (fCurrentSettings.fullscreen_mode == WINDOW_MODE)
1446 					fPreviousFrame = Frame();
1447 				BScreen	b_screen(this);
1448 				ResizeTo(b_screen.Frame().Width() + LEFT_WIDTH, b_screen.Frame().Height() + TOP_LEFT_LIMIT);
1449 				MoveTo(b_screen.Frame().left - LEFT_WIDTH, b_screen.Frame().top - TOP_LEFT_LIMIT);
1450 			}
1451 			break;
1452 		}
1453 	}
1454 
1455 	/* check for change in the target refresh rate */
1456 	if (fCurrentSettings.refresh_rate != new_set.refresh_rate) {
1457 		fCurrentSettings.refresh_rate = new_set.refresh_rate;
1458 		old_step = fInstantLoad->step;
1459 		fInstantLoad->step = (int32)((fCurrentSettings.refresh_rate+6.0)/12.0);
1460 		if (fInstantLoad->step < 1)
1461 			fInstantLoad->step = 1;
1462 		if (LockWithTimeout(200000) == B_OK) {
1463 			if (old_step != fInstantLoad->step)
1464 				fInstantLoad->Invalidate();
1465 			fRefreshButton->SetEnabledOff(ButtonPicture(false, REFRESH_BUTTON_PICT));
1466 			fRefreshButton->SetEnabledOn(ButtonPicture(true, REFRESH_BUTTON_PICT));
1467 			fRefreshButton->Invalidate();
1468 			Unlock();
1469 		}
1470 		if (fCurrentSettings.animation != ANIMATION_OFF)
1471 			fFrameDelay = (bigtime_t)(1000000.0/new_set.refresh_rate);
1472 	}
1473 
1474 	/* check for change in the star colors list */
1475 	for (i=0; i<7; i++)
1476 		if (fCurrentSettings.colors[i] != new_set.colors[i]) {
1477 			/* if any, get the list of usable color index... */
1478 			color_count = 0;
1479 			for (i=0; i<7; i++)
1480 				if (new_set.colors[i])
1481 					color_index[color_count++] = i;
1482 			/* check that at least one color is enabled */
1483 			if (color_count == 0)
1484 				color_index[color_count++] = 6;
1485 			/* set a new color distribution in the starfield */
1486 			SetStarColors(color_index, color_count);
1487 			break;
1488 		}
1489 
1490 	/* check for change of the special effect setting */
1491 	if (new_set.special != fCurrentSettings.special)
1492 		InitSpecials(new_set.special);
1493 
1494 	/* check for change of the display method */
1495 	if (new_set.display != fCurrentSettings.display) {
1496 		if (new_set.display == DISPLAY_BITMAP) {
1497 			/* check the settings of the offscreen bitmap */
1498 			CheckBitmap(new_set.depth, new_set.width, new_set.height);
1499 			/* synchronise the camera geometry and the offscreen buffer geometry */
1500 			SetGeometry(fBitmapBuffer.buffer_width, fBitmapBuffer.buffer_height);
1501 			/* reset the offscreen background and cancel the erasing */
1502 			SetBitmapBackGround();
1503 			fStars.erase_count = 0;
1504 			fSpecials.erase_count = 0;
1505 		}
1506 		if (new_set.display == DISPLAY_DIRECT) {
1507 			/* this need to be atomic in regard of DirectConnected */
1508 			while (acquire_sem(fDrawingLock) == B_INTERRUPTED)
1509 				;
1510 			/* synchronise the camera geometry and the direct buffer geometry */
1511 			SetGeometry(fDirectBuffer.buffer_width, fDirectBuffer.buffer_height);
1512 			/* cancel erasing of stars not in visible part of the direct window */
1513 			RefreshClipping(&fDirectBuffer, &fStars);
1514 			RefreshClipping(&fDirectBuffer, &fSpecials);
1515 			release_sem(fDrawingLock);
1516 		}
1517 	}
1518 
1519 	/* check for change of the animation mode. */
1520 	if (new_set.animation != fCurrentSettings.animation) {
1521 		/* when there is no camera animation, we loop only
1522 		   10 times per second. */
1523 		if (new_set.animation == ANIMATION_OFF)
1524 			fFrameDelay = 100000;
1525 		else
1526 			fFrameDelay = (bigtime_t)(1000000.0/new_set.refresh_rate);
1527 		/* reset the free camera animation context for a fresh start */
1528 		if (new_set.animation == ANIMATION_FREE_MOVE) {
1529 			fDynamicAlpha = 0.0;
1530 			fDynamicTheta = 0.0;
1531 			fDynamicPhi = 0.0;
1532 			fCountAlpha = 0;
1533 			fCountTheta = 0;
1534 			fCountPhi = 0;
1535 		}
1536 	}
1537 
1538 	/* check for change of starfield model */
1539 	if (new_set.space_model != fCurrentSettings.space_model) {
1540 		/* Generate a new starfield. Also reset the special animation */
1541 		InitStars(new_set.space_model);
1542 		InitSpecials(new_set.special);
1543 	}
1544 
1545 	/* check for change of the background color */
1546 	if ((new_set.back_color.red != fCurrentSettings.back_color.red) ||
1547 		(new_set.back_color.green != fCurrentSettings.back_color.green) ||
1548 		(new_set.back_color.blue != fCurrentSettings.back_color.blue)) {
1549 		if (LockWithTimeout(200000) == B_OK) {
1550 			BScreen		screen(this);
1551 			/* set the background color and it's 8 bits index equivalent */
1552 			fCurrentSettings.back_color = new_set.back_color;
1553 			fBackColorIndex = screen.IndexForColor(new_set.back_color);
1554 			/* set the nackground color of the view (directwindow mode) */
1555 			fChartView->SetViewColor(new_set.back_color);
1556 			/* change the color of the picture button used in the UI */
1557 			fColorButton->SetEnabledOff(ButtonPicture(false, COLOR_BUTTON_PICT));
1558 			fColorButton->SetEnabledOn(ButtonPicture(true, COLOR_BUTTON_PICT));
1559 			fColorButton->Invalidate();
1560 			/* update all dependencies in the offscreen buffer descriptor */
1561 			SetColorSpace(&fBitmapBuffer, fBitmapBuffer.depth);
1562 			/* update all dependencies in the directwindow buffer descriptor */
1563 			while (acquire_sem(fDrawingLock) == B_INTERRUPTED)
1564 				;
1565 			SetColorSpace(&fDirectBuffer, fDirectBuffer.depth);
1566 			release_sem(fDrawingLock);
1567 			/* in offscreen mode, erase the background and cancel star erasing */
1568 			if (new_set.display == DISPLAY_BITMAP) {
1569 				SetBitmapBackGround();
1570 				fStars.erase_count = 0;
1571 				fSpecials.erase_count = 0;
1572 			}
1573 			/* in directwindow mode, just force an update */
1574 			else
1575 				fChartView->Invalidate();
1576 			Unlock();
1577 		}
1578 	}
1579 
1580 	/* check for change of the star animation density */
1581 	if (new_set.star_density != fCurrentSettings.star_density) {
1582 		if (LockWithTimeout(200000) == B_OK) {
1583 			fCurrentSettings.star_density = new_set.star_density;
1584 			/* change the picture button used in the UI */
1585 			fDensityButton->SetEnabledOff(ButtonPicture(false, DENSITY_BUTTON_PICT));
1586 			fDensityButton->SetEnabledOn(ButtonPicture(true, DENSITY_BUTTON_PICT));
1587 			fDensityButton->Invalidate();
1588 			Unlock();
1589 		}
1590 		fStars.count = new_set.star_density;
1591 	}
1592 
1593 	/* check for change in the buffer format for the offscreen bitmap.
1594 	   DirectWindow depth change are always handle in realtime */
1595 	if (new_set.depth != fCurrentSettings.depth) {
1596 		CheckBitmap(new_set.depth, new_set.width, new_set.height);
1597 		/* need to reset the buffer if it's currently used for display */
1598 		if (new_set.display == DISPLAY_BITMAP) {
1599 			SetBitmapBackGround();
1600 			fStars.erase_count = 0;
1601 			fSpecials.erase_count = 0;
1602 		}
1603 	}
1604 
1605 	/* check for change in the drawing area of the offscreen bitmap */
1606 	if ((new_set.width != fCurrentSettings.width) || (new_set.height != fCurrentSettings.height)) {
1607 		CheckBitmap(new_set.depth, new_set.width, new_set.height);
1608 		fBitmapBuffer.buffer_width = new_set.width;
1609 		fBitmapBuffer.buffer_height = new_set.height;
1610 		if (new_set.display == DISPLAY_BITMAP)
1611 			SetGeometry(fBitmapBuffer.buffer_width, fBitmapBuffer.buffer_height);
1612 		SetBitmapClipping(new_set.width, new_set.height);
1613 	}
1614 
1615 	/* copy the new state as the new current state */
1616 	fCurrentSettings.Set(&new_set);
1617 }
1618 
1619 /* Initialise the starfield in the different modes */
1620 void
1621 ChartWindow::InitStars(int32 space_model)
1622 {
1623 	star		*s;
1624 	int32		step;
1625 	int32		amas_select[32];
1626 	float		dx, dy, dz, dist, fact, alpha, r;
1627 	float		factor[8];
1628 	uint32		i, index, i_step;
1629 	TPoint		amas[8];
1630 
1631 	switch (space_model) {
1632 	/* Create a random starfield */
1633 	case SPACE_CHAOS :
1634 		FillStarList(fStars.list, STAR_DENSITY_MAX);
1635 		fKeyPointCount = 0;
1636 		break;
1637 
1638 	/* Create a starfield with big concentration of stars (amas) */
1639 	case SPACE_AMAS :
1640 	case SPACE_SPIRAL :
1641 		/* pick 8 random position for the amas */
1642 		FillStarList(fStars.list, 8);
1643 		for (i=0; i<8; i++) {
1644 			amas[i].x = fStars.list[i].x;
1645 			amas[i].y = fStars.list[i].y;
1646 			amas[i].z = fStars.list[i].z;
1647 			amas_select[i] = i;
1648 			factor[i] = ((float)(fCrcAlea&2047) + 0.5)*(1.0/128.0) + 16.0/3.0;
1649 			CrcStep();
1650 			CrcStep();
1651 		}
1652 		/* make each amas ramdomly smaller or bigger */
1653 		for (i=8; i<32; i++) {
1654 			amas_select[i] = (fCrcAlea & 7);
1655 			CrcStep();
1656 		}
1657 
1658 		/* create a random starfield */
1659 		FillStarList(fStars.list, STAR_DENSITY_MAX);
1660 
1661 		/* In spiral mode, only half the star will be put into the amas.
1662 		   the other half will be put into the spiral galaxy. */
1663 		if (space_model == SPACE_AMAS)
1664 			i_step = 1;
1665 		else
1666 			i_step = 2;
1667 		s = fStars.list;
1668 
1669 		for (i=0; i<STAR_DENSITY_MAX; i+=i_step) {
1670 			/* for every star, calculate its position relative to the
1671 			   center of the corresponding amas. */
1672 			index = amas_select[i&31];
1673 			dx = s->x-amas[index].x;
1674 			if (dx < -0.5) dx += 1.0;
1675 			if (dx > 0.5)  dx -= 1.0;
1676 			dy = s->y-amas[index].y;
1677 			if (dy < -0.5) dy += 1.0;
1678 			if (dy > 0.5)  dy -= 1.0;
1679 			dz = s->z-amas[index].z;
1680 			if (dz < -0.5) dz += 1.0;
1681 			if (dz > 0.5)  dz -= 1.0;
1682 
1683 			/* make the star randomly closer from its center, but keep
1684 			   it on the same orientation. */
1685 			step = 0;
1686 			dist = (abs(dx) + abs(dy) + abs(dz))*factor[index];
1687 			while (dist > 1.0) {
1688 				dist *= 0.5;
1689 				step++;
1690 			}
1691 
1692 			step -= (fCrcAlea&3);
1693 			CrcStep();
1694 			fact = 1.0;
1695 			for (;step>=0; step--)
1696 				fact *= 0.55;
1697 			dx *= fact;
1698 			dy *= fact;
1699 			dz *= fact;
1700 
1701 			/* put the star back in the [0-1]x[0-1]x[0-1] iteration of
1702 			   the cubic torus. */
1703 			s->x = amas[index].x + dx;
1704 			if (s->x >= 1.0) s->x -= 1.0;
1705 			if (s->x <= 0.0) s->x += 1.0;
1706 			s->y = amas[index].y + dy;
1707 			if (s->y >= 1.0) s->y -= 1.0;
1708 			if (s->y <= 0.0) s->y += 1.0;
1709 			s->z = amas[index].z + dz;
1710 			if (s->z >= 1.0) s->z -= 1.0;
1711 			if (s->z <= 0.0) s->z += 1.0;
1712 
1713 			s += i_step;
1714 		}
1715 
1716 		/* record the center of the amas as key points for the free
1717 		   camera animation mode. */
1718 		for (i=0; i<8; i++)
1719 			fKeyPoints[i] = amas[i];
1720 		fKeyPointCount = 8;
1721 
1722 		/* no further processing needed in amas only mode. */
1723 		if (space_model == SPACE_AMAS)
1724 			break;
1725 
1726 		/* in spiral mode, the second half of the star will be distributed
1727 		   on random spiral like galaxy. */
1728 		s = fStars.list+1;
1729 		for (i=1; i<STAR_DENSITY_MAX; i+=2) {
1730 			/* some random point (probability 50 %) will be move into a
1731 			   big amas at the center of the spiral galaxy. */
1732 			if (fCrcAlea & 2048) {
1733 				/* for every star, calculate its position relative to the
1734 				   center of the galaxy. */
1735 				dx = s->x - 0.5;
1736 				dy = s->y - 0.5;
1737 				dz = s->z - 0.5;
1738 
1739 				/* make the star randomly closer from its center, but keep
1740 				   it on the same orientation. */
1741 				step = 0;
1742 				dist = (dx*dx + dy*dy + dz*dz) * (32.0/0.75);
1743 				while (dist > 1.0) {
1744 					dist *= 0.5;
1745 					step++;
1746 				}
1747 
1748 				step -= (fCrcAlea&3);
1749 				CrcStep();
1750 				fact = 0.5;
1751 				for (;step>=0; step--)
1752 					fact *= 0.55;
1753 				dx *= fact;
1754 				dy *= fact;
1755 				dz *= fact;
1756 			}
1757 			else {
1758 				/* other star are put at a random place somewhere on one of
1759 				   teh two spiral arms... */
1760 				alpha = 3.4 * s->x * (s->x*0.5 + 1.0);
1761 				if (fCrcAlea & 64)
1762 					alpha += 3.14159;
1763 				r = s->x * 0.34 + 0.08;
1764 				r += (s->y-0.725 + 0.03 * (float)(fCrcAlea & 15))*0.04*(1.2+r);
1765 				r *= 0.5;
1766 				dx = (s->z-0.8 + 0.04 * (float)(fCrcAlea & 15)) * (2.0 - abs(s->y - 0.5)) * (0.025*0.5);
1767 				dy = cos(alpha) * r;
1768 				dz = sin(alpha) * r;
1769 			}
1770 			CrcStep();
1771 
1772 			/* put the star back in the [0-1]x[0-1]x[0-1] iteration of
1773 			   the cubic torus. */
1774 			s->x = 0.5 + dx;
1775 			s->y = 0.5 + dy;
1776 			s->z = 0.5 + dz;
1777 			s += 2;
1778 		}
1779 
1780 		/* add the center of the galaxy to the key point list for free camera
1781 		   animation mode */
1782 		fKeyPoints[8].x = 0.5;
1783 		fKeyPoints[8].y = 0.5;
1784 		fKeyPoints[8].z = 0.5;
1785 		/* add seven other galaxy star to the key point list */
1786 		for (i=9; i<16; i++) {
1787 			fKeyPoints[i].x = fStars.list[i*(STAR_DENSITY_MAX/18)].x;
1788 			fKeyPoints[i].y = fStars.list[i*(STAR_DENSITY_MAX/18)].y;
1789 			fKeyPoints[i].z = fStars.list[i*(STAR_DENSITY_MAX/18)].z;
1790 		}
1791 		fKeyPointCount = 16;
1792 		break;
1793 	}
1794 
1795 	/* In all starfield modes, for all stars, peek a random brightness level */
1796 	for (i=0; i<STAR_DENSITY_MAX; i++) {
1797 		fStars.list[i].size = (float)((fCrcAlea&15)+17)*(1.0/56.0);
1798 		if ((fCrcAlea & 0xc0) == 0)
1799 			fStars.list[i].size *= 2.0;
1800 		if ((fCrcAlea & 0x3f00) == 0)
1801 			fStars.list[i].size *= 3.0;
1802 		CrcStep();
1803 	}
1804 }
1805 
1806 /* Fill a list of star with random position in the [0-1]x[0-1]x[0-1] cube */
1807 void
1808 ChartWindow::FillStarList(star *list, int32 count)
1809 {
1810 	int32		i;
1811 
1812 	for (i=0; i<count; i++) {
1813 		list[i].x = ((float)(fCrcAlea&2047) + 0.5)*(1.0/2048.0);
1814 		CrcStep();
1815 	}
1816 	for (i=0; i<count; i++) {
1817 		list[i].y = ((float)(fCrcAlea&2047) + 0.5)*(1.0/2048.0);
1818 		CrcStep();
1819 	}
1820 	for (i=0; i<count; i++) {
1821 		list[i].z = ((float)(fCrcAlea&2047) + 0.5)*(1.0/2048.0);
1822 		CrcStep();
1823 	}
1824 }
1825 
1826 /* initialise anything needed to enable a specific special animation */
1827 void
1828 ChartWindow::InitSpecials(int32 code)
1829 {
1830 	int			i, j;
1831 	float		alpha, ksin, kcos, coeff;
1832 	TPoint		dx, dy;
1833 	TMatrix		matrix;
1834 
1835 	switch (code) {
1836 	/* turn special animation off */
1837 	case SPECIAL_NONE :
1838 		fSpecials.count = 0;
1839 		break;
1840 
1841 	/* Initialise the pixel-comet animation */
1842 	case SPECIAL_COMET :
1843 		/* Get a bunchof random values by getting some radom stars */
1844 		fSpecials.count = 512;
1845 		FillStarList(fSpecials.list, 4);
1846 		/* for both comets... */
1847 		for (j=0; j<2; j++) {
1848 			/* select the initial position of the comet head */
1849 			fComet[j].x = fSpecials.list[j].x;
1850 			fComet[j].y = fSpecials.list[j].y;
1851 			fComet[j].z = fSpecials.list[j].z;
1852 			fSpecials.list[0].size = 1.4;
1853 
1854 			/* select the speed vector of the comet */
1855 			matrix.Set(fSpecials.list[j+2].x * 6.28319, fSpecials.list[j+2].y * 3.14159 - 1.5708, 0.0);
1856 			fDeltaComet[j] = matrix.Axis(0) * 0.0015;
1857 			dx = matrix.Axis(1);
1858 			dy = matrix.Axis(2);
1859 
1860 			for (i=j+2; i<fSpecials.count; i+=2) {
1861 				/* make the pixel invisible at first */
1862 				fSpecials.list[i].x = -10.0;
1863 				fSpecials.list[i].y = 0.0;
1864 				fSpecials.list[i].z = 0.0;
1865 				/* spread the initial count on a linear scale (to make pixel
1866 				   appear progressively */
1867 				fSpecialList[i].comet.count = i/2;
1868 				/* spread the pixel trace count randomly on a [93-124] range */
1869 				fSpecialList[i].comet.count0 = (fCrcAlea & 31) + 93;
1870 				CrcStep();
1871 				/* pick a random ejection angle */
1872 				alpha = ((fCrcAlea>>8) & 1023) * (6.283159/1024.0);
1873 				CrcStep();
1874 
1875 				/* pick a random ejection speed */
1876 				coeff = 0.000114 + 0.0000016 * (float)((fCrcAlea>>17) & 31);
1877 				if ((fCrcAlea & 7) > 4) coeff *= 0.75;
1878 				if ((fCrcAlea & 7) == 7) coeff *= 0.65;
1879 				CrcStep();
1880 
1881 				/* calculate the ejection speed vector */
1882 				ksin = sin(alpha) * coeff;
1883 				kcos = cos(alpha) * coeff;
1884 				fSpecialList[i].comet.dx = dx.x * kcos + dy.x * ksin;
1885 				fSpecialList[i].comet.dy = dx.y * kcos + dy.y * ksin;
1886 				fSpecialList[i].comet.dz = dx.z * kcos + dy.z * ksin;
1887 			}
1888 		}
1889 		break;
1890 
1891 	/* Add a list of random star (used for nova effect by modifying their
1892 	   brightness level in real time) close from the first stars of the
1893 	   starfield. */
1894 	case SPECIAL_NOVAS :
1895 		fSpecials.count = 96;
1896 		for (i=0; i<fSpecials.count; i++) {
1897 			fSpecialList[i].nova.count = i + 40;
1898 			fSpecialList[i].nova.count0 = (fCrcAlea & 63) + 28;
1899 			CrcStep();
1900 			fSpecials.list[i].x = fStars.list[i].x + (fCrcAlea & 1)*0.02 - 0.01;
1901 			CrcStep();
1902 			fSpecials.list[i].y = fStars.list[i].y + (fCrcAlea & 1)*0.02 - 0.01;
1903 			CrcStep();
1904 			fSpecials.list[i].z = fStars.list[i].z + (fCrcAlea & 1)*0.02 - 0.01;
1905 			CrcStep();
1906 			fSpecials.list[i].size = 0.0;
1907 		}
1908 		break;
1909 
1910 	/* not implemented */
1911 	case SPECIAL_BATTLE :
1912 		fSpecials.count = 0;
1913 		break;
1914 	}
1915 }
1916 
1917 /* select a color for each star (and special animation point) by
1918    looping through the color index list. */
1919 void
1920 ChartWindow::SetStarColors(int32 *color_list, int32 color_count)
1921 {
1922 	int32		i, index;
1923 
1924 	index = 0;
1925 	for (i=0; i<STAR_DENSITY_MAX; i++) {
1926 		fStars.list[i].color_type = color_list[index];
1927 		index++;
1928 		if (index >= color_count)
1929 			index = 0;
1930 	}
1931 	for (i=0; i<SPECIAL_COUNT_MAX; i++) {
1932 		fSpecials.list[i].color_type = color_list[index];
1933 		index++;
1934 		if (index >= color_count)
1935 			index = 0;
1936 	}
1937 }
1938 
1939 
1940 void
1941 ChartWindow::SetGeometry(int32 dh, int32 dv)
1942 {
1943 	float zoom;
1944 
1945 	/* calculate the zoom factor for the 3d projection */
1946 	fGeometry.zoom_factor = (float)dh*(fDepthRef/DH_REF);
1947 	zoom = (float)dv*(fDepthRef/DV_REF);
1948 	if (zoom > fGeometry.zoom_factor)
1949 		fGeometry.zoom_factor = zoom;
1950 
1951 	/* offset of the origin in the view area */
1952 	fGeometry.offset_h = (float)dh * 0.5;
1953 	fGeometry.offset_v = (float)dv * 0.5;
1954 
1955 	/* sub-pixel precision double-sampling */
1956 	fGeometry.zoom_factor *= 2.0;
1957 	fGeometry.offset_h = fGeometry.offset_h * 2.0 - 1.0;
1958 	fGeometry.offset_v = fGeometry.offset_v * 2.0 - 1.0;
1959 }
1960 
1961 
1962 void
1963 ChartWindow::SetColorSpace(buffer *buf, color_space depth)
1964 {
1965 	bool swap_needed;
1966 	int32 red_shift = 0, green_shift = 0;
1967 	int32 blue_shift = 0, alpha_shift = 0;
1968 	int32 step_doubling = 0;
1969 
1970 	int32 red_divide_shift = 0, green_divide_shift = 0;
1971 	int32 blue_divide_shift = 0, alpha_divide_shift = 0;
1972 	int32 i;
1973 	uint32 color;
1974 	uint32 *col;
1975 	BScreen screen(this);
1976 	rgb_color ref_color;
1977 
1978 	/* depending the colorspace of the target buffer, set parameters used
1979 	   to encode the RGBA information for various color information in
1980 	   the right format. */
1981 	buf->depth = depth;
1982 	switch (depth) {
1983 		case B_RGBA32_BIG :
1984 		case B_RGB32_BIG :
1985 		case B_RGBA32 :
1986 		case B_RGB32 :
1987 			buf->depth_mode = PIXEL_4_BYTES;
1988 			buf->bytes_per_pixel = 4;
1989 			red_shift = 16;
1990 			green_shift = 8;
1991 			blue_shift = 0;
1992 			alpha_shift = 24;
1993 			red_divide_shift = 0;
1994 			green_divide_shift = 0;
1995 			blue_divide_shift = 0;
1996 			alpha_divide_shift = 0;
1997 			step_doubling = 32;
1998 			break;
1999 		case B_RGB16_BIG :
2000 		case B_RGB16 :
2001 			buf->depth_mode = PIXEL_2_BYTES;
2002 			buf->bytes_per_pixel = 2;
2003 			red_shift = 11;
2004 			red_divide_shift = 3;
2005 			green_shift = 5;
2006 			green_divide_shift = 2;
2007 			blue_shift = 0;
2008 			blue_divide_shift = 3;
2009 			alpha_shift = 32;
2010 			alpha_divide_shift = 8;
2011 			step_doubling = 16;
2012 			break;
2013 		case B_RGB15 :
2014 		case B_RGBA15 :
2015 		case B_RGB15_BIG :
2016 		case B_RGBA15_BIG :
2017 			buf->depth_mode = PIXEL_2_BYTES;
2018 			buf->bytes_per_pixel = 2;
2019 			red_shift = 10;
2020 			red_divide_shift = 3;
2021 			green_shift = 5;
2022 			green_divide_shift = 3;
2023 			blue_shift = 0;
2024 			blue_divide_shift = 3;
2025 			alpha_shift = 15;
2026 			alpha_divide_shift = 7;
2027 			step_doubling = 16;
2028 			break;
2029 		case B_CMAP8 :
2030 		default:
2031 			buf->depth_mode = PIXEL_1_BYTE;
2032 			buf->bytes_per_pixel = 1;
2033 			break;
2034 	}
2035 
2036 	/* Check if the endianess of the buffer is different from the
2037 	   endianess use by the processor to encode the color information */
2038 	switch (depth) {
2039 		case B_RGBA32_BIG :
2040 		case B_RGB32_BIG :
2041 		case B_RGB16_BIG :
2042 		case B_RGB15_BIG :
2043 		case B_RGBA15_BIG :
2044 			swap_needed = true;
2045 			break;
2046 		case B_RGBA32 :
2047 		case B_RGB32 :
2048 		case B_RGB16 :
2049 		case B_RGB15 :
2050 		case B_RGBA15 :
2051 		case B_CMAP8 :
2052 		default:
2053 			swap_needed = false;
2054 			break;
2055 	}
2056 
2057 #if B_HOST_IS_BENDIAN
2058 	swap_needed = ~swap_needed;
2059 #endif
2060 	/* fill the color tables (8 light level for 7 colors, and also encode
2061 	   the background color */
2062 	col = buf->colors[0];
2063 	switch (buf->depth_mode) {
2064 		case PIXEL_1_BYTE :
2065 			/* 8 bits, indexed mode */
2066 			for (i=0; i<7*8; i++) {
2067 				ref_color = color_list[i>>3];
2068 				ref_color.red   = (ref_color.red*light_gradient[i&7])>>16;
2069 				ref_color.green = (ref_color.green*light_gradient[i&7])>>16;
2070 				ref_color.blue  = (ref_color.blue*light_gradient[i&7])>>16;
2071 				color = screen.IndexForColor(ref_color);
2072 				col[i] = (color<<24) | (color<<16) | (color<<8) | color;
2073 			}
2074 			color = screen.IndexForColor(fCurrentSettings.back_color);
2075 			buf->back_color = (color<<24) | (color<<16) | (color<<8) | color;
2076 			break;
2077 		case PIXEL_2_BYTES :
2078 		case PIXEL_4_BYTES :
2079 			/* 15, 16 or 32 bytes, RGB modes. Those modes just directly encode
2080 			   part of the bits of the initial rgba_color, at the right bit
2081 			   position */
2082 			for (i=0; i<7*8; i++) {
2083 				ref_color = color_list[i>>3];
2084 				ref_color.red   = (ref_color.red*light_gradient[i&7])>>16;
2085 				ref_color.green = (ref_color.green*light_gradient[i&7])>>16;
2086 				ref_color.blue  = (ref_color.blue*light_gradient[i&7])>>16;
2087 				color = ((uint8)ref_color.red >> red_divide_shift) << red_shift;
2088 				color |= ((uint8)ref_color.green >> green_divide_shift) << green_shift;
2089 				color |= ((uint8)ref_color.blue >> blue_divide_shift) << blue_shift;
2090 				color |= ((uint8)ref_color.alpha >> alpha_divide_shift) << alpha_shift;
2091 				col[i] = (color<<step_doubling) | color;
2092 			}
2093 			color = ((uint8)fCurrentSettings.back_color.red >> red_divide_shift) << red_shift;
2094 			color |= ((uint8)fCurrentSettings.back_color.green >> green_divide_shift) << green_shift;
2095 			color |= ((uint8)fCurrentSettings.back_color.blue >> blue_divide_shift) << blue_shift;
2096 			color |= ((uint8)fCurrentSettings.back_color.alpha >> alpha_divide_shift) << alpha_shift;
2097 			buf->back_color = (color<<step_doubling) | color;
2098 			break;
2099 	}
2100 
2101 	/* do the endianess swap if needed */
2102 	if (swap_needed) {
2103 		col = buf->colors[0];
2104 		for (i = 0; i < 7*8; i++) {
2105 			B_SWAP_INT32(col[i]);
2106 		}
2107 		B_SWAP_INT32(buf->back_color);
2108 	}
2109 }
2110 
2111 
2112 /*!
2113 	For each different offset used to access a pixel of the star matrix,
2114 	create a buffer pointer based on the main buffer pointer offset by
2115 	the pixel matrix offset. That way, any pixel of the matrix can be
2116 	address later by just picking the right pointer and indexing it by
2117 	the global star offset
2118 */
2119 void
2120 ChartWindow::SetPatternBits(buffer *buf)
2121 {
2122 	for (int32 i=0; i<32; i++) {
2123 		buf->pattern_bits[i] = (void*)((char*)buf->bits +
2124 			buf->bytes_per_row * pattern_dv[i] +
2125 			buf->bytes_per_pixel * pattern_dh[i]);
2126 	}
2127 }
2128 
2129 
2130 //	#pragma mark Engine processing related functions.
2131 
2132 
2133 /*!
2134 	That's the main thread controling the animation and synchronising
2135 	the engine state with the changes coming from the UI.
2136 */
2137 long
2138 ChartWindow::Animation(void *data)
2139 {
2140 	int32			i, cur_4_frames_index, cur_last_fps, count_fps;
2141 	float			time_factor = 0, total_fps;
2142 	float			last_fps[4];
2143 	bigtime_t		next_stat;
2144 	bigtime_t		timer, time_left, current;
2145 	bigtime_t		before_frame, after_frame, fps;
2146 	bigtime_t		last_4_frames[4];
2147 	ChartWindow		*w;
2148 
2149 	w = (ChartWindow*)data;
2150 
2151 	/* init refresh rate control */
2152 	timer = system_time();
2153 	w->fFrameDelay = 100000;
2154 
2155 	/* init performance timing control variables */
2156 	next_stat = timer + STAT_DELAY;
2157 	cur_4_frames_index = 0;
2158 	cur_last_fps = 0;
2159 	for (i=0; i<4; i++)
2160 		last_fps[i] = 0.0;
2161 	total_fps = 0.0;
2162 	count_fps = 0;
2163 
2164 	/* here start the loop doing all the good stuff */
2165 	while (!w->fKillThread) {
2166 
2167 		/* start the performance mesurement here */
2168 		before_frame = system_time();
2169 
2170 		/* credit the timer by the current delay between frame */
2171 		timer += w->fFrameDelay;
2172 
2173 		/* change the settings, if needed */
2174 		w->ChangeSetting(w->fNextSettings);
2175 
2176 		/* draw the next frame */
2177 		if (w->fCurrentSettings.display == DISPLAY_BITMAP) {
2178 			w->RefreshStars(&w->fBitmapBuffer, time_factor * 2.4);
2179 			if (w->LockWithTimeout(200000) == B_OK) {
2180 				w->fChartView->DrawBitmap(w->fOffscreen);
2181 				w->Unlock();
2182 			}
2183 		}
2184 		else if (w->fCurrentSettings.display == DISPLAY_DIRECT) {
2185 			/* This part get the drawing-lock to guarantee that the
2186 			   directbuffer context won't change during the drawing
2187 			   operations. During that period, no Window should be
2188 			   done to avoid any potential deadlock. */
2189 			while (acquire_sem(w->fDrawingLock) == B_INTERRUPTED)
2190 				;
2191 
2192 			if (w->fDirectConnected)
2193 				w->RefreshStars(&w->fDirectBuffer, time_factor * 2.4);
2194 
2195 			release_sem(w->fDrawingLock);
2196 		}
2197 
2198 		/* do the camera animation */
2199 		w->CameraAnimation(time_factor);
2200 
2201 		/* end the performance mesurement here */
2202 		after_frame = system_time();
2203 
2204 		/* performance timing calculation here (if display enabled). */
2205 		if (w->fCurrentSettings.display != DISPLAY_OFF) {
2206 			/* record frame duration into a 2 levels 4 entries ring buffer */
2207 			last_4_frames[cur_4_frames_index] = after_frame - before_frame;
2208 			cur_4_frames_index++;
2209 			if (cur_4_frames_index == 4) {
2210 				cur_4_frames_index = 0;
2211 				last_fps[cur_last_fps++ & 3] =
2212 					last_4_frames[0]+last_4_frames[1]+last_4_frames[2]+last_4_frames[3];
2213 				/* the instant load is calculated based on the average duration
2214 				   of the last 16 frames. */
2215 				fps = (bigtime_t) (16e6 / (last_fps[0]+last_fps[1]+last_fps[2]+last_fps[3]));
2216 				w->DrawInstantLoad(fps);
2217 
2218 				total_fps += fps;
2219 				count_fps += 1;
2220 
2221 				/* The statistic numbers are based on the ratio between the real
2222 				   duration and the frame count during a period of approximately
2223 				   STAT_DELAY microseconds. */
2224 				if (after_frame > next_stat) {
2225 					w->PrintStatNumbers(total_fps/(float)count_fps);
2226 					next_stat = after_frame+STAT_DELAY;
2227 					total_fps = 0.0;
2228 					count_fps = 0;
2229 				}
2230 			}
2231 		}
2232 
2233 		/* do a pause if necessary */
2234 		current = system_time();
2235 		time_left = timer-current;
2236 		if (time_left > 2000) {
2237 			snooze(time_left);
2238 			time_left = 0;
2239 		}
2240 		else if (time_left < -5000)
2241 			timer = current;
2242 
2243 		/* this factor controls the dynamic timing configuration, that
2244 		   slow down or speed up the whole animation step to compensate
2245 		   for varaiation of the framerate. */
2246 		time_factor = (float)(system_time() - before_frame) * (1.0/4e4);
2247 	}
2248 	return 0;
2249 }
2250 
2251 /* This is the second thread doing star animation. It's just a poor
2252    slave of the Animation thread. It's directly synchronised with its
2253    master, and will only do some star animation processing whenever
2254    its master allows him to do so. */
2255 long
2256 ChartWindow::Animation2(void *data)
2257 {
2258 	ChartWindow *w = (ChartWindow*)data;
2259 	while (!w->fKillThread) {
2260 		/* This thread need to both wait for its master to unblock
2261 		   him to do some real work, or for the main control to
2262 		   set the fKillThread flag, asking it to quit. */
2263 		status_t status;
2264 		do {
2265 			status = acquire_sem_etc(w->fSecondThreadLock, 1, B_TIMEOUT, 500000);
2266 			if (w->fKillThread)
2267 				return 0;
2268 		} while (status == B_TIMED_OUT || status == B_INTERRUPTED);
2269 
2270 		/* the duration of the processing is needed to control the
2271 		   dynamic load split (see RefreshStar) */
2272 		bigtime_t before = system_time();
2273 		RefreshStarPacket(w->fSecondThreadBuffer, &w->fStars2, &w->fGeometry);
2274 		RefreshStarPacket(w->fSecondThreadBuffer, &w->fSpecials2, &w->fGeometry);
2275 		bigtime_t after = system_time();
2276 
2277 		w->fSecondThreadDelay = after-before;
2278 
2279 		release_sem(w->fSecondThreadRelease);
2280 	}
2281 	return 0;
2282 }
2283 
2284 
2285 void
2286 ChartWindow::SetCubeOffset()
2287 {
2288 	int32		i;
2289 	TPoint		min, max, dx, dy, dz, p1;
2290 
2291 	/* calculate the shortest aligned cube encapsulating the pyramid
2292 	   of vision, by calculating the min and max on the 3 main axis
2293 	   of the coordinates of the 8 extremities of the pyramid of
2294 	   vision (as limited by its 4 sides and the rear and front
2295 	   cut plan) */
2296 	min.x = min.y = min.z = 10.0;
2297 	max.x = max.y = max.z = -10.0;
2298 
2299 	dx = fCamera.Axis(0)*(DH_REF*0.5);
2300 	dy = fCamera.Axis(1)*(DV_REF*0.5);
2301 	dz = fCamera.Axis(2)*fDepthRef;
2302 
2303 	for (i=0; i<8; i++) {
2304 		/* left side / right side */
2305 		if (i&1) p1 = dz + dx;
2306 		else	 p1 = dz - dx;
2307 		/* top side / bottom side */
2308 		if (i&2) p1 = p1 + dy;
2309 		else	 p1 = p1 - dy;
2310 		/* rear cut plan / front cut plan */
2311 		if (i&4) p1 = p1 * (1.0 / Z_CUT_RATIO);
2312 		/* relative to the position of the camera */
2313 		p1 = p1 + fOrigin;
2314 
2315 		if (min.x > p1.x) min.x = p1.x;
2316 		if (min.y > p1.y) min.y = p1.y;
2317 		if (min.z > p1.z) min.z = p1.z;
2318 		if (max.x < p1.x) max.x = p1.x;
2319 		if (max.y < p1.y) max.y = p1.y;
2320 		if (max.z < p1.z) max.z = p1.z;
2321 	}
2322 
2323 	/* offset the camera origin by +1 or -1 on any axis (which
2324 	   doesn't change its relative position in the cubic torus
2325 	   as the cubic torus repeat itself identicaly for any move
2326 	   of +1 or -1 on any axis), to get the bounding cube into
2327 	   [0-2[ x [0-2[ x [0-2[. As the pyramid of vision is just
2328 	   small enough to gurantee that its bounding box will never
2329 	   be larger than 1 on any axis, it's always possible. */
2330 	while (min.x < 0.0) {
2331 		min.x += 1.0;
2332 		max.x += 1.0;
2333 		fOrigin.x += 1.0;
2334 	}
2335 	while (min.y < 0.0) {
2336 		min.y += 1.0;
2337 		max.y += 1.0;
2338 		fOrigin.y += 1.0;
2339 	}
2340 	while (min.z < 0.0) {
2341 		min.z += 1.0;
2342 		max.z += 1.0;
2343 		fOrigin.z += 1.0;
2344 	}
2345 	while (max.x >= 2.0) {
2346 		min.x -= 1.0;
2347 		max.x -= 1.0;
2348 		fOrigin.x -= 1.0;
2349 	}
2350 	while (max.y >= 2.0) {
2351 		min.y -= 1.0;
2352 		max.y -= 1.0;
2353 		fOrigin.y -= 1.0;
2354 	}
2355 	while (max.z >= 2.0) {
2356 		min.z -= 1.0;
2357 		max.z -= 1.0;
2358 		fOrigin.z -= 1.0;
2359 	}
2360 
2361 	/* set the cutting plans. For example, if the bouding box of
2362 	   the pyramid of vision of the camera imcludes only X in
2363 	   [0.43 ; 1.37], we know that points with X in [0 ; 0.4] are
2364 	   not visible from the camera. So we will offset them by +1
2365 	   in [1.0 ; 1.4] where they will be visible. Same process
2366 	   on other axis. That way, we have to test every star of the
2367 	   starfield in one position and only one. */
2368 	fCut.x = (min.x + max.x - 1.0) * 0.5;
2369 	fCut.y = (min.y + max.y - 1.0) * 0.5;
2370 	fCut.z = (min.z + max.z - 1.0) * 0.5;
2371 
2372 	/* Make sure those new settings are copied into the struct
2373 	   used by the embedded C-engine. */
2374 	SyncGeo();
2375 }
2376 
2377 /* move the camera around, as defined by the animation popup.
2378    This is adjusted by a time factor to compensate for change
2379    in the framerate. */
2380 void
2381 ChartWindow::CameraAnimation(float time_factor)
2382 {
2383 	TPoint			move;
2384 
2385 	switch (fCurrentSettings.animation) {
2386 	/* Slow rotation around the "center" of the visible area. */
2387 	case ANIMATION_ROTATE :
2388 		/* turn around a point at 0.45 in front of the camera */
2389 		move = fCamera.Axis(2);
2390 		move = move * 0.45;
2391 		fOrigin = fOrigin + move;
2392 
2393 		/* turn around the alpha angle of the spheric rotation
2394 		   matrix */
2395 		fCameraAlpha += 0.011*time_factor;
2396 		if (fCameraAlpha > 2*3.14159)
2397 			fCameraAlpha -= 2*3.14159;
2398 
2399 		/* set the other two angles close from hardcoded values */
2400 		if (fCameraTheta < 0.18)
2401 			fCameraTheta += 0.003*time_factor;
2402 		if (fCameraTheta > 0.22)
2403 			fCameraTheta -= 0.003*time_factor;
2404 
2405 		if (fCameraPhi < -0.02)
2406 			fCameraPhi += 0.003*time_factor;
2407 		if (fCameraPhi > 0.02)
2408 			fCameraPhi -= 0.003*time_factor;
2409 
2410 		fCamera.Set(fCameraAlpha, fCameraTheta, fCameraPhi);
2411 		fCameraInvert = fCamera.Transpose();
2412 		move = fCamera.Axis(2);
2413 		move = move * -0.45;
2414 		fOrigin = fOrigin + move;
2415 		/* As we moved or rotated the camera, we need to process
2416 		   again the parameters specific to the pyramid of vision. */
2417 		SetCubeOffset();
2418 		break;
2419 
2420 	case ANIMATION_SLOW_MOVE :
2421 		/* Just move forward, at slow speed */
2422 		move = fCamera.Axis(2);
2423 		move = move * 0.006*time_factor;
2424 		fOrigin = fOrigin + move;
2425 		SetCubeOffset();
2426 		break;
2427 
2428 	case ANIMATION_FAST_MOVE :
2429 		/* Just move forward, at fast speed */
2430 		move = fCamera.Axis(2);
2431 		move = move * 0.018*time_factor;
2432 		fOrigin = fOrigin + move;
2433 		SetCubeOffset();
2434 		break;
2435 
2436 	case ANIMATION_FREE_MOVE :
2437 		/* go into advanced selection process no more than once every
2438 		   0.5 time unit (average time). */
2439 		fLastDynamicDelay += time_factor;
2440 		if (fLastDynamicDelay > 0.5) {
2441 			fLastDynamicDelay -= 0.5;
2442 			if (fLastDynamicDelay > 0.2)
2443 				fLastDynamicDelay = 0.2;
2444 
2445 			/* if we're not following any target, then just turn
2446 			   randomly (modifying only the direction of the
2447 			   acceleration) */
2448 			if (fTrackingTarget < 0) {
2449 				if ((fCrcAlea & 0x4200) == 0) {
2450 					if (fCrcAlea & 0x8000)
2451 						fCountAlpha += 1 - (fCountAlpha/4);
2452 					else
2453 						fCountAlpha += -1 - (fCountAlpha/4);
2454 					CrcStep();
2455 					if (fCrcAlea & 0x8000)
2456 						fCountTheta += 1 - (fCountTheta/4);
2457 					else
2458 						fCountTheta += -1 - (fCountTheta/4);
2459 					CrcStep();
2460 					if (fCrcAlea & 0x8000)
2461 						fCountPhi += 1 - (fCountPhi/4);
2462 					else
2463 						fCountPhi += -1 - (fCountPhi/4);
2464 					CrcStep();
2465 				}
2466 				CrcStep();
2467 			}
2468 			/* if following a target, try to turn in its direction */
2469 			else
2470 				FollowTarget();
2471 
2472 			/* Change target everyonce in a while... */
2473 			if ((fCrcAlea & 0xf80) == 0)
2474 				SelectNewTarget();
2475 
2476 			/* depending the direction of acceleration, increase or
2477 			   reduce the angular speed of the 3 spherical angles. */
2478 			if (fCountAlpha < 0)
2479 				fDynamicAlpha += -0.0005 - fDynamicAlpha * 0.025;
2480 			else if (fCountAlpha > 0)
2481 				fDynamicAlpha += 0.0005 - fDynamicAlpha * 0.025;
2482 
2483 			if (fCountTheta < 0)
2484 				fDynamicTheta += -0.0002 - fDynamicTheta * 0.025;
2485 			else if (fCountTheta > 0)
2486 				fDynamicTheta += 0.0002 - fDynamicTheta * 0.025;
2487 
2488 			if (fCountPhi < 0)
2489 				fDynamicPhi += -0.00025 - fDynamicPhi * 0.025;
2490 			else if (fCountPhi >0)
2491 				fDynamicPhi += 0.00025 - fDynamicPhi * 0.025;
2492 		}
2493 
2494 		/* turn the camera following the specified angular speed */
2495 		fCameraAlpha += fDynamicAlpha*time_factor;
2496 		if (fCameraAlpha < 0.0)
2497 			fCameraAlpha += 2*3.14159;
2498 		else if (fCameraAlpha > 2*3.14159)
2499 			fCameraAlpha -= 2*3.14159;
2500 
2501 		fCameraTheta += fDynamicTheta*time_factor;
2502 		if (fCameraTheta < 0.0)
2503 			fCameraTheta += 2*3.14159;
2504 		else if (fCameraTheta > 2*3.14159)
2505 			fCameraTheta -= 2*3.14159;
2506 
2507 		fCameraPhi += fDynamicPhi*time_factor;
2508 		if (fCameraPhi < 0.0)
2509 			fCameraPhi += 2*3.14159;
2510 		else if (fCameraPhi > 2*3.14159)
2511 			fCameraPhi -= 2*3.14159;
2512 
2513 		/* Set the new rotation matrix of the camera */
2514 		fCamera.Set(fCameraAlpha, fCameraTheta, fCameraPhi);
2515 		fCameraInvert = fCamera.Transpose();
2516 
2517 		/* move the camera forward at medium speed */
2518 		move = fCamera.Axis(2);
2519 		move = move * 0.0115*time_factor;
2520 		fOrigin = fOrigin + move;
2521 		SetCubeOffset();
2522 		break;
2523 	}
2524 }
2525 
2526 
2527 void
2528 ChartWindow::SelectNewTarget()
2529 {
2530 	float		ratio, ratio_min;
2531 	float		dist, lateral, axial, ftmp;
2532 	int32		i, index_min;
2533 	TPoint		axis, pt, vect;
2534 
2535 	axis = fCamera.Axis(2);
2536 	ratio_min = 1e6;
2537 	index_min = -3;
2538 
2539 	for (i=-2; i<fKeyPointCount; i++) {
2540 		/* if they're used, the comets are two good potential
2541 		   targets. */
2542 		if (i < 0) {
2543 			if (fCurrentSettings.special == SPECIAL_COMET)
2544 				pt = fComet[i+2];
2545 			else
2546 				continue;
2547 		}
2548 		/* other potential targets are the key_points defined
2549 		   in the star field. */
2550 		else
2551 			pt = fKeyPoints[i];
2552 
2553 		/* Qualify the interest of the potential target in
2554 		   relationship with its distance and its proximity to
2555 		   the axis of the camera. */
2556 		if (pt.x < fCut.x)
2557 			pt.x += 1.0;
2558 		if (pt.y < fCut.y)
2559 			pt.y += 1.0;
2560 		if (pt.z < fCut.z)
2561 			pt.z += 1.0;
2562 		pt = pt - fOrigin;
2563 		dist = pt.Length();
2564 		ftmp = 1.0/dist;
2565 		pt.x *= ftmp;
2566 		pt.y *= ftmp;
2567 		pt.z *= ftmp;
2568 		vect = pt ^ axis;
2569 		lateral = axis.Length();
2570 		axial = pt.x*axis.x + pt.y*axis.y + pt.z*axis.z;
2571 		ratio = (lateral/axial) * sqrt(dist);
2572 
2573 		/* keep track of the best possible choice */
2574 		if ((dist > 0.05) && (ratio < ratio_min)) {
2575 			ratio_min = ratio;
2576 			index_min = i;
2577 		}
2578 	}
2579 
2580 	/* record what target has been chosen */
2581 	fTrackingTarget = index_min+2;
2582 }
2583 
2584 /* Try to change the angular acceleration to aim in direction
2585    of the current target. */
2586 void
2587 ChartWindow::FollowTarget()
2588 {
2589 	float		x0, y0, x, y, z, cphi, sphi;
2590 	TPoint		pt;
2591 
2592 	/* get the target point */
2593 	if (fTrackingTarget < 2)
2594 		pt = fComet[fTrackingTarget];
2595 	else
2596 		pt = fKeyPoints[fTrackingTarget-2];
2597 	/* move it in the right iteration of the cubic torus (the
2598 	   one iteration that is the most likely to be close from
2599 	   the pyramid of vision. */
2600 	if (pt.x < fCut.x)
2601 		pt.x += 1.0;
2602 	if (pt.y < fCut.y)
2603 		pt.y += 1.0;
2604 	if (pt.z < fCut.z)
2605 		pt.z += 1.0;
2606 	/* convert the target coordinates in the camera referential */
2607 	pt = pt - fOrigin;
2608 	x = fCameraInvert.m[0][0]*pt.x + fCameraInvert.m[1][0]*pt.y + fCameraInvert.m[2][0]*pt.z;
2609 	y = fCameraInvert.m[0][1]*pt.x + fCameraInvert.m[1][1]*pt.y + fCameraInvert.m[2][1]*pt.z;
2610 	z = fCameraInvert.m[0][2]*pt.x + fCameraInvert.m[1][2]*pt.y + fCameraInvert.m[2][2]*pt.z;
2611 	if (z <= 0.001) {
2612 		/* need to do a U-turn (better to do it using theta). */
2613 		fCountAlpha = 0;
2614 		fCountTheta = -1;
2615 		fCountPhi = 0;
2616 	}
2617 	else {
2618 		/* need to do a direction adjustement (play with
2619 		   alpha and theta) */
2620 		cphi = cos(fCameraPhi);
2621 		sphi = sin(fCameraPhi);
2622 		x0 = x*cphi - y*sphi;
2623 		y0 = x*sphi + y*cphi;
2624 
2625 		/* need to move first on the left/right axis */
2626 		if (abs(x0) > abs(y0)) {
2627 			if (x0 > 0)
2628 				fCountAlpha = -1;
2629 			else
2630 				fCountAlpha = 1;
2631 			fCountTheta = 0;
2632 			fCountPhi = 0;
2633 		}
2634 		/* need to move first on the top/bottom axis */
2635 		else {
2636 			if (y0 > 0)
2637 				fCountTheta = -1;
2638 			else
2639 				fCountTheta = 1;
2640 			fCountAlpha = 0;
2641 			fCountPhi = 0;
2642 		}
2643 	}
2644 }
2645 
2646 /* Do whatever special processing is required to do special
2647    animation. This used a time_step (or time_factor) to
2648    compensate for change in the framerate of the animation. */
2649 void
2650 ChartWindow::AnimSpecials(float time_step)
2651 {
2652 	int			i, j;
2653 	star		*s;
2654 	float		delta;
2655 	special		*sp;
2656 
2657 	switch (fCurrentSettings.special) {
2658 	case SPECIAL_COMET :
2659 		/* for both comets... */
2660 		for (j=0; j<2; j++) {
2661 			/* move the comet forward, at its specific speed */
2662 			fComet[j] = fComet[j] + fDeltaComet[j] * time_step;
2663 			/* Insure that the comet stays in the [0-1]x[0-1]x[0-1]
2664 			   iteration of the cubic torus. */
2665 			if (fComet[j].x < 0.0) fComet[j].x += 1.0;
2666 			else if (fComet[j].x > 1.0) fComet[j].x -= 1.0;
2667 			if (fComet[j].y < 0.0) fComet[j].y += 1.0;
2668 			else if (fComet[j].y > 1.0) fComet[j].y -= 1.0;
2669 			if (fComet[j].z < 0.0) fComet[j].z += 1.0;
2670 			else if (fComet[j].z > 1.0) fComet[j].z -= 1.0;
2671 			/* set the position of the star used to represent the
2672 			   head of the comet. */
2673 			fSpecials.list[j].x = fComet[j].x;
2674 			fSpecials.list[j].y = fComet[j].y;
2675 			fSpecials.list[j].z = fComet[j].z;
2676 
2677 			/* for other point, the ones that are ejected from the
2678 			   comet, depending for allow long they have been ejected... */
2679 			s = fSpecials.list+j+2;
2680 			sp = fSpecialList+j+2;
2681 			for (i=j+2; i<fSpecials.count; i+=2) {
2682 				sp->comet.count -= (int32)time_step;
2683 				/* they are reset and reejected again, just a little in
2684 				   the back of the head of the comet */
2685 				if (sp->comet.count <= 0.0) {
2686 					delta = (0.6 + (float)(fCrcAlea & 31) * (1.0/32.0)) * time_step;
2687 					s->x = fComet[j].x + 6.0 * sp->comet.dx - fDeltaComet[j].x * delta;
2688 					s->y = fComet[j].y + 6.0 * sp->comet.dy - fDeltaComet[j].y * delta;
2689 					s->z = fComet[j].z + 6.0 * sp->comet.dz - fDeltaComet[j].z * delta;
2690 					s->size = 0.6;
2691 					sp->comet.count = (int32)(sp->comet.count0 + (fCrcAlea & 63));
2692 					CrcStep();
2693 				}
2694 				/* or they just move at their own (ejection) speed */
2695 				else {
2696 					s->x += sp->comet.dx * time_step;
2697 					s->y += sp->comet.dy * time_step;
2698 					s->z += sp->comet.dz * time_step;
2699 					s->size *= (1.0 - 0.031 * time_step + 0.001 * time_step * time_step);
2700 				}
2701 				sp+=2;
2702 				s+=2;
2703 			}
2704 		}
2705 		break;
2706 
2707 	case SPECIAL_NOVAS :
2708 		/* Novas are just stars (usualy invisible) that periodically
2709 		   become much brighter during a suddent flash, then disappear
2710 		   again until their next cycle */
2711 		sp = fSpecialList;
2712 		for (i=0; i<fSpecials.count; i++) {
2713 			sp->nova.count -= time_step;
2714 			if (sp->nova.count <= 0.0) {
2715 				fSpecials.list[i].x -= 10.0;
2716 				sp->nova.count = sp->nova.count0 + (fCrcAlea & 31);
2717 				CrcStep();
2718 			}
2719 			else if (sp->nova.count < 16.0) {
2720 				if (fSpecials.list[i].x < 0.0)
2721 					fSpecials.list[i].x += 10.0;
2722 				fSpecials.list[i].size = sp->nova.count;
2723 			}
2724 			sp++;
2725 		}
2726 		break;
2727 
2728 	case SPECIAL_BATTLE :
2729 		/* not implemented */
2730 		break;
2731 	}
2732 }
2733 
2734 
2735 /* Sync the embedded camera state with the window class camera
2736    state (before calling the embedded C-engine in ChartRender.c */
2737 void
2738 ChartWindow::SyncGeo()
2739 {
2740 	fGeometry.x = fOrigin.x;
2741 	fGeometry.y = fOrigin.y;
2742 	fGeometry.z = fOrigin.z;
2743 	fGeometry.cutx = fCut.x;
2744 	fGeometry.cuty = fCut.y;
2745 	fGeometry.cutz = fCut.z;
2746 	memcpy(fGeometry.m, fCameraInvert.m, sizeof(float)*9);
2747 }
2748 
2749 
2750 void
2751 ChartWindow::RefreshStars(buffer *buf, float time_step)
2752 {
2753 	/* do the specials animation (single-threaded) */
2754 	AnimSpecials(time_step);
2755 
2756 	/* do the projection, clipping, erase and redraw
2757 	   of all stars. This operation is done by the
2758 	   embedded C-engine. This code only control the
2759 	   dynamic load split between the two threads, when
2760 	   needed. */
2761 	if (fCurrentSettings.second_thread) {
2762 		int32 star_threshold = (int32)((float)fStars.count * fSecondThreadThreshold + 0.5);
2763 		int32 special_threshold = (int32)((float)fSpecials.count * fSecondThreadThreshold + 0.5);
2764 
2765 		/* split the work load (star and special animation)
2766 		   between the two threads, proportionnaly to the
2767 		   last split factor determined during the last
2768 		   cycle. */
2769 		star_packet stars1;
2770 		stars1.list = fStars.list;
2771 		stars1.count = star_threshold;
2772 		stars1.erase_count = star_threshold;
2773 		if (stars1.erase_count > fStars.erase_count)
2774 			stars1.erase_count = fStars.erase_count;
2775 
2776 		fStars2.list = fStars.list + star_threshold;
2777 		fStars2.count = fStars.count - star_threshold;
2778 		fStars2.erase_count = fStars.erase_count - star_threshold;
2779 		if (fStars2.erase_count < 0)
2780 			fStars2.erase_count = 0;
2781 
2782 		star_packet specials1;
2783 		specials1.list = fSpecials.list;
2784 		specials1.count = special_threshold;
2785 		specials1.erase_count = special_threshold;
2786 		if (specials1.erase_count > fSpecials.erase_count)
2787 			specials1.erase_count = fSpecials.erase_count;
2788 
2789 		fSpecials2.list = fSpecials.list + special_threshold;
2790 		fSpecials2.count = fSpecials.count - special_threshold;
2791 		fSpecials2.erase_count = fSpecials.erase_count - special_threshold;
2792 		if (fSpecials2.erase_count < 0)
2793 			fSpecials2.erase_count = 0;
2794 
2795 		fSecondThreadBuffer = buf;
2796 
2797 		/* release the slave thread */
2798 		release_sem(fSecondThreadLock);
2799 
2800 		/* do its own part (time it) */
2801 		bigtime_t before = system_time();
2802 		RefreshStarPacket(buf, &stars1, &fGeometry);
2803 		RefreshStarPacket(buf, &specials1, &fGeometry);
2804 		bigtime_t after = system_time();
2805 
2806 		/* wait for completion of the second thread */
2807 		while (acquire_sem(fSecondThreadRelease) == B_INTERRUPTED)
2808 			;
2809 
2810 		/* calculate the new optimal split ratio depending
2811 		   of the previous one and the time used by both
2812 		   threads to do their work. */
2813 		float ratio = ((float)fSecondThreadDelay/(float)(after-before)) *
2814 				(fSecondThreadThreshold/(1.0-fSecondThreadThreshold));
2815 		fSecondThreadThreshold = ratio / (1.0+ratio);
2816 
2817 	} else {
2818 		 /* In single-threaded mode, nothing fancy to be done. */
2819 		RefreshStarPacket(buf, &fStars, &fGeometry);
2820 		RefreshStarPacket(buf, &fSpecials, &fGeometry);
2821 	}
2822 
2823 	/* All the stars that were drawn will have to be erased during
2824 	   the next frame. */
2825 	fStars.erase_count = fStars.count;
2826 	fSpecials.erase_count = fSpecials.count;
2827 }
2828 
2829 
2830 //	#pragma mark Offscreen bitmap configuration related functions.
2831 
2832 
2833 void
2834 ChartWindow::CheckBitmap(color_space depth, int32 width, int32 height)
2835 {
2836 	color_space		cur_depth;
2837 
2838 	if (LockWithTimeout(200000) != B_OK)
2839 		return;
2840 	/* If there was no offscreen before, or if it was too small
2841 	   or in the wrong depth, then... */
2842 	if (fOffscreen == NULL)
2843 		cur_depth = B_NO_COLOR_SPACE;
2844 	else
2845 		cur_depth = fBitmapBuffer.depth;
2846 	if ((cur_depth != depth) || (width > fMaxWidth) || (height > fMaxHeight)) {
2847 		/* We free the old one if needed... */
2848 		if (fOffscreen)
2849 			delete fOffscreen;
2850 		/* We chose a new size (resizing are done by big step to
2851 		   avoid resizing to often)... */
2852 		while ((width > fMaxWidth) || (height > fMaxHeight)) {
2853 			fMaxWidth += WINDOW_H_STEP;
2854 			fMaxHeight += WINDOW_V_STEP;
2855 		}
2856 		/* And we try to allocate a new BBitmap at the new size. */
2857 		fOffscreen = new BBitmap(BRect(0, 0, fMaxWidth-1, fMaxHeight-1), depth);
2858 		if (!fOffscreen->IsValid()) {
2859 			/* If we failed, the offscreen is released and the buffer
2860 			   clipping is set as empty. */
2861 			delete fOffscreen;
2862 			fOffscreen = NULL;
2863 			fBitmapBuffer.depth = B_NO_COLOR_SPACE;
2864 			fBitmapBuffer.clip_bounds.top = 0;
2865 			fBitmapBuffer.clip_bounds.left = 0;
2866 			fBitmapBuffer.clip_bounds.right = -1;
2867 			fBitmapBuffer.clip_bounds.bottom = -1;
2868 		}
2869 		else {
2870 			/* If we succeed, then initialise the generic buffer
2871 			   descriptor, we set the clipping to the required size,
2872 			   and we set the buffer background color. */
2873 			fBitmapBuffer.bits = fOffscreen->Bits();
2874 			fBitmapBuffer.bytes_per_row = fOffscreen->BytesPerRow();
2875 			fBitmapBuffer.buffer_width = fCurrentSettings.width;
2876 			fBitmapBuffer.buffer_height = fCurrentSettings.height;
2877 			SetColorSpace(&fBitmapBuffer, fOffscreen->ColorSpace());
2878 			SetPatternBits(&fBitmapBuffer);
2879 			SetBitmapClipping(fCurrentSettings.width, fCurrentSettings.height);
2880 			SetBitmapBackGround();
2881 		}
2882 	}
2883 	Unlock();
2884 }
2885 
2886 
2887 void
2888 ChartWindow::SetBitmapClipping(int32 width, int32 height)
2889 {
2890 	/* Set the bitmap buffer clipping to the required size of
2891 	   the buffer (even if the allocated buffer is larger) */
2892 	fBitmapBuffer.clip_list_count = 1;
2893 	fBitmapBuffer.clip_bounds.top = 0;
2894 	fBitmapBuffer.clip_bounds.left = 0;
2895 	fBitmapBuffer.clip_bounds.right = width-1;
2896 	fBitmapBuffer.clip_bounds.bottom = height-1;
2897 	fBitmapBuffer.clip_list[0].top = fBitmapBuffer.clip_bounds.top;
2898 	fBitmapBuffer.clip_list[0].left = fBitmapBuffer.clip_bounds.left;
2899 	fBitmapBuffer.clip_list[0].right = fBitmapBuffer.clip_bounds.right;
2900 	fBitmapBuffer.clip_list[0].bottom = fBitmapBuffer.clip_bounds.bottom;
2901 }
2902 
2903 
2904 void
2905 ChartWindow::SetBitmapBackGround()
2906 {
2907 	int32		i, count;
2908 	uint32		*bits;
2909 	uint32		color;
2910 
2911 	/* set the bitmap buffer to the right background color */
2912 	bits = (uint32*)fOffscreen->Bits();
2913 	count = fOffscreen->BitsLength()/4;
2914 	color = fBitmapBuffer.back_color;
2915 
2916 	for (i=0; i<count; i++)
2917 		bits[i] = color;
2918 }
2919 
2920 
2921 //	#pragma mark DirectWindow related functions.
2922 
2923 
2924 void
2925 ChartWindow::DirectConnected(direct_buffer_info *info)
2926 {
2927 	/* block the animation thread. */
2928 	while (acquire_sem(fDrawingLock) == B_INTERRUPTED)
2929 		;
2930 	/* update the direct screen infos. */
2931 	SwitchContext(info);
2932 	/* unblock the animation thread. */
2933 	release_sem(fDrawingLock);
2934 }
2935 
2936 /* This function update the internal graphic context of the ChartWindow
2937    object to reflect the infos send through the DirectConnected API.
2938    It also update the state of stars (and erase some of them) to
2939    insure a clean transition during resize. As this function is called
2940    in DirectConnected, it's a bad idea to do any heavy drawing (long)
2941    operation. But as we only update the stars (the background will be
2942    update a little later by the view system), it's not a big deal. */
2943 void
2944 ChartWindow::SwitchContext(direct_buffer_info *info)
2945 {
2946 	uint32 i, j;
2947 
2948 	/* you need to use that mask to read the buffer state. */
2949 	switch (info->buffer_state & B_DIRECT_MODE_MASK) {
2950 	/* start a direct screen connection. */
2951 	case B_DIRECT_START :
2952 		/* set the status as connected, and continue as a modify */
2953 		fDirectConnected = true;
2954 
2955 	/* change the state of a direct screen connection. */
2956 	case B_DIRECT_MODIFY :
2957 		/* update the description of the abstract buffer representing
2958 		   the direct window connection. DirectConnected returns the
2959 		   description of the full content area. As we want to use
2960 		   only the animation view part of the window, we will need
2961 		   to compensate for that when update the descriptor. */
2962 
2963 		/* This calculate the base address of the animation view, taking into
2964 		   account the base address of the screen buffer, the position of the
2965 		   window and the position of the view in the window */
2966 		fDirectBuffer.bits = (void*)((char*)info->bits +
2967 			(info->window_bounds.top + TOP_LEFT_LIMIT) * info->bytes_per_row +
2968 			(info->window_bounds.left + LEFT_WIDTH) * (info->bits_per_pixel>>3));
2969 		/* Bytes per row and pixel-format are the same than the window values */
2970 		fDirectBuffer.bytes_per_row = info->bytes_per_row;
2971 		SetColorSpace(&fDirectBuffer, info->pixel_format);
2972 		SetPatternBits(&fDirectBuffer);
2973 
2974 		/* the width and height of the animation view are linked to the width
2975 		   and height of the window itself, reduced by the size of the borders
2976 		   reserved for the UI. */
2977 		fDirectBuffer.buffer_width =
2978 			info->window_bounds.right-info->window_bounds.left+1 - LEFT_WIDTH;
2979 		fDirectBuffer.buffer_height =
2980 			info->window_bounds.bottom-info->window_bounds.top+1 - TOP_LEFT_LIMIT;
2981 
2982 		/* Now, we go through the clipping list and "clip" the clipping
2983 		   rectangle to the animation view boundary. */
2984 		j = 0;
2985 		for (i=0; i<info->clip_list_count; i++) {
2986 			fDirectBuffer.clip_list[j].top = info->clip_list[i].top - info->window_bounds.top;
2987 			if (fDirectBuffer.clip_list[j].top < TOP_LEFT_LIMIT)
2988 				fDirectBuffer.clip_list[j].top = TOP_LEFT_LIMIT;
2989 			fDirectBuffer.clip_list[j].left = info->clip_list[i].left - info->window_bounds.left;
2990 			if (fDirectBuffer.clip_list[j].left < LEFT_WIDTH)
2991 				fDirectBuffer.clip_list[j].left = LEFT_WIDTH;
2992 			fDirectBuffer.clip_list[j].right = info->clip_list[i].right - info->window_bounds.left;
2993 			fDirectBuffer.clip_list[j].bottom = info->clip_list[i].bottom - info->window_bounds.top;
2994 
2995 			/* All clipped rectangle that are not empty are recorded in
2996 			   the buffer clipping list. We keep only the 64 first (as
2997 			   a reasonnable approximation of most cases), but the rectangle
2998 			   list could easily be made dynamic if needed. Those clipping
2999 			   rectangle are offset to animation view coordinates */
3000 			if ((fDirectBuffer.clip_list[j].top <= fDirectBuffer.clip_list[j].bottom) &&
3001 				(fDirectBuffer.clip_list[j].left <= fDirectBuffer.clip_list[j].right)) {
3002 				fDirectBuffer.clip_list[j].top -= TOP_LEFT_LIMIT;
3003 				fDirectBuffer.clip_list[j].left -= LEFT_WIDTH;
3004 				fDirectBuffer.clip_list[j].right -= LEFT_WIDTH;
3005 				fDirectBuffer.clip_list[j].bottom -= TOP_LEFT_LIMIT;
3006 				j++;
3007 				if (j == 64)
3008 					break;
3009 			}
3010 		}
3011 		/* record the count of clipping rect in the new clipping list (less
3012 		   or equal to the window clipping list count, as some rectangle can
3013 		   be made invisible by the extra animation view clipping */
3014 		fDirectBuffer.clip_list_count = j;
3015 
3016 		/* the bounding box of the clipping list need to be calculated again
3017 		   from scratch. Clipping the bounding box of the window clipping
3018 		   region to the animation view can give us an incorrect (larger)
3019 		   bounding box. Remember that the bounding box of a region is
3020 		   required to be minimal */
3021 		fDirectBuffer.clip_bounds.top = 20000;
3022 		fDirectBuffer.clip_bounds.left = 20000;
3023 		fDirectBuffer.clip_bounds.right = -20000;
3024 		fDirectBuffer.clip_bounds.bottom = -20000;
3025 
3026 		for (i=0; i<fDirectBuffer.clip_list_count; i++) {
3027 			if (fDirectBuffer.clip_bounds.top > fDirectBuffer.clip_list[i].top)
3028 				fDirectBuffer.clip_bounds.top = fDirectBuffer.clip_list[i].top;
3029 			if (fDirectBuffer.clip_bounds.left > fDirectBuffer.clip_list[i].left)
3030 				fDirectBuffer.clip_bounds.left = fDirectBuffer.clip_list[i].left;
3031 			if (fDirectBuffer.clip_bounds.right < fDirectBuffer.clip_list[i].right)
3032 				fDirectBuffer.clip_bounds.right = fDirectBuffer.clip_list[i].right;
3033 			if (fDirectBuffer.clip_bounds.bottom < fDirectBuffer.clip_list[i].bottom)
3034 				fDirectBuffer.clip_bounds.bottom = fDirectBuffer.clip_list[i].bottom;
3035 		}
3036 
3037 		/* If the bounding box is empty, nothing is visible and all erasing
3038 		   should be canceled */
3039 		if ((fDirectBuffer.clip_bounds.top > fDirectBuffer.clip_bounds.bottom) ||
3040 			(fDirectBuffer.clip_bounds.left > fDirectBuffer.clip_bounds.right)) {
3041 			fStars.erase_count = 0;
3042 			goto nothing_visible;
3043 		}
3044 
3045 		if (fCurrentSettings.display == DISPLAY_DIRECT) {
3046 			/* When the direct display mode is used, the geometry changes
3047 			   need to be immediatly applied to the engine. */
3048 			SetGeometry(fDirectBuffer.buffer_width, fDirectBuffer.buffer_height);
3049 			/* if the buffer was reset then
3050 			   we cancel the erasing of the stars for the next frame. */
3051 			if (info->buffer_state & B_BUFFER_RESET) {
3052 				fStars.erase_count = 0;
3053 			}
3054 			/* In the other case, we need to cancel the erasing of star that
3055 			   were drawn at the previous frame, but are no longer visible */
3056 			else if (info->buffer_state & B_CLIPPING_MODIFIED) {
3057 				RefreshClipping(&fDirectBuffer, &fStars);
3058 				RefreshClipping(&fDirectBuffer, &fSpecials);
3059 			}
3060 		}
3061 		break;
3062 
3063 	/* stop a direct screen connection */
3064 	case B_DIRECT_STOP :
3065 		/* set the status as not connected */
3066 		fDirectConnected = false;
3067 	nothing_visible:
3068 		/* set an empty clipping */
3069 		fDirectBuffer.clip_list_count = 1;
3070 		fDirectBuffer.clip_bounds.top = 0;
3071 		fDirectBuffer.clip_bounds.left = 0;
3072 		fDirectBuffer.clip_bounds.right = -1;
3073 		fDirectBuffer.clip_bounds.bottom = -1;
3074 		fDirectBuffer.clip_list[0].top = 0;
3075 		fDirectBuffer.clip_list[0].left = 0;
3076 		fDirectBuffer.clip_list[0].right = -1;
3077 		fDirectBuffer.clip_list[0].bottom = -1;
3078 		break;
3079 	}
3080 }
3081 
3082 
3083 /*! copy a setting into another */
3084 void
3085 ChartWindow::setting::Set(setting *master)
3086 {
3087 	memcpy(this, master, sizeof(setting));
3088 }
3089 
3090 
3091 /*! Pseudo-random generator increment function.	*/
3092 void
3093 ChartWindow::CrcStep()
3094 {
3095 	fCrcAlea <<= 1;
3096 	if (fCrcAlea < 0)
3097 		fCrcAlea ^= CRC_KEY;
3098 }
3099