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). */
47enum {
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. */
56enum {
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
94int32 LEFT_WIDTH = 96;
95
96
97/* min, max and standard setting of the star count, also
98   called starfield density. */
99enum {
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. */
112enum {
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. */
120enum {
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 */
134enum {
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. */
151static 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. */
162static 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 */
178TPoint
179TPoint::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 */
190TPoint
191TPoint::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 */
202TPoint 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 */
212TPoint
213TPoint::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 */
224float
225TPoint::Length() const
226{
227	return sqrt(x*x + y*y + z*z);
228}
229
230/* product of a vector by a matrix */
231TPoint
232TMatrix::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. */
243TPoint
244TMatrix::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 */
256TMatrix
257TMatrix::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 */
274void
275TMatrix::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". */
306void
307LaunchSound()
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). */
332status_t
333get_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
361ChartWindow::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
930ChartWindow::~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
959bool
960ChartWindow::QuitRequested()
961{
962	be_app->PostMessage(B_QUIT_REQUESTED);
963	return BWindow::QuitRequested();
964}
965
966
967void
968ChartWindow::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
1099void
1100ChartWindow::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
1110void
1111ChartWindow::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. */
1127BWindow	*
1128ChartWindow::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. */
1152BPicture *
1153ChartWindow::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. */
1207void
1208ChartWindow::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 */
1237void
1238ChartWindow::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 */
1268void
1269ChartWindow::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. */
1297void
1298ChartWindow::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
1352void
1353ChartWindow::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
1398void
1399ChartWindow::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. */
1429void
1430ChartWindow::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 */
1638void
1639ChartWindow::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 */
1825void
1826ChartWindow::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 */
1845void
1846ChartWindow::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. */
1937void
1938ChartWindow::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
1958void
1959ChartWindow::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
1980void
1981ChartWindow::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*/
2137void
2138ChartWindow::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*/
2155int32
2156ChartWindow::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. */
2273int32
2274ChartWindow::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
2303void
2304ChartWindow::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. */
2398void
2399ChartWindow::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
2545void
2546ChartWindow::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. */
2604void
2605ChartWindow::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. */
2667void
2668ChartWindow::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 */
2755void
2756ChartWindow::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
2768void
2769ChartWindow::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
2853void
2854ChartWindow::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
2907void
2908ChartWindow::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
2924void
2925ChartWindow::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
2944void
2945ChartWindow::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. */
2963void
2964ChartWindow::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 */
3104void
3105ChartWindow::setting::Set(setting *master)
3106{
3107	memcpy(this, master, sizeof(setting));
3108}
3109
3110
3111/*! Pseudo-random generator increment function.	*/
3112void
3113ChartWindow::CrcStep()
3114{
3115	fCrcAlea <<= 1;
3116	if (fCrcAlea < 0)
3117		fCrcAlea ^= CRC_KEY;
3118}
3119