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