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