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