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