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