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