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