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