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