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