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