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