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