00001
00032 #include "llviewerprecompiledheaders.h"
00033
00034 #include "lljoystickbutton.h"
00035
00036
00037 #include "llcoord.h"
00038 #include "indra_constants.h"
00039 #include "llglimmediate.h"
00040
00041
00042 #include "llui.h"
00043 #include "llagent.h"
00044 #include "llviewerimage.h"
00045 #include "llviewerimagelist.h"
00046 #include "llviewerwindow.h"
00047 #include "llmoveview.h"
00048
00049 #include "llglheaders.h"
00050
00051 static LLRegisterWidget<LLJoystickAgentSlide> r1("joystick_slide");
00052 static LLRegisterWidget<LLJoystickAgentTurn> r2("joystick_turn");
00053
00054
00055 const F32 NUDGE_TIME = 0.25f;
00056 const F32 ORBIT_NUDGE_RATE = 0.05f;
00057
00058
00059
00060
00061 LLJoystick::LLJoystick(
00062 const LLString& name,
00063 LLRect rect,
00064 const LLString &default_image,
00065 const LLString &selected_image,
00066 EJoystickQuadrant initial_quadrant )
00067 :
00068 LLButton(name, rect, default_image, selected_image, NULL, NULL),
00069 mInitialQuadrant(initial_quadrant),
00070 mInitialOffset(0, 0),
00071 mLastMouse(0, 0),
00072 mFirstMouse(0, 0),
00073 mVertSlopNear(0),
00074 mVertSlopFar(0),
00075 mHorizSlopNear(0),
00076 mHorizSlopFar(0),
00077 mHeldDown(FALSE),
00078 mHeldDownTimer()
00079 {
00080 setHeldDownCallback(&LLJoystick::onHeldDown);
00081 setCallbackUserData(this);
00082 }
00083
00084
00085 void LLJoystick::updateSlop()
00086 {
00087 mVertSlopNear = getRect().getHeight();
00088 mVertSlopFar = getRect().getHeight() * 2;
00089
00090 mHorizSlopNear = getRect().getWidth();
00091 mHorizSlopFar = getRect().getWidth() * 2;
00092
00093
00094
00095 switch (mInitialQuadrant)
00096 {
00097 case JQ_ORIGIN:
00098 mInitialOffset.set(0, 0);
00099 break;
00100
00101 case JQ_UP:
00102 mInitialOffset.mX = 0;
00103 mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
00104 break;
00105
00106 case JQ_DOWN:
00107 mInitialOffset.mX = 0;
00108 mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
00109 break;
00110
00111 case JQ_LEFT:
00112 mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
00113 mInitialOffset.mY = 0;
00114 break;
00115
00116 case JQ_RIGHT:
00117 mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
00118 mInitialOffset.mY = 0;
00119 break;
00120
00121 default:
00122 llerrs << "LLJoystick::LLJoystick() - bad switch case" << llendl;
00123 break;
00124 }
00125
00126 return;
00127 }
00128
00129
00130 BOOL LLJoystick::handleMouseDown(S32 x, S32 y, MASK mask)
00131 {
00132
00133
00134 mLastMouse.set(x, y);
00135 mFirstMouse.set(x, y);
00136
00137 mMouseDownTimer.reset();
00138 return LLButton::handleMouseDown(x, y, mask);
00139 }
00140
00141
00142 BOOL LLJoystick::handleMouseUp(S32 x, S32 y, MASK mask)
00143 {
00144
00145
00146 if( hasMouseCapture() )
00147 {
00148 mLastMouse.set(x, y);
00149 mHeldDown = FALSE;
00150 onMouseUp();
00151 }
00152
00153 return LLButton::handleMouseUp(x, y, mask);
00154 }
00155
00156
00157 BOOL LLJoystick::handleHover(S32 x, S32 y, MASK mask)
00158 {
00159 if( hasMouseCapture() )
00160 {
00161 mLastMouse.set(x, y);
00162 }
00163
00164 return LLButton::handleHover(x, y, mask);
00165 }
00166
00167 F32 LLJoystick::getElapsedHeldDownTime()
00168 {
00169 if( mHeldDown )
00170 {
00171 return getHeldDownTime();
00172 }
00173 else
00174 {
00175 return 0.f;
00176 }
00177 }
00178
00179
00180 void LLJoystick::onHeldDown(void *userdata)
00181 {
00182 LLJoystick *self = (LLJoystick *)userdata;
00183
00184
00185
00186
00187
00188 self->mHeldDown = TRUE;
00189 self->onHeldDown();
00190 }
00191
00192 EJoystickQuadrant LLJoystick::selectQuadrant(LLXMLNodePtr node)
00193 {
00194
00195 EJoystickQuadrant quadrant = JQ_RIGHT;
00196
00197 if (node->hasAttribute("quadrant"))
00198 {
00199 LLString quadrant_name;
00200 node->getAttributeString("quadrant", quadrant_name);
00201
00202 quadrant = quadrantFromName(quadrant_name.c_str());
00203 }
00204 return quadrant;
00205 }
00206
00207
00208 LLString LLJoystick::nameFromQuadrant(EJoystickQuadrant quadrant)
00209 {
00210 if (quadrant == JQ_ORIGIN) return LLString("origin");
00211 else if (quadrant == JQ_UP) return LLString("up");
00212 else if (quadrant == JQ_DOWN) return LLString("down");
00213 else if (quadrant == JQ_LEFT) return LLString("left");
00214 else if (quadrant == JQ_RIGHT) return LLString("right");
00215 else return LLString();
00216 }
00217
00218
00219 EJoystickQuadrant LLJoystick::quadrantFromName(const LLString& sQuadrant)
00220 {
00221 EJoystickQuadrant quadrant = JQ_RIGHT;
00222
00223 if (sQuadrant == "origin")
00224 {
00225 quadrant = JQ_ORIGIN;
00226 }
00227 else if (sQuadrant == "up")
00228 {
00229 quadrant = JQ_UP;
00230 }
00231 else if (sQuadrant == "down")
00232 {
00233 quadrant = JQ_DOWN;
00234 }
00235 else if (sQuadrant == "left")
00236 {
00237 quadrant = JQ_LEFT;
00238 }
00239 else if (sQuadrant == "right")
00240 {
00241 quadrant = JQ_RIGHT;
00242 }
00243
00244 return quadrant;
00245 }
00246
00247
00248 LLXMLNodePtr LLJoystick::getXML(bool save_children) const
00249 {
00250 LLXMLNodePtr node = LLUICtrl::getXML();
00251
00252 node->createChild("halign", TRUE)->setStringValue(LLFontGL::nameFromHAlign(getHAlign()));
00253 node->createChild("quadrant", TRUE)->setStringValue(nameFromQuadrant(mInitialQuadrant));
00254
00255 addImageAttributeToXML(node,getImageUnselectedName(),getImageUnselectedID(),"image_unselected");
00256 addImageAttributeToXML(node,getImageSelectedName(),getImageSelectedID(),"image_selected");
00257
00258 node->createChild("scale_image", TRUE)->setBoolValue(getScaleImage());
00259
00260 return node;
00261 }
00262
00263
00264
00265
00266
00267
00268
00269 void LLJoystickAgentTurn::onHeldDown()
00270 {
00271 F32 time = getElapsedHeldDownTime();
00272 updateSlop();
00273
00274
00275
00276 S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
00277 S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
00278
00279 float m = (float) (dx)/abs(dy);
00280
00281 if (m > 1) {
00282 m = 1;
00283 }
00284 else if (m < -1) {
00285 m = -1;
00286 }
00287 gAgent.moveYaw(-LLFloaterMove::getYawRate(time)*m);
00288
00289
00290
00291 if (dy > mVertSlopFar)
00292 {
00293
00294 gAgent.moveAt(1);
00295 }
00296 else if (dy > mVertSlopNear)
00297 {
00298 if( time < NUDGE_TIME )
00299 {
00300 gAgent.moveAtNudge(1);
00301 }
00302 else
00303 {
00304
00305
00306 gAgent.moveAt(1);
00307 }
00308 }
00309 else if (dy < -mVertSlopFar)
00310 {
00311
00312 gAgent.moveAt(-1);
00313 }
00314 else if (dy < -mVertSlopNear)
00315 {
00316 if( time < NUDGE_TIME )
00317 {
00318 gAgent.moveAtNudge(-1);
00319 }
00320 else
00321 {
00322
00323
00324 gAgent.moveAt(-1);
00325 }
00326 }
00327 }
00328
00329 LLView* LLJoystickAgentTurn::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
00330 {
00331 LLString name("button");
00332 node->getAttributeString("name", name);
00333
00334 LLString image_unselected;
00335 if (node->hasAttribute("image_unselected")) node->getAttributeString("image_unselected",image_unselected);
00336
00337 LLString image_selected;
00338 if (node->hasAttribute("image_selected")) node->getAttributeString("image_selected",image_selected);
00339
00340 EJoystickQuadrant quad = JQ_ORIGIN;
00341 if (node->hasAttribute("quadrant")) quad = selectQuadrant(node);
00342
00343 LLJoystickAgentTurn *button = new LLJoystickAgentTurn(name,
00344 LLRect(),
00345 image_unselected,
00346 image_selected,
00347 quad);
00348
00349 if (node->hasAttribute("halign"))
00350 {
00351 LLFontGL::HAlign halign = selectFontHAlign(node);
00352 button->setHAlign(halign);
00353 }
00354
00355 if (node->hasAttribute("scale_image"))
00356 {
00357 BOOL needsScale = FALSE;
00358 node->getAttributeBOOL("scale_image",needsScale);
00359 button->setScaleImage( needsScale );
00360 }
00361
00362 button->initFromXML(node, parent);
00363
00364 return button;
00365 }
00366
00367
00368
00369
00370
00371
00372
00373 void LLJoystickAgentSlide::onMouseUp()
00374 {
00375 F32 time = getElapsedHeldDownTime();
00376 if( time < NUDGE_TIME )
00377 {
00378 switch (mInitialQuadrant)
00379 {
00380 case JQ_LEFT:
00381 gAgent.moveLeftNudge(1);
00382 break;
00383
00384 case JQ_RIGHT:
00385 gAgent.moveLeftNudge(-1);
00386 break;
00387
00388 default:
00389 break;
00390 }
00391 }
00392 }
00393
00394 void LLJoystickAgentSlide::onHeldDown()
00395 {
00396
00397
00398 updateSlop();
00399
00400 S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
00401 S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
00402
00403
00404 if (dx > mHorizSlopNear)
00405 {
00406 gAgent.moveLeft(-1);
00407 }
00408 else if (dx < -mHorizSlopNear)
00409 {
00410 gAgent.moveLeft(1);
00411 }
00412
00413
00414 if (dy > mVertSlopFar)
00415 {
00416
00417 gAgent.moveAt(1);
00418 }
00419 else if (dy > mVertSlopNear)
00420 {
00421
00422 gAgent.moveAtNudge(1);
00423 }
00424 else if (dy < -mVertSlopFar)
00425 {
00426
00427 gAgent.moveAt(-1);
00428 }
00429 else if (dy < -mVertSlopNear)
00430 {
00431
00432 gAgent.moveAtNudge(-1);
00433 }
00434 }
00435
00436
00437
00438 LLView* LLJoystickAgentSlide::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
00439 {
00440 LLString name("button");
00441 node->getAttributeString("name", name);
00442
00443 LLString image_unselected;
00444 if (node->hasAttribute("image_unselected")) node->getAttributeString("image_unselected",image_unselected);
00445
00446 LLString image_selected;
00447 if (node->hasAttribute("image_selected")) node->getAttributeString("image_selected",image_selected);
00448
00449
00450 EJoystickQuadrant quad = JQ_ORIGIN;
00451 if (node->hasAttribute("quadrant")) quad = selectQuadrant(node);
00452
00453 LLJoystickAgentSlide *button = new LLJoystickAgentSlide(name,
00454 LLRect(),
00455 image_unselected,
00456 image_selected,
00457 quad);
00458
00459 if (node->hasAttribute("halign"))
00460 {
00461 LLFontGL::HAlign halign = selectFontHAlign(node);
00462 button->setHAlign(halign);
00463 }
00464
00465 if (node->hasAttribute("scale_image"))
00466 {
00467 BOOL needsScale = FALSE;
00468 node->getAttributeBOOL("scale_image",needsScale);
00469 button->setScaleImage( needsScale );
00470 }
00471
00472 button->initFromXML(node, parent);
00473
00474 return button;
00475 }
00476
00477
00478
00479
00480
00481
00482 LLJoystickCameraRotate::LLJoystickCameraRotate(const LLString& name, LLRect rect, const LLString &out_img, const LLString &in_img)
00483 :
00484 LLJoystick(name, rect, out_img, in_img, JQ_ORIGIN),
00485 mInLeft( FALSE ),
00486 mInTop( FALSE ),
00487 mInRight( FALSE ),
00488 mInBottom( FALSE )
00489 { }
00490
00491
00492 void LLJoystickCameraRotate::updateSlop()
00493 {
00494
00495
00496
00497 mVertSlopNear = 16;
00498 mVertSlopFar = 32;
00499
00500 mHorizSlopNear = 16;
00501 mHorizSlopFar = 32;
00502
00503 return;
00504 }
00505
00506
00507 BOOL LLJoystickCameraRotate::handleMouseDown(S32 x, S32 y, MASK mask)
00508 {
00509 updateSlop();
00510
00511
00512 S32 horiz_center = getRect().getWidth() / 2;
00513 S32 vert_center = getRect().getHeight() / 2;
00514
00515 S32 dx = x - horiz_center;
00516 S32 dy = y - vert_center;
00517
00518 if (dy > dx && dy > -dx)
00519 {
00520
00521 mInitialOffset.mX = 0;
00522 mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
00523 mInitialQuadrant = JQ_UP;
00524 }
00525 else if (dy > dx && dy <= -dx)
00526 {
00527
00528 mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
00529 mInitialOffset.mY = 0;
00530 mInitialQuadrant = JQ_LEFT;
00531 }
00532 else if (dy <= dx && dy <= -dx)
00533 {
00534
00535 mInitialOffset.mX = 0;
00536 mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
00537 mInitialQuadrant = JQ_DOWN;
00538 }
00539 else
00540 {
00541
00542 mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
00543 mInitialOffset.mY = 0;
00544 mInitialQuadrant = JQ_RIGHT;
00545 }
00546
00547 return LLJoystick::handleMouseDown(x, y, mask);
00548 }
00549
00550
00551 void LLJoystickCameraRotate::onHeldDown()
00552 {
00553 updateSlop();
00554
00555 S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
00556 S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
00557
00558
00559 if (dx > mHorizSlopNear)
00560 {
00561 gAgent.unlockView();
00562 gAgent.setOrbitLeftKey(getOrbitRate());
00563 }
00564 else if (dx < -mHorizSlopNear)
00565 {
00566 gAgent.unlockView();
00567 gAgent.setOrbitRightKey(getOrbitRate());
00568 }
00569
00570
00571 if (dy > mVertSlopNear)
00572 {
00573 gAgent.unlockView();
00574 gAgent.setOrbitUpKey(getOrbitRate());
00575 }
00576 else if (dy < -mVertSlopNear)
00577 {
00578 gAgent.unlockView();
00579 gAgent.setOrbitDownKey(getOrbitRate());
00580 }
00581 }
00582
00583 F32 LLJoystickCameraRotate::getOrbitRate()
00584 {
00585 F32 time = getElapsedHeldDownTime();
00586 if( time < NUDGE_TIME )
00587 {
00588 F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME;
00589
00590 return rate;
00591 }
00592 else
00593 {
00594 return 1;
00595 }
00596 }
00597
00598
00599
00600 void LLJoystickCameraRotate::setToggleState( BOOL left, BOOL top, BOOL right, BOOL bottom )
00601 {
00602 mInLeft = left;
00603 mInTop = top;
00604 mInRight = right;
00605 mInBottom = bottom;
00606 }
00607
00608 void LLJoystickCameraRotate::draw()
00609 {
00610 LLGLSUIDefault gls_ui;
00611
00612 getImageUnselected()->draw( 0, 0 );
00613
00614 if( mInTop )
00615 {
00616 drawRotatedImage( getImageSelected()->getImage(), 0 );
00617 }
00618
00619 if( mInRight )
00620 {
00621 drawRotatedImage( getImageSelected()->getImage(), 1 );
00622 }
00623
00624 if( mInBottom )
00625 {
00626 drawRotatedImage( getImageSelected()->getImage(), 2 );
00627 }
00628
00629 if( mInLeft )
00630 {
00631 drawRotatedImage( getImageSelected()->getImage(), 3 );
00632 }
00633
00634 if (sDebugRects)
00635 {
00636 drawDebugRect();
00637 }
00638 }
00639
00640
00641 void LLJoystickCameraRotate::drawRotatedImage( const LLImageGL* image, S32 rotations )
00642 {
00643 S32 width = image->getWidth();
00644 S32 height = image->getHeight();
00645
00646 F32 uv[][2] =
00647 {
00648 { 1.f, 1.f },
00649 { 0.f, 1.f },
00650 { 0.f, 0.f },
00651 { 1.f, 0.f }
00652 };
00653
00654 image->bind();
00655
00656 gGL.color4fv(UI_VERTEX_COLOR.mV);
00657
00658 gGL.begin(LLVertexBuffer::QUADS);
00659 {
00660 gGL.texCoord2fv( uv[ (rotations + 0) % 4]);
00661 gGL.vertex2i(width, height );
00662
00663 gGL.texCoord2fv( uv[ (rotations + 1) % 4]);
00664 gGL.vertex2i(0, height );
00665
00666 gGL.texCoord2fv( uv[ (rotations + 2) % 4]);
00667 gGL.vertex2i(0, 0);
00668
00669 gGL.texCoord2fv( uv[ (rotations + 3) % 4]);
00670 gGL.vertex2i(width, 0);
00671 }
00672 gGL.end();
00673 }
00674
00675
00676
00677
00678
00679
00680
00681
00682 void LLJoystickCameraTrack::onHeldDown()
00683 {
00684 updateSlop();
00685
00686 S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
00687 S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
00688
00689 if (dx > mVertSlopNear)
00690 {
00691 gAgent.unlockView();
00692 gAgent.setPanRightKey(getOrbitRate());
00693 }
00694 else if (dx < -mVertSlopNear)
00695 {
00696 gAgent.unlockView();
00697 gAgent.setPanLeftKey(getOrbitRate());
00698 }
00699
00700
00701 if (dy > mVertSlopNear)
00702 {
00703 gAgent.unlockView();
00704 gAgent.setPanUpKey(getOrbitRate());
00705 }
00706 else if (dy < -mVertSlopNear)
00707 {
00708 gAgent.unlockView();
00709 gAgent.setPanDownKey(getOrbitRate());
00710 }
00711 }
00712
00713
00714
00715
00716
00717
00718
00719 LLJoystickCameraZoom::LLJoystickCameraZoom(const LLString& name, LLRect rect, const LLString &out_img, const LLString &plus_in_img, const LLString &minus_in_img)
00720 :
00721 LLJoystick(name, rect, out_img, "", JQ_ORIGIN),
00722 mInTop( FALSE ),
00723 mInBottom( FALSE )
00724 {
00725 mPlusInImage = LLUIImageList::getInstance()->getUIImage(plus_in_img);
00726 mMinusInImage = LLUIImageList::getInstance()->getUIImage(minus_in_img);
00727 }
00728
00729
00730 BOOL LLJoystickCameraZoom::handleMouseDown(S32 x, S32 y, MASK mask)
00731 {
00732 BOOL handled = LLJoystick::handleMouseDown(x, y, mask);
00733
00734 if( handled )
00735 {
00736 if (mFirstMouse.mY > getRect().getHeight() / 2)
00737 {
00738 mInitialQuadrant = JQ_UP;
00739 }
00740 else
00741 {
00742 mInitialQuadrant = JQ_DOWN;
00743 }
00744 }
00745 return handled;
00746 }
00747
00748
00749 void LLJoystickCameraZoom::onHeldDown()
00750 {
00751 updateSlop();
00752
00753 const F32 FAST_RATE = 2.5f;
00754
00755 S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
00756
00757 if (dy > mVertSlopFar)
00758 {
00759
00760 gAgent.unlockView();
00761 gAgent.setOrbitInKey(FAST_RATE);
00762 }
00763 else if (dy > mVertSlopNear)
00764 {
00765
00766 gAgent.unlockView();
00767 gAgent.setOrbitInKey(getOrbitRate());
00768 }
00769 else if (dy < -mVertSlopFar)
00770 {
00771
00772 gAgent.unlockView();
00773 gAgent.setOrbitOutKey(FAST_RATE);
00774 }
00775 else if (dy < -mVertSlopNear)
00776 {
00777
00778 gAgent.unlockView();
00779 gAgent.setOrbitOutKey(getOrbitRate());
00780 }
00781 }
00782
00783
00784 void LLJoystickCameraZoom::setToggleState( BOOL top, BOOL bottom )
00785 {
00786 mInTop = top;
00787 mInBottom = bottom;
00788 }
00789
00790 void LLJoystickCameraZoom::draw()
00791 {
00792 if( mInTop )
00793 {
00794 mPlusInImage->draw(0,0);
00795 }
00796 else
00797 if( mInBottom )
00798 {
00799 mMinusInImage->draw(0,0);
00800 }
00801 else
00802 {
00803 getImageUnselected()->draw( 0, 0 );
00804 }
00805
00806 if (sDebugRects)
00807 {
00808 drawDebugRect();
00809 }
00810 }
00811
00812 void LLJoystickCameraZoom::updateSlop()
00813 {
00814 mVertSlopNear = getRect().getHeight() / 4;
00815 mVertSlopFar = getRect().getHeight() / 2;
00816
00817 mHorizSlopNear = getRect().getWidth() / 4;
00818 mHorizSlopFar = getRect().getWidth() / 2;
00819
00820
00821
00822 switch (mInitialQuadrant)
00823 {
00824 case JQ_ORIGIN:
00825 mInitialOffset.set(0, 0);
00826 break;
00827
00828 case JQ_UP:
00829 mInitialOffset.mX = 0;
00830 mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
00831 break;
00832
00833 case JQ_DOWN:
00834 mInitialOffset.mX = 0;
00835 mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
00836 break;
00837
00838 case JQ_LEFT:
00839 mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
00840 mInitialOffset.mY = 0;
00841 break;
00842
00843 case JQ_RIGHT:
00844 mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
00845 mInitialOffset.mY = 0;
00846 break;
00847
00848 default:
00849 llerrs << "LLJoystick::LLJoystick() - bad switch case" << llendl;
00850 break;
00851 }
00852
00853 return;
00854 }
00855
00856
00857 F32 LLJoystickCameraZoom::getOrbitRate()
00858 {
00859 F32 time = getElapsedHeldDownTime();
00860 if( time < NUDGE_TIME )
00861 {
00862 F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME;
00863
00864 return rate;
00865 }
00866 else
00867 {
00868 return 1;
00869 }
00870 }