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