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