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