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