├── big.ico ├── small.ico ├── OBProjectSettings.cpp ├── README.md ├── resource.h ├── OBGlobals.h ├── OBObject.h ├── LICENSE ├── OBEngine.h ├── resource.rc ├── Path.h ├── Orbit.h ├── OBObject.cpp ├── OBEngine.cpp ├── Orbit.cpp └── Path.cpp /big.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-martian-trajectory-app/trajectory/HEAD/big.ico -------------------------------------------------------------------------------- /small.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-martian-trajectory-app/trajectory/HEAD/small.ico -------------------------------------------------------------------------------- /OBProjectSettings.cpp: -------------------------------------------------------------------------------- 1 | #include "FGProjectSettings.h" 2 | 3 | #include "OBEngine.h" 4 | FGEngine *createEngine() 5 | { 6 | FGEngine *newEngine = new OBEngine(); 7 | return newEngine; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # trajectory 2 | The non-proprietary components of Andy Weir's application he made to calculate orbits for his book "The Martian" 3 | 4 | These sources are provided courtesy of Andy Weir, with the understanding that he is not willing to provide any tech support or assistance at all in building or using it. 5 | 6 | There are many references to graphics and app management classes that aren't present because they are proprietary. All the math and calculation stuff is present. 7 | -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Developer Studio generated include file. 3 | // Used by resource.rc 4 | // 5 | #define IDC_MYICON 2 6 | #define IDD_WINPC_DIALOG 102 7 | #define IDI_BIG 107 8 | #define IDI_SMALL 108 9 | #define IDR_MAINFRAME 128 10 | #define IDC_STATIC -1 11 | 12 | // Next default values for new objects 13 | // 14 | #ifdef APSTUDIO_INVOKED 15 | #ifndef APSTUDIO_READONLY_SYMBOLS 16 | #define _APS_NEXT_RESOURCE_VALUE 130 17 | #define _APS_NEXT_COMMAND_VALUE 32771 18 | #define _APS_NEXT_CONTROL_VALUE 1000 19 | #define _APS_NEXT_SYMED_VALUE 110 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /OBGlobals.h: -------------------------------------------------------------------------------- 1 | #ifndef __OBGLOBALS__ 2 | #define __OBGLOBALS__ 3 | 4 | // NOTE: All units in this simulation are kg, km, and seconds 5 | 6 | // SGPs are in km^3/s^2 7 | #define SUN_SGP (132712440018.0) 8 | 9 | 10 | // distances are in km 11 | #define VENUS_APOGEE (108942109.0) 12 | #define EARTH_APOGEE (152098232.0) 13 | #define MARS_APOGEE (249209300.0) 14 | 15 | // velocities are in km/s 16 | #define VENUS_APOGEE_VEL (35.02) 17 | #define EARTH_APOGEE_VEL (29.3) 18 | #define MARS_APOGEE_VEL (21.97) 19 | 20 | // angles are in radians 21 | // AOP = Argument of Periapsis 22 | #define VENUS_AOP (0.9581) // 54.9 degrees 23 | #define EARTH_AOP (1.99330) // 114.20783 degrees 24 | #define MARS_AOP (4.9997) // 286.4623 degrees 25 | 26 | // how much time is in a tick, in seconds 27 | // some helpful values: 28 | // One day: 86400.0 29 | // half-day: 43200.0 30 | // hour: 3600.0 31 | #define TICK_SECONDS (43200.0) 32 | 33 | // how much acceleration the ship has. This is in km/s^2 34 | #define SHIP_ACC (0.0000001) 35 | 36 | // pi 37 | #define PI (3.1415926535) 38 | #define TWOPI (6.2831853071) 39 | 40 | // helper functions 41 | bool fnear(double a, double b, double slop = 0.001); 42 | 43 | #endif 44 | 45 | -------------------------------------------------------------------------------- /OBObject.h: -------------------------------------------------------------------------------- 1 | #ifndef __OBOJECT__ 2 | #define __OBOJECT__ 3 | 4 | #include "FGDoubleVector.h" 5 | #include "FGGraphics.h" 6 | #include "Orbit.h" 7 | 8 | class OBEngine; 9 | 10 | class OBObject 11 | { 12 | public: 13 | OBObject(); 14 | ~OBObject(); 15 | 16 | // you can send NULL for the orbitee. It's ok to send 0 for SGP 17 | void initOrbitee(double sgp, FGDoubleVector &pos, int color, int size); 18 | void initOrbiter(OBObject *orbitee, FGDoubleVector &pos, FGDoubleVector &vel, int color, int size); 19 | void initOrbiter(OBObject *orbitee, double apogeeDist, double apogeeVel, double aop, int color, int size); 20 | void initOrbiter(OBObject *orbitee, Orbit &orbit, double theta, int color, int size); 21 | void drawSelf(FGGraphics &g); 22 | void tick(double seconds); 23 | 24 | // something external has affected the position or velocity. 25 | // recalculate the orbit based on our current pos and vel 26 | void recalcOrbit(); 27 | 28 | // data 29 | FGDoubleVector m_pos; 30 | FGDoubleVector m_vel; 31 | 32 | // display stuff 33 | int m_color; 34 | int m_size; 35 | 36 | // relevant only if something is orbiting this 37 | double m_sgp; 38 | 39 | // cached for perf 40 | OBEngine *m_engine; 41 | 42 | // the thing we're orbiting 43 | OBObject *m_orbitee; 44 | 45 | Orbit m_orbit; 46 | }; 47 | 48 | #endif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Andy Weir 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of trajectory nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /OBEngine.h: -------------------------------------------------------------------------------- 1 | #ifndef __ACENGINE__ 2 | #define __ACENGINE__ 3 | 4 | #include "FGEngine.h" 5 | #include "FGFont.h" 6 | #include "FGDoubleVector.h" 7 | #include "FGTimer.h" 8 | 9 | #include "OBGlobals.h" 10 | #include "OBObject.h" 11 | #include "Path.h" 12 | 13 | #define UI_INERT 0 14 | #define UI_ADDINGPOINT 1 15 | #define UI_ADJUSTINGPOINT 2 16 | #define UI_ADJUSTINGMARS 3 17 | #define UI_PLAYBACK 4 18 | 19 | class OBEngine : public FGEngine 20 | { 21 | public: 22 | 23 | OBEngine(); 24 | virtual ~OBEngine(); 25 | virtual void init(); 26 | virtual void onKeyPressed(int key); 27 | virtual void onKeyReleased(int key); 28 | virtual void onDrawSelf(FGGraphics &g); 29 | virtual void onTick(); 30 | virtual void onPause(); 31 | virtual void onResume(); 32 | virtual void onExitApp(); 33 | virtual void onMousePressed(int button); 34 | virtual void onMouseReleased(int button); 35 | virtual void onMouseWheel(int delta); 36 | virtual void onFileDrop(const char *filePath); 37 | virtual const char *getTitle() { return "Orbits"; } 38 | virtual int getForcedScreenWidth() { return 768; } 39 | virtual int getForcedScreenHeight() { return 768; } 40 | 41 | int modelToViewX(double modelX); 42 | int modelToViewY(double modelY); 43 | 44 | void drawPlayback(FGGraphics &g); 45 | void drawNormal(FGGraphics &g); 46 | void drawPathObject(FGGraphics &g, Path *toDraw, int pointIdx); 47 | void addDistInfo(FGString &str, int dist); 48 | 49 | void load(const char *filename); 50 | 51 | // font 52 | FGFont m_font; 53 | 54 | // scale and translation 55 | FGDoubleVector m_center; // this x,y location will be centered on screen 56 | double m_kmPerPixel; 57 | 58 | // cached for perf 59 | int m_screenW; 60 | int m_screenH; 61 | 62 | // bodies 63 | OBObject m_sun; 64 | OBObject m_venus; 65 | OBObject m_earth; 66 | OBObject m_mars; 67 | Path m_ship; 68 | 69 | // paths 70 | Path m_venusPath; 71 | Path m_earthPath; 72 | Path m_marsPath; 73 | 74 | // UI stuff 75 | int m_uiMode; // a UI_XXXX constant 76 | int m_hoverPathPointIdx; 77 | AccelerationPoint *m_hoverAccelPoint; 78 | FGString m_msg; 79 | bool m_bShowVenus; 80 | 81 | // playback stuff 82 | FGTimer m_playbackTimer; 83 | int m_playbackStepIdx; 84 | }; 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /resource.rc: -------------------------------------------------------------------------------- 1 | //Microsoft Developer Studio generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #define APSTUDIO_HIDDEN_SYMBOLS 11 | #include "windows.h" 12 | #undef APSTUDIO_HIDDEN_SYMBOLS 13 | #include "resource.h" 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | #undef APSTUDIO_READONLY_SYMBOLS 17 | 18 | ///////////////////////////////////////////////////////////////////////////// 19 | // English (U.S.) resources 20 | 21 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 22 | #ifdef _WIN32 23 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 24 | #pragma code_page(1252) 25 | #endif //_WIN32 26 | 27 | ///////////////////////////////////////////////////////////////////////////// 28 | // 29 | // Icon 30 | // 31 | 32 | // Icon with lowest ID value placed first to ensure application icon 33 | // remains consistent on all systems. 34 | IDI_BIG ICON DISCARDABLE "BIG.ICO" 35 | IDI_SMALL ICON DISCARDABLE "SMALL.ICO" 36 | 37 | #ifdef APSTUDIO_INVOKED 38 | ///////////////////////////////////////////////////////////////////////////// 39 | // 40 | // TEXTINCLUDE 41 | // 42 | 43 | 2 TEXTINCLUDE DISCARDABLE 44 | BEGIN 45 | "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" 46 | "#include ""windows.h""\r\n" 47 | "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" 48 | "#include ""resource.h""\r\n" 49 | "\0" 50 | END 51 | 52 | 3 TEXTINCLUDE DISCARDABLE 53 | BEGIN 54 | "\r\n" 55 | "\0" 56 | END 57 | 58 | 1 TEXTINCLUDE DISCARDABLE 59 | BEGIN 60 | "resource.h\0" 61 | END 62 | 63 | #endif // APSTUDIO_INVOKED 64 | 65 | #endif // English (U.S.) resources 66 | ///////////////////////////////////////////////////////////////////////////// 67 | 68 | 69 | 70 | #ifndef APSTUDIO_INVOKED 71 | ///////////////////////////////////////////////////////////////////////////// 72 | // 73 | // Generated from the TEXTINCLUDE 3 resource. 74 | // 75 | 76 | 77 | ///////////////////////////////////////////////////////////////////////////// 78 | #endif // not APSTUDIO_INVOKED 79 | 80 | -------------------------------------------------------------------------------- /Path.h: -------------------------------------------------------------------------------- 1 | #ifndef __PATH__ 2 | #define __PATH__ 3 | 4 | #include "FGDoubleVector.h" 5 | #include "FGGraphics.h" 6 | #include 7 | 8 | class OBObject; 9 | class OBEngine; 10 | 11 | // how long each point represents, and how many points there are 12 | #define POINTS_TIME (86400.0) 13 | #define PATH_NUM_POINTS 900 14 | 15 | // due to the units of this simulator, this is in km/s^2. 16 | // So it's a very small number 17 | #define PATH_ACCELERATION (0.000002) 18 | 19 | #define DISPLAY_THRUSTLINE_LENGTH 50 20 | 21 | // acceleration point types 22 | #define ACCTYPE_NORMAL 0 // normal acceleration 23 | #define ACCTYPE_REDIRECT 1 // reset the direction of travel, keep the current velocity 24 | #define ACCTYPE_STOPTRACE 2 // stop showing the trace beyond this point 25 | 26 | #define FATAL_SUN_APPROACH (35000000.0) 27 | 28 | // an acceleration point 29 | class AccelerationPoint 30 | { 31 | public: 32 | int m_pointIdx; // the point index that this acceleration takes effect 33 | 34 | // the acceleration, relative to the vector from the object to the thing its orbiting 35 | int m_type; 36 | double m_angle; 37 | double m_mag; 38 | }; 39 | 40 | typedef std::list AccelerationPointList; 41 | typedef AccelerationPointList::iterator AccelerationPointIter; 42 | 43 | class Path 44 | { 45 | public: 46 | Path(); 47 | virtual ~Path(); 48 | 49 | void init(OBObject *orbitee, FGDoubleVector &pos, FGDoubleVector &vel, int color, int size); 50 | void initNoAcc(OBObject *orbitee, FGDoubleVector &pos, FGDoubleVector &vel, int color, int size); 51 | 52 | // takes angle in J200 coordinate system 53 | void initNoAcc(OBObject *orbiter, double angle); 54 | 55 | void drawSelf(FGGraphics &g, AccelerationPoint *sel); 56 | void drawThrustLine(FGGraphics &g, int pointIDX); 57 | void drawProgressivePath(FGGraphics &g, int pointIdx); 58 | 59 | int getNearestPointIdx(int viewX, int viewY); 60 | AccelerationPoint *getNearestAccelPoint(int viewX, int viewY); 61 | 62 | void getThrustForPoint(int pointIdx, FGDoubleVector &result); 63 | void getGravForPoint(int pointIdx, FGDoubleVector &result); 64 | int getStopPoint(); 65 | void calcPoints(); 66 | 67 | AccelerationPoint *createAccelerationPoint(int pointIdx); 68 | void removeAccelerationPoint(AccelerationPoint *ap); 69 | void adjustAccelerationPoint(AccelerationPoint *ap, int mx, int my, double newMag); 70 | 71 | // persistance 72 | void save(FGDataWriter &out); 73 | void load(FGDataReader &in); 74 | 75 | // data 76 | FGDoubleVector m_startPos; 77 | FGDoubleVector m_startVel; 78 | FGDoubleVector m_points[PATH_NUM_POINTS]; // in model coordinates 79 | OBObject *m_orbitee; 80 | OBEngine *m_engine; 81 | 82 | // acceleration points 83 | AccelerationPointList m_accelerationPoints; 84 | 85 | // display stuff 86 | int m_color; 87 | int m_size; 88 | }; 89 | 90 | #endif 91 | 92 | -------------------------------------------------------------------------------- /Orbit.h: -------------------------------------------------------------------------------- 1 | #ifndef __ORBIT__ 2 | #define __ORBIT__ 3 | 4 | #include "FGDoubleVector.h" 5 | #include "FGGraphics.h" 6 | 7 | class OBEngine; 8 | 9 | class DrawPoint 10 | { 11 | public: 12 | int m_viewX; 13 | int m_viewY; 14 | }; 15 | 16 | // note: the gravitic body is presumed to be at 0,0. This means that the f1 point 17 | // will *always* be at (0.0, 0.0). 18 | 19 | class Orbit 20 | { 21 | public: 22 | Orbit(); 23 | ~Orbit(); 24 | 25 | void set(Orbit &other); // copy all the values of the sent-in orbit 26 | 27 | // init the orbit with the key traits: 28 | // sgp = Standard Gravitational Parameter of the gravitic mass 29 | // e = eccentricity of the orbit 30 | // a = semi-major axis length. Note this is NOT apogee distance. 31 | // This is the distance from apogee to the center of the 32 | // ellipse. (Not from the gravitation object at a focus) 33 | // w = Angle of the major axis. This is also the angle of apogee. 34 | void init(double sgp, double e, double a, double w); 35 | 36 | // init the orbit with the gravitic mass, sgp, and an orbiter's position and velocity 37 | void initPV(double sgp, FGDoubleVector &orbiterPos, FGDoubleVector &orbiterVel); 38 | 39 | // init the orbit with known apogee and perigee points 40 | void initAP(double sgp, FGDoubleVector &apogee, FGDoubleVector &perigee); 41 | 42 | // information about the orbit 43 | bool isValid() { return m_bValid; } 44 | double getR(double theta); // send in the theta, this will return the r (distance from F1 to the ellipse point) 45 | double calcDeviance(Orbit &other); // very time consuming. Gives the total area of the two orbits that does *not* overlap. 46 | void getVel(double theta, FGDoubleVector &outVel); // get the velocity vector for the body when its at angle theta. 47 | void getPos(double theta, FGDoubleVector &outPos); // get the position vector for the body when its at angle theta. 48 | 49 | // display settings 50 | void setColorFromObjectColor(int objectColor); // work out a color based on the orbiter's color 51 | void setColor(int color); // set the color directly 52 | 53 | // draw 54 | void drawSelf(FGGraphics &g); 55 | 56 | 57 | // helpers 58 | void calcDrawPoints(); 59 | 60 | // display 61 | int m_color; 62 | OBEngine *m_engine; 63 | DrawPoint *m_drawPoints; 64 | int m_numDrawPoints; 65 | 66 | // relevant orbit data. However the orbit is initted, all 67 | // these values will be calculated and stored. 68 | FGDoubleVector m_f1; // focus 1 (the location of the gravitic object) 69 | FGDoubleVector m_f2; // focus 2 (no object here) 70 | FGDoubleVector m_center; // center of the ellipse 71 | double m_u; // the Standard Gravitation Parameter of the gravitic mass 72 | double m_e; // eccentricity of the ellipse 73 | double m_a; // semi-major axis length of the ellipse 74 | double m_b; // semi-minor axis length of the ellipse 75 | double m_f; // distance from C to either focus 76 | double m_w; // angle (radians) of the line through the foci 77 | double m_energy; // specific orbital energy of this orbit 78 | double m_apogee; // distance from the gravitic body at apogee 79 | double m_perigee; // distance from the gravitic body at perigee 80 | double m_orbitArea; // the total area of this orbit (area of the ellipse) 81 | 82 | // if this is false, it means it's not an orbit. Usually this means it's an escape, 83 | // but it can also mean the path comes too close to the gravity object for calculation. 84 | // If this is true, you should consider all functions inoperative. 85 | bool m_bValid; 86 | }; 87 | 88 | 89 | #endif 90 | 91 | -------------------------------------------------------------------------------- /OBObject.cpp: -------------------------------------------------------------------------------- 1 | #include "OBObject.h" 2 | #include "OBEngine.h" 3 | 4 | #include "FGDoubleGeometry.h" 5 | 6 | OBObject::OBObject() 7 | { 8 | } 9 | 10 | OBObject::~OBObject() 11 | { 12 | } 13 | 14 | void OBObject::initOrbitee(double sgp, FGDoubleVector &pos, int color, int size) 15 | { 16 | m_pos.set(pos); 17 | m_vel.setXY(0.0, 0.0); 18 | m_color = color; 19 | m_size = size; 20 | m_sgp = sgp; 21 | m_orbitee = NULL; 22 | 23 | m_engine = (OBEngine *)FGEngine::getEngine(); 24 | } 25 | 26 | void OBObject::initOrbiter(OBObject *orbitee, double apogeeDist, double apogeeVel, double aop, int color, int size) 27 | { 28 | // work out the position and velocity of apogee 29 | FGDoubleVector pos; 30 | FGDoubleVector vel; 31 | 32 | // we know the angle of periapsis. That minus PI will be 33 | // the andge of apogee. And we are given the distance at apogee. 34 | double angleOfApogee = aop - 3.14159; 35 | pos.setRTheta(apogeeDist, angleOfApogee); 36 | pos.addVector(orbitee->m_pos); // offset by the position of the orbitee 37 | 38 | // the vel is PI/2 radians off from the position. 39 | vel.set(pos); 40 | vel.rotate(-3.14159/2.0); 41 | vel.setLength(apogeeVel); 42 | 43 | // now we have the position and velocity of apogee. We can feed it to the main initter. 44 | initOrbiter(orbitee, pos, vel, color, size); 45 | } 46 | 47 | void OBObject::initOrbiter(OBObject *orbitee, FGDoubleVector &pos, FGDoubleVector &vel, int color, int size) 48 | { 49 | m_pos.set(pos); 50 | m_vel.set(vel); 51 | m_color = color; 52 | m_size = size; 53 | m_sgp = 0.0; 54 | m_orbitee = orbitee; 55 | m_engine = (OBEngine *)FGEngine::getEngine(); 56 | 57 | // prep the orbit 58 | m_orbit.initPV(m_orbitee->m_sgp, m_pos, m_vel); 59 | m_orbit.setColorFromObjectColor(m_color); 60 | } 61 | 62 | void OBObject::initOrbiter(OBObject *orbitee, Orbit &orbit, double theta, int color, int size) 63 | { 64 | // note the basics 65 | m_color = color; 66 | m_size = size; 67 | m_sgp = 0.0; 68 | m_orbitee = orbitee; 69 | m_engine = (OBEngine *)FGEngine::getEngine(); 70 | 71 | // adopt that orbit, and note color stuff 72 | m_orbit.set(orbit); 73 | m_orbit.setColorFromObjectColor(m_color); 74 | 75 | // work out our position and velocity from the sent-in angle 76 | m_orbit.getPos(theta, m_pos); 77 | m_orbit.getVel(theta, m_vel); 78 | } 79 | 80 | void OBObject::tick(double seconds) 81 | { 82 | if ( m_orbitee == NULL ) return; 83 | 84 | // accelerate based on the gravitic object 85 | // start with a vector pointed at the orbitee 86 | FGDoubleVector acc; 87 | acc.set(m_orbitee->m_pos); 88 | acc.subtractVector(m_pos); 89 | 90 | // work out the strength of the acceleration. 91 | // It'll be u/d^2 where u = SGP, and d = distance to the object 92 | // this is a good time to include the seconds value 93 | double accLen = (seconds * m_orbitee->m_sgp)/acc.getLengthSq(); 94 | 95 | // set the new length 96 | acc.setLength(accLen); 97 | 98 | // apply the acceleration 99 | m_vel.addVector(acc); 100 | 101 | // apply the velocity. Our vel is in km/s, so we need to multiply 102 | FGDoubleVector toAdd; 103 | toAdd.set(m_vel); 104 | toAdd.scalarMultiply(seconds); 105 | m_pos.addVector(toAdd); 106 | } 107 | 108 | void OBObject::drawSelf(FGGraphics &g) 109 | { 110 | m_orbit.drawSelf(g); 111 | 112 | // note our location, offset by half our size 113 | int x = m_engine->modelToViewX(m_pos.m_fixX) - m_size/2; 114 | int y = m_engine->modelToViewY(m_pos.m_fixY) - m_size/2; 115 | 116 | g.setColor(m_color); 117 | g.fillRect(x, y, m_size, m_size); 118 | } 119 | 120 | void OBObject::recalcOrbit() 121 | { 122 | m_orbit.initPV(m_orbit.m_u, m_pos, m_vel); 123 | } 124 | 125 | 126 | -------------------------------------------------------------------------------- /OBEngine.cpp: -------------------------------------------------------------------------------- 1 | #include "OBEngine.h" 2 | #include "FGDataWriter.h" 3 | #include "FGDataReader.h" 4 | #include "FGDoubleGeometry.h" 5 | 6 | #define PLAYBACK_STEP_TIME 50 7 | 8 | OBEngine::OBEngine() 9 | { 10 | } 11 | 12 | OBEngine::~OBEngine() 13 | { 14 | } 15 | 16 | void OBEngine::init() 17 | { 18 | m_screenW = getScreenWidth(); 19 | m_screenH = getScreenHeight(); 20 | 21 | // init the font 22 | m_font.init("smallfont.png", "smallfont.font"); 23 | 24 | // init graphics metrics 25 | double r = MARS_APOGEE*1.05; 26 | double width = r*2.0; 27 | m_kmPerPixel = width/(double)m_screenW; 28 | 29 | // start off centered at 0,0 30 | m_center.setXY(0.0, 0.0); 31 | 32 | // sun and earth 33 | FGDoubleVector pos; 34 | FGDoubleVector vel; 35 | 36 | // the sun is in the middle and doesn't move 37 | pos.setXY(0.0, 0.0); 38 | vel.setXY(0.0, 0.0); 39 | m_sun.initOrbitee(SUN_SGP, pos, 0xffffff, 8); 40 | 41 | // start the planets 42 | m_venus.initOrbiter(&m_sun, VENUS_APOGEE, VENUS_APOGEE_VEL, VENUS_AOP, 0xffff7f, 1); 43 | m_earth.initOrbiter(&m_sun, EARTH_APOGEE, EARTH_APOGEE_VEL, EARTH_AOP, 0x7f7fff, 1); 44 | m_mars.initOrbiter(&m_sun, MARS_APOGEE, MARS_APOGEE_VEL, MARS_AOP, 0xff7f7f, 1); 45 | 46 | /************ PATHS *****************/ 47 | // July 7, 2035 48 | m_venusPath.initNoAcc(&m_venus, 1.4623927498); 49 | m_earthPath.initNoAcc(&m_earth, 4.9745875522); 50 | m_marsPath.initNoAcc(&m_mars, 5.4429575522); 51 | m_ship.init(&m_sun, m_earthPath.m_startPos, m_earthPath.m_startVel, 0x7f7f7f, 5); 52 | 53 | // internals 54 | m_hoverPathPointIdx = -1; 55 | m_hoverAccelPoint = NULL; 56 | m_uiMode = UI_INERT; 57 | m_bShowVenus = false; 58 | } 59 | 60 | void OBEngine::onKeyPressed(int key) 61 | { 62 | if ( key == 16 ) 63 | { 64 | m_hoverPathPointIdx = -1; 65 | m_hoverAccelPoint = NULL; 66 | m_uiMode = UI_ADDINGPOINT; 67 | } 68 | } 69 | 70 | void OBEngine::onKeyReleased(int key) 71 | { 72 | m_msg.set(""); 73 | 74 | if ( key == 'V' ) 75 | { 76 | m_bShowVenus = !m_bShowVenus; 77 | } 78 | 79 | if ( key == 16 ) 80 | { 81 | m_hoverPathPointIdx = -1; 82 | m_hoverAccelPoint = NULL; 83 | m_uiMode = UI_INERT; 84 | } 85 | 86 | if ( (key==8) || (key==46) ) 87 | { 88 | // delete/backspace 89 | if ( m_hoverAccelPoint != NULL ) 90 | { 91 | m_ship.removeAccelerationPoint(m_hoverAccelPoint); 92 | m_ship.calcPoints(); 93 | m_hoverAccelPoint = NULL; 94 | } 95 | } 96 | 97 | if ( key=='0' ) 98 | { 99 | // kill the thrust for this point 100 | if ( m_hoverAccelPoint != NULL ) 101 | { 102 | m_hoverAccelPoint->m_mag = 0.0; 103 | m_ship.calcPoints(); 104 | m_hoverAccelPoint = NULL; 105 | } 106 | } 107 | 108 | if ( key=='1' ) 109 | { 110 | // max out the thrust for this point 111 | if ( m_hoverAccelPoint != NULL ) 112 | { 113 | m_hoverAccelPoint->m_mag = PATH_ACCELERATION; 114 | m_ship.calcPoints(); 115 | m_hoverAccelPoint = NULL; 116 | } 117 | } 118 | if ( key == 'X' ) 119 | { 120 | if ( m_hoverAccelPoint != NULL ) 121 | { 122 | if ( m_hoverAccelPoint->m_type == ACCTYPE_NORMAL ) 123 | { 124 | m_hoverAccelPoint->m_type = ACCTYPE_STOPTRACE; 125 | } 126 | else if ( m_hoverAccelPoint->m_type == ACCTYPE_STOPTRACE ) 127 | { 128 | m_hoverAccelPoint->m_type = ACCTYPE_NORMAL; 129 | } 130 | m_ship.calcPoints(); 131 | m_hoverAccelPoint = NULL; 132 | } 133 | } 134 | if ( key == 'R' ) 135 | { 136 | if ( m_hoverAccelPoint != NULL ) 137 | { 138 | if ( m_hoverAccelPoint->m_type == ACCTYPE_NORMAL ) 139 | { 140 | m_hoverAccelPoint->m_type = ACCTYPE_REDIRECT; 141 | } 142 | else if ( m_hoverAccelPoint->m_type == ACCTYPE_REDIRECT ) 143 | { 144 | m_hoverAccelPoint->m_type = ACCTYPE_NORMAL; 145 | } 146 | m_ship.calcPoints(); 147 | m_hoverAccelPoint = NULL; 148 | } 149 | } 150 | 151 | if ( key == ' ' ) 152 | { 153 | // toggle playback 154 | if ( m_uiMode == UI_INERT ) 155 | { 156 | m_playbackStepIdx = 0; 157 | m_playbackTimer.start(PLAYBACK_STEP_TIME); 158 | m_uiMode = UI_PLAYBACK; 159 | } 160 | else 161 | { 162 | // halt playback 163 | m_uiMode = UI_INERT; 164 | } 165 | } 166 | 167 | if ( key == 116 ) 168 | { 169 | // save 170 | FGDataWriter out; 171 | out.init(); 172 | 173 | m_earthPath.save(out); 174 | m_marsPath.save(out); 175 | m_ship.save(out); 176 | 177 | FGData *toSave = out.getData(); 178 | getFileSystem()->putFile("path.sav", toSave); 179 | delete toSave; 180 | 181 | m_msg.set("Saved"); 182 | } 183 | if ( key == 117 ) 184 | { 185 | // load 186 | load("path.sav"); 187 | } 188 | } 189 | 190 | void OBEngine::load(const char *filename) 191 | { 192 | FGData *inData = getFileSystem()->getFile(filename); 193 | if ( inData == NULL ) return; 194 | 195 | FGDataReader in; 196 | in.init(inData); 197 | 198 | m_earthPath.load(in); 199 | m_marsPath.load(in); 200 | m_ship.load(in); 201 | delete inData; 202 | 203 | m_msg.set("Loaded "); 204 | m_msg.add(filename); 205 | 206 | m_uiMode = UI_INERT; 207 | 208 | // note the angle diff between Mars and Earth 209 | double angleE = m_earthPath.m_startPos.getAngle(); 210 | double angleM = m_marsPath.m_startPos.getAngle(); 211 | double diff = FGDoubleGeometry::angleDiff(angleE, angleM); 212 | int a=5; 213 | } 214 | 215 | void OBEngine::onDrawSelf(FGGraphics &g) 216 | { 217 | if ( m_uiMode == UI_PLAYBACK ) 218 | { 219 | drawPlayback(g); 220 | } 221 | else 222 | { 223 | drawNormal(g); 224 | } 225 | } 226 | 227 | void OBEngine::drawPathObject(FGGraphics &g, Path *toDraw, int pointIdx) 228 | { 229 | // dont' draw past the stop point 230 | int stopPoint = toDraw->getStopPoint(); 231 | if ( pointIdx > stopPoint ) return; 232 | 233 | int x = modelToViewX(toDraw->m_points[pointIdx].m_fixX); 234 | int y = modelToViewY(toDraw->m_points[pointIdx].m_fixY); 235 | g.fillRect(x-2, y-2, 5, 5); 236 | } 237 | 238 | void OBEngine::drawPlayback(FGGraphics &g) 239 | { 240 | // draw 241 | g.setColor(0); 242 | g.fillRect(0, 0, m_screenW, m_screenH); 243 | 244 | // draw the traces 245 | m_sun.drawSelf(g); 246 | if ( m_bShowVenus ) 247 | { 248 | m_venusPath.drawSelf(g, NULL); 249 | } 250 | m_earthPath.drawSelf(g, NULL); 251 | m_marsPath.drawSelf(g, NULL); 252 | 253 | 254 | m_ship.drawProgressivePath(g, m_playbackStepIdx); 255 | 256 | // draw the objects 257 | if ( m_bShowVenus ) 258 | { 259 | g.setColor(m_venus.m_color); 260 | drawPathObject(g, &m_venusPath, m_playbackStepIdx); 261 | } 262 | 263 | g.setColor(m_earth.m_color); 264 | drawPathObject(g, &m_earthPath, m_playbackStepIdx); 265 | 266 | g.setColor(m_mars.m_color); 267 | drawPathObject(g, &m_marsPath, m_playbackStepIdx); 268 | 269 | g.setColor(m_ship.m_color); 270 | drawPathObject(g, &m_ship, m_playbackStepIdx); 271 | 272 | // note the day and sol 273 | FGString out; 274 | out.set("Mission day: "); 275 | int missionDay = m_playbackStepIdx+1; 276 | out.add(missionDay); 277 | if ( missionDay >= 133 ) 278 | { 279 | // work out the sol 280 | double sol = (double)(missionDay-132); 281 | sol *= 86400.0; 282 | sol /= 88775.24409; 283 | int intSol = (int)sol; 284 | out.add("\nSol: "); 285 | out.add(intSol); 286 | } 287 | 288 | m_font.drawText(g, out.getNativeString(), 0, 0, m_screenW); 289 | } 290 | 291 | void OBEngine::drawNormal(FGGraphics &g) 292 | { 293 | // draw 294 | g.setColor(0); 295 | g.fillRect(0, 0, m_screenW, m_screenH); 296 | 297 | m_sun.drawSelf(g); 298 | if ( m_bShowVenus ) 299 | { 300 | m_venusPath.drawSelf(g, NULL); 301 | } 302 | 303 | m_earthPath.drawSelf(g, NULL); 304 | m_marsPath.drawSelf(g, NULL); 305 | 306 | // ship 307 | m_ship.drawSelf(g, m_hoverAccelPoint); 308 | 309 | // output 310 | bool bShowPlanets = false; 311 | if ( m_hoverPathPointIdx != -1 ) 312 | { 313 | g.setColor(0xff0000); 314 | m_ship.drawThrustLine(g, m_hoverPathPointIdx); 315 | 316 | // also note the day 317 | int daynum = (m_hoverPathPointIdx*86400)/(int)POINTS_TIME + 1; 318 | FGString out; 319 | out.set("Day "); 320 | out.add(daynum); 321 | 322 | // report distances 323 | int emDist = (int)FGDoubleGeometry::getDistance(m_earthPath.m_points[m_hoverPathPointIdx], m_marsPath.m_points[m_hoverPathPointIdx]); 324 | int ehDist = (int)FGDoubleGeometry::getDistance(m_earthPath.m_points[m_hoverPathPointIdx], m_ship.m_points[m_hoverPathPointIdx]); 325 | int mhDist = (int)FGDoubleGeometry::getDistance(m_marsPath.m_points[m_hoverPathPointIdx], m_ship.m_points[m_hoverPathPointIdx]); 326 | out.add("\nE-M Dist: "); 327 | addDistInfo(out, emDist); 328 | out.add("\nE-H Dist: "); 329 | addDistInfo(out, ehDist); 330 | out.add("\nH-M Dist: "); 331 | addDistInfo(out, mhDist); 332 | m_font.drawText(g, out.getNativeString(), 0, 0, m_screenW); 333 | 334 | bShowPlanets = true; 335 | } 336 | 337 | if ( m_uiMode == UI_ADJUSTINGMARS ) bShowPlanets = true; 338 | if ( isKeyDown(17) ) bShowPlanets = true; 339 | 340 | if ( bShowPlanets ) 341 | { 342 | int idx = m_hoverPathPointIdx; 343 | if ( idx == -1 ) 344 | { 345 | // just show the start point 346 | idx = 0; 347 | } 348 | 349 | // draw planets 350 | if ( m_bShowVenus ) 351 | { 352 | g.setColor(m_venus.m_color); 353 | drawPathObject(g, &m_venusPath, m_hoverPathPointIdx); 354 | } 355 | 356 | g.setColor(m_earth.m_color); 357 | drawPathObject(g, &m_earthPath, m_hoverPathPointIdx); 358 | 359 | g.setColor(m_mars.m_color); 360 | drawPathObject(g, &m_marsPath, m_hoverPathPointIdx); 361 | } 362 | 363 | // message 364 | if ( m_msg.length() > 0 ) 365 | { 366 | m_font.setJustify(FGFont::JUSTIFY_CENTER); 367 | m_font.drawText(g, m_msg.getNativeString(), 0, 0, m_screenW); 368 | m_font.setJustify(FGFont::JUSTIFY_LEFT); 369 | } 370 | } 371 | 372 | void OBEngine::addDistInfo(FGString &str, int dist) 373 | { 374 | int lightSeconds = dist/300000; 375 | int lightMinutes = lightSeconds/60; 376 | lightSeconds = lightSeconds%60; 377 | 378 | str.add(dist); 379 | str.add("km, "); 380 | str.add(lightMinutes); 381 | str.add("m "); 382 | str.add(lightSeconds); 383 | str.add("s"); 384 | } 385 | 386 | void OBEngine::onTick() 387 | { 388 | if ( m_uiMode == UI_PLAYBACK ) 389 | { 390 | // do playback and nothing else 391 | if ( m_playbackTimer.isOver() ) 392 | { 393 | m_playbackStepIdx+=2; 394 | 395 | int stopIdx = m_ship.getStopPoint(); 396 | if ( m_playbackStepIdx > stopIdx ) 397 | { 398 | m_playbackStepIdx = stopIdx; 399 | } 400 | m_playbackTimer.start(PLAYBACK_STEP_TIME); 401 | } 402 | } 403 | 404 | int mx = getMouseX(); 405 | int my = getMouseY(); 406 | 407 | if ( m_uiMode == UI_ADJUSTINGPOINT ) 408 | { 409 | if ( m_hoverAccelPoint == NULL ) return; 410 | m_ship.adjustAccelerationPoint(m_hoverAccelPoint, mx, my, m_hoverAccelPoint->m_mag); 411 | m_ship.calcPoints(); 412 | } 413 | else if ( m_uiMode == UI_ADJUSTINGMARS) 414 | { 415 | // place mars's startling position. 416 | // First get the angle from the center 417 | FGDoubleVector mouse; 418 | mouse.setXY(mx-m_screenW/2, my-m_screenH/2); 419 | double angle = mouse.getAngle(); 420 | 421 | // note mars's position and velocity at that angle 422 | FGDoubleVector newPos; 423 | FGDoubleVector newVel; 424 | m_mars.m_orbit.getPos(angle, newPos); 425 | m_mars.m_orbit.getVel(angle, newVel); 426 | 427 | // set it 428 | m_marsPath.m_startPos.set(newPos); 429 | m_marsPath.m_startVel.set(newVel); 430 | 431 | // recalc 432 | m_marsPath.calcPoints(); 433 | } 434 | else 435 | { 436 | if ( m_uiMode == UI_ADDINGPOINT ) 437 | { 438 | m_hoverPathPointIdx = m_ship.getNearestPointIdx(mx, my); 439 | } 440 | else 441 | { 442 | m_hoverAccelPoint = m_ship.getNearestAccelPoint(mx, my); 443 | } 444 | } 445 | } 446 | 447 | void OBEngine::onPause() 448 | { 449 | } 450 | 451 | void OBEngine::onResume() 452 | { 453 | } 454 | 455 | void OBEngine::onExitApp() 456 | { 457 | } 458 | 459 | void OBEngine::onMousePressed(int button) 460 | { 461 | if ( m_uiMode == UI_ADDINGPOINT ) return; 462 | 463 | if ( isKeyDown(17) ) // ctrl 464 | { 465 | m_uiMode = UI_ADJUSTINGMARS; 466 | } 467 | else 468 | { 469 | m_uiMode = UI_ADJUSTINGPOINT; 470 | } 471 | } 472 | 473 | void OBEngine::onMouseReleased(int button) 474 | { 475 | if ( (m_uiMode == UI_ADDINGPOINT) && (m_hoverPathPointIdx != -1) ) 476 | { 477 | // time to add a point 478 | AccelerationPoint *newPoint = m_ship.createAccelerationPoint(m_hoverPathPointIdx); 479 | m_ship.calcPoints(); 480 | } 481 | m_uiMode = UI_INERT; 482 | } 483 | 484 | void OBEngine::onMouseWheel(int delta) 485 | { 486 | } 487 | 488 | void OBEngine::onFileDrop(const char *filePath) 489 | { 490 | // load that file 491 | load(filePath); 492 | } 493 | 494 | int OBEngine::modelToViewX(double modelX) 495 | { 496 | // offset to the centerpoint 497 | double x = modelX - m_center.m_fixX; 498 | 499 | // scale to the screen 500 | x /= m_kmPerPixel; 501 | int ret = (int)x; 502 | 503 | // offset to center it 504 | ret += m_screenW/2; 505 | return ret; 506 | } 507 | 508 | int OBEngine::modelToViewY(double modelY) 509 | { 510 | // offset to the centerpoint 511 | double y = modelY - m_center.m_fixY; 512 | 513 | // scale to the screen 514 | y /= m_kmPerPixel; 515 | int ret = (int)y; 516 | 517 | // offset to center it 518 | ret += m_screenH/2; 519 | return ret; 520 | } 521 | 522 | // global helpers 523 | bool fnear(double a, double b, double slop) 524 | { 525 | double diff = fabs(a-b); 526 | if ( diff < slop ) return true; 527 | return false; 528 | } 529 | -------------------------------------------------------------------------------- /Orbit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Orbit.h" 3 | #include "OBEngine.h" 4 | #include "OBGlobals.h" 5 | #include "FGDoubleGeometry.h" 6 | 7 | Orbit::Orbit() 8 | { 9 | m_drawPoints = NULL; 10 | m_numDrawPoints = 0; 11 | m_color = 0x7f7f7f; 12 | m_bValid = false; 13 | } 14 | 15 | Orbit::~Orbit() 16 | { 17 | delete[] m_drawPoints; 18 | } 19 | 20 | void Orbit::set(Orbit &other) 21 | { 22 | // display 23 | m_color = other.m_color; 24 | m_engine = other.m_engine; 25 | 26 | m_f1.set(other.m_f1); 27 | m_f2.set(other.m_f2); 28 | m_center.set(other.m_center); 29 | m_u = other.m_u; 30 | m_e = other.m_e; 31 | m_a = other.m_a; 32 | m_b = other.m_b; 33 | m_f = other.m_f; 34 | m_w = other.m_w; 35 | m_energy = other.m_energy; 36 | m_apogee = other.m_apogee; 37 | m_perigee = other.m_perigee; 38 | m_orbitArea = other.m_orbitArea; 39 | m_bValid = other.m_bValid; 40 | 41 | if ( other.m_drawPoints == NULL ) 42 | { 43 | delete[] m_drawPoints; 44 | m_drawPoints = NULL; 45 | m_numDrawPoints = 0; 46 | } 47 | else 48 | { 49 | // deal with draw points 50 | m_numDrawPoints = other.m_numDrawPoints; 51 | delete[] m_drawPoints; 52 | m_drawPoints = new DrawPoint[m_numDrawPoints]; 53 | for ( int i=0 ; i>16; 64 | int g = (objectColor&0x00ff00)>>8; 65 | int b = (objectColor&0x0000ff); 66 | 67 | r/=4; 68 | g/=4; 69 | b/=4; 70 | 71 | m_color = (r<<16) + (g<<8) + b; 72 | } 73 | 74 | void Orbit::setColor(int color) 75 | { 76 | m_color = color; 77 | } 78 | 79 | void Orbit::init(double sgp, double e, double a, double w) 80 | { 81 | m_engine = (OBEngine *)FGEngine::getEngine(); 82 | 83 | m_bValid = true; 84 | if ( e > 1.0 ) 85 | { 86 | m_bValid = false; 87 | return; 88 | } 89 | if ( a <= 0.0 ) 90 | { 91 | m_bValid = false; 92 | return; 93 | } 94 | 95 | // start with what we know 96 | m_f1.setXY(0.0, 0.0); 97 | m_u = sgp; 98 | m_e = e; 99 | m_a = a; 100 | m_w = w; 101 | 102 | // work out what we don't know 103 | 104 | // e = f/a, so f = ea 105 | m_f = m_e*m_a; 106 | 107 | // f = sqrt(a^2 - b^2). 108 | // From that we can derive: b = sqrt (a^2 - f^2) 109 | m_b = sqrt(m_a*m_a - m_f*m_f); 110 | 111 | // the center is f distance from f1 along angle w 112 | m_center.setRTheta(m_f, w); 113 | 114 | // f2 is 2f distance from f1 along angle w 115 | m_f2.setRTheta(2.0*m_f, w); 116 | 117 | // the specific orbital energy is (by definition) -u/2a 118 | m_energy = -m_u/(2.0*m_a); 119 | 120 | // work our apogee and perigee. 121 | // apogee is just a+f. Perigee is just a-f 122 | m_apogee = m_a + m_f; 123 | m_perigee = m_a - m_f; 124 | 125 | // note the area 126 | m_orbitArea = PI*m_a*m_b; 127 | 128 | // precalculate the draw points 129 | calcDrawPoints(); 130 | } 131 | 132 | void Orbit::initPV(double sgp, FGDoubleVector &orbiterPos, FGDoubleVector &orbiterVel) 133 | { 134 | // note what we were given 135 | m_f1.setXY(0.0, 0.0); 136 | m_u = sgp; 137 | 138 | // note the orbiter position relative to F1. The vector will be 139 | // pointing FROM the orbiter TOWARD F1. So we're just reversing it. 140 | FGDoubleVector relativePos; 141 | relativePos.set(orbiterPos); 142 | relativePos.scalarMultiply(-1.0); 143 | 144 | // note the distance from F1 of the orbiter 145 | double r = relativePos.getLength(); 146 | if ( r <= 0.0 ) 147 | { 148 | m_bValid = false; 149 | return; 150 | } 151 | 152 | // note the length of the velocity vector 153 | double v = orbiterVel.getLength(); 154 | 155 | // note the specific orbital energy from the position and velocity of the orbiter 156 | // this is one of the definitions of specific orbital energy (it will make a negative 157 | // number) 158 | m_energy = (v*v/2.0) - (m_u/r); 159 | if ( m_energy >= 0.0 ) 160 | { 161 | // non-negative means an escape 162 | m_bValid = false; 163 | return; 164 | } 165 | 166 | // from the energy, we can calculate the semi-major axis length 167 | // (note the negation). 168 | m_a = -m_u/(2.0*m_energy); 169 | 170 | // work out the distance from the orbiter to F2 171 | double d = 2.0*m_a - r; 172 | 173 | // work out the angle toward F2 from the velocity vector. 174 | // This works out to be PI-Theta, where Theta is the angle 175 | // between the velocity vector and the line from the orbiter 176 | // to F1. 177 | double orbiterAngle = relativePos.getAngle(); 178 | double velAngle = orbiterVel.getAngle(); 179 | double theta = FGDoubleGeometry::angleDiff(velAngle, orbiterAngle); 180 | double phi = PI - theta; 181 | 182 | // F2 will be d units along that angle from the orbiter 183 | m_f2.set(orbiterVel); 184 | m_f2.setLength(d); 185 | m_f2.rotate(phi); 186 | m_f2.addVector(orbiterPos); 187 | 188 | // w will be the angle of F1F2 189 | // F1 is always 0,0 so it's just the angle to F2 190 | m_w = m_f2.getAngle(); 191 | 192 | // f will be half the distance from F1 to F2 193 | // f1 is always 0,0, so it's just half the length of f2 194 | m_f = m_f2.getLength()/2.0; 195 | 196 | // eccentricity is f/a 197 | m_e = m_f/m_a; 198 | 199 | // now we have e, a, and w. We can call the primary init 200 | init(sgp, m_e, m_a, m_w); 201 | } 202 | 203 | void Orbit::initAP(double sgp, FGDoubleVector &apogee, FGDoubleVector &perigee) 204 | { 205 | // we need eccentricity, semomajor axis, and angle. All are pretty easy to get 206 | // when you know the apogee, perigee, and a focus. 207 | 208 | // sanity check: The apogee and perigee have to be in line with the focus (0,0). 209 | // we'll just check the angles and their difference 210 | double angleDiff = FGDoubleGeometry::angleDiff(apogee.getAngle(), perigee.getAngle()); 211 | angleDiff = fabs(angleDiff); 212 | if ( !fnear(angleDiff, PI, 0.01) ) 213 | { 214 | // not close enough to opposition. 215 | m_bValid = false; 216 | return; 217 | } 218 | 219 | // pretty straight forward, and we'll need them in a minute. 220 | m_apogee = apogee.getLength(); 221 | m_perigee = perigee.getLength(); 222 | 223 | // the semimajor axis is just half the distance from perigee to apogee. 224 | m_a = (m_apogee + m_perigee)/2.0; 225 | 226 | // w is the angle from f1 to apogee. 227 | m_w = apogee.getAngle(); 228 | 229 | // and e is defined as (a-p)/(a+p) where a and p are apogee and perigee distances 230 | m_e = (m_apogee-m_perigee)/(m_apogee+m_perigee); 231 | 232 | // we now have the criticals 233 | init(sgp, m_e, m_a, m_w); 234 | } 235 | 236 | void Orbit::calcDrawPoints() 237 | { 238 | if ( !m_bValid ) return; 239 | 240 | // working vector 241 | FGDoubleVector work; 242 | 243 | // note how far to walk along the ellipse per step 244 | // we'll shoot for a number of pixels 245 | double xStep = 2.0*m_engine->m_kmPerPixel; // 2 pixels per step 246 | 247 | // prep the points array. 248 | int numPoints = int((2.0*m_a)/xStep) + 2; // the +2 is for rounding safety 249 | int maxDrawPoints = numPoints*2; 250 | 251 | // set up the drawpoints array (trashing any old one that may have been there) 252 | delete[] m_drawPoints; 253 | m_drawPoints = new DrawPoint[maxDrawPoints]; 254 | 255 | // working arrays 256 | DrawPoint *firstHalf = new DrawPoint[numPoints]; 257 | DrawPoint *secondHalf = new DrawPoint[numPoints]; 258 | int halfPos = 0; 259 | 260 | // work out the points to connect to draw the ellipse 261 | // The relevant thing here is the definition of an ellipse. 262 | // from this we can calculate that y = sqrt(b^2*(1-x^2/a^2)) 263 | // so we start x at perihelion, stroll along to aphelion, and 264 | // note the y values. This gives us half the ellipse 265 | 266 | // the total width of the ellipse will be the semi-major axis times 2 267 | double x = -m_a; 268 | while ( true ) 269 | { 270 | // calculate the y for this x 271 | double alpha = 1.0-(x*x)/(m_a*m_a); 272 | double y = sqrt(m_b*m_b*alpha); 273 | 274 | if ( halfPos >= numPoints ) 275 | { 276 | FGEngine::fatal("Draw point overflow"); 277 | } 278 | 279 | // make a vector 280 | work.setXY(x, y); 281 | 282 | // rotate by the orbital angle 283 | work.rotate(m_w); 284 | 285 | // the x,y values are deltas from the ellipse center 286 | // so we have to offset to the center of this ellipse 287 | work.addVector(m_center); 288 | 289 | // what we just worked out was the first half 290 | firstHalf[halfPos].m_viewX = m_engine->modelToViewX(work.m_fixX); 291 | firstHalf[halfPos].m_viewY = m_engine->modelToViewY(work.m_fixY); 292 | 293 | // now work out the other half's location. Same process as documented 294 | // above, but we negate the y value before translating and rotating it. 295 | work.setXY(x, -y); 296 | work.rotate(m_w); 297 | work.addVector(m_center); 298 | secondHalf[halfPos].m_viewX = m_engine->modelToViewX(work.m_fixX); 299 | secondHalf[halfPos].m_viewY = m_engine->modelToViewY(work.m_fixY); 300 | 301 | // advance the half pos 302 | halfPos++; 303 | 304 | if ( x == m_a ) 305 | { 306 | // finished the last point 307 | break; 308 | } 309 | 310 | // advance x 311 | x += xStep; 312 | if ( x > m_a ) 313 | { 314 | x = m_a; 315 | } 316 | } 317 | 318 | // assemble all the points in to a single sequence. 319 | // we run the first half in order, and the second half in reverse 320 | // so the line draws are always in one direction around the arc. 321 | m_numDrawPoints = 0; 322 | for ( int i=0 ; i= maxDrawPoints ) 325 | { 326 | FGEngine::fatal("draw point overflow"); 327 | } 328 | 329 | m_drawPoints[m_numDrawPoints] = firstHalf[i]; 330 | m_numDrawPoints++; 331 | } 332 | 333 | for ( int i=halfPos-1 ; i>=0 ; i-- ) 334 | { 335 | if ( m_numDrawPoints >= maxDrawPoints ) 336 | { 337 | FGEngine::fatal("draw point overflow"); 338 | } 339 | 340 | m_drawPoints[m_numDrawPoints] = secondHalf[i]; 341 | m_numDrawPoints++; 342 | } 343 | 344 | // done with the working arrays 345 | delete[] firstHalf; 346 | delete[] secondHalf; 347 | } 348 | 349 | void Orbit::drawSelf(FGGraphics &g) 350 | { 351 | if ( !m_bValid ) return; 352 | if ( m_drawPoints == NULL ) return; 353 | if ( m_numDrawPoints < 2 ) return; 354 | 355 | g.setColor(m_color); 356 | 357 | int lastX = m_drawPoints[0].m_viewX; 358 | int lastY = m_drawPoints[0].m_viewY; 359 | for ( int i=1 ; im_pointIdx = 0; 26 | newPoint->m_type = ACCTYPE_NORMAL; 27 | newPoint->m_angle = PI/2.0; 28 | newPoint->m_mag = PATH_ACCELERATION; 29 | 30 | calcPoints(); 31 | } 32 | 33 | void Path::initNoAcc(OBObject *orbitee, FGDoubleVector &pos, FGDoubleVector &vel, int color, int size) 34 | { 35 | // set up the basics 36 | m_engine = (OBEngine *)FGEngine::getEngine(); 37 | m_orbitee = orbitee; 38 | m_startPos.set(pos); 39 | m_startVel.set(vel); 40 | m_color = color; 41 | m_size = size; 42 | 43 | calcPoints(); 44 | } 45 | 46 | void Path::initNoAcc(OBObject *orbiter, double angle) 47 | { 48 | // convert from J2000 49 | angle = -PI/2.0-angle; 50 | 51 | // basics, harvestable from the orbiter 52 | OBObject *orbitee = orbiter->m_orbitee; 53 | int color = orbiter->m_orbit.m_color; 54 | int size = orbiter->m_color; 55 | 56 | // work out the position and velocity from the angle 57 | FGDoubleVector pos; 58 | orbiter->m_orbit.getPos(angle, pos); 59 | FGDoubleVector vel; 60 | orbiter->m_orbit.getVel(angle, vel); 61 | 62 | // init with these values 63 | initNoAcc(orbitee, pos, vel, color, size); 64 | } 65 | 66 | void Path::save(FGDataWriter &out) 67 | { 68 | // start and end points 69 | m_startPos.writeOut(&out); 70 | m_startVel.writeOut(&out); 71 | 72 | // number of points 73 | out.writeInt(m_accelerationPoints.size()); 74 | 75 | // point info 76 | for ( AccelerationPointIter iter = m_accelerationPoints.begin() ; iter != m_accelerationPoints.end() ; iter++ ) 77 | { 78 | // if we have *passed* the current idx, then we know our location 79 | AccelerationPoint *ap= *iter; 80 | out.writeInt(ap->m_pointIdx); 81 | out.writeInt(ap->m_type); 82 | out.writeDouble(ap->m_angle); 83 | out.writeDouble(ap->m_mag); 84 | } 85 | } 86 | 87 | void Path::load(FGDataReader &in) 88 | { 89 | // start and end points 90 | m_startPos.readIn(&in); 91 | m_startVel.readIn(&in); 92 | 93 | // clear the old points 94 | for ( AccelerationPointIter iter = m_accelerationPoints.begin() ; iter != m_accelerationPoints.end() ; iter++ ) 95 | { 96 | delete *iter; 97 | } 98 | m_accelerationPoints.clear(); 99 | 100 | // number of points 101 | int count = in.readInt(); 102 | 103 | // point info 104 | for ( int i=0 ; im_pointIdx = in.readInt(); 108 | ap->m_type = in.readInt(); 109 | ap->m_angle = in.readDouble(); 110 | ap->m_mag = in.readDouble(); 111 | m_accelerationPoints.push_back(ap); 112 | } 113 | 114 | calcPoints(); 115 | } 116 | 117 | void Path::removeAccelerationPoint(AccelerationPoint *ap) 118 | { 119 | // you can not remove the initial acceleration point 120 | if ( ap->m_pointIdx == 0 ) return; 121 | 122 | m_accelerationPoints.remove(ap); 123 | delete ap; 124 | } 125 | 126 | void Path::adjustAccelerationPoint(AccelerationPoint *ap, int mx, int my, double newMag) 127 | { 128 | // work out the view x,y for this acceleration point 129 | int pointIdx = ap->m_pointIdx; 130 | int apX = m_engine->modelToViewX(m_points[pointIdx].m_fixX); 131 | int apY = m_engine->modelToViewY(m_points[pointIdx].m_fixY); 132 | 133 | // make a vector that goes from the ap to mx, my 134 | FGDoubleVector newLine; 135 | // newLine.setXY(apX-mx, apY-my); 136 | newLine.setXY(mx-apX, my-apY); 137 | 138 | // note that angle 139 | double unadjustedAng = newLine.getAngle(); 140 | 141 | // get the angle from that point to the orbitee 142 | FGDoubleVector gravDir; 143 | getGravForPoint(pointIdx, gravDir); 144 | double gravAng = gravDir.getAngle(); 145 | 146 | // set the specifics 147 | ap->m_angle = FGDoubleGeometry::angleDiff(gravAng, unadjustedAng); 148 | ap->m_mag = newMag; 149 | } 150 | 151 | void Path::getGravForPoint(int pointIdx, FGDoubleVector &result) 152 | { 153 | result.set(m_orbitee->m_pos); 154 | 155 | // we can't trust the location of the current point. We need to 156 | // calculate from the location of the previous point. 157 | if ( pointIdx == 0 ) 158 | { 159 | result.subtractVector(m_startPos); 160 | } 161 | else 162 | { 163 | result.subtractVector(m_points[pointIdx-1]); 164 | } 165 | } 166 | 167 | AccelerationPoint *Path::createAccelerationPoint(int pointIdx) 168 | { 169 | // if the acceleration point is out of range, we fail 170 | if ( pointIdx < 0 ) return NULL; 171 | if ( pointIdx >= PATH_NUM_POINTS ) return NULL; 172 | 173 | // find the iterator *after* the point we want to insert 174 | AccelerationPointIter insertIter; 175 | for ( insertIter = m_accelerationPoints.begin() ; insertIter != m_accelerationPoints.end() ; insertIter++ ) 176 | { 177 | AccelerationPoint *ap= *insertIter; 178 | if ( ap->m_pointIdx == pointIdx ) 179 | { 180 | // it's already there. 181 | return ap; 182 | } 183 | 184 | if ( ap->m_pointIdx > pointIdx ) 185 | { 186 | // this is the place we'll be inserting 187 | break; 188 | } 189 | } 190 | 191 | // note, even a value of end() for insertIter is valid. 192 | AccelerationPoint *newPoint = new AccelerationPoint(); 193 | newPoint->m_pointIdx = pointIdx; 194 | 195 | // set it to what that point was, but only if we have some basis for setting it 196 | if ( m_accelerationPoints.size() > 0 ) 197 | { 198 | // set it up to be the current value for that point 199 | // note the apparent thrust for that location. 200 | FGDoubleVector thrust; 201 | getThrustForPoint(pointIdx, thrust); 202 | 203 | // note the magnitude of thrust 204 | newPoint->m_mag = thrust.getLength(); 205 | 206 | // work out the angle to the orbitee at that point, using the same 207 | // method the thrust method uses. 208 | FGDoubleVector gravDir; 209 | getGravForPoint(pointIdx, gravDir); 210 | 211 | // note the angle difference 212 | newPoint->m_angle = FGDoubleGeometry::angleDiff(gravDir.getAngle(), thrust.getAngle()); 213 | } 214 | 215 | // presume a normal point 216 | newPoint->m_type = ACCTYPE_NORMAL; 217 | 218 | // ready to turn it loose. 219 | m_accelerationPoints.insert(insertIter, newPoint); 220 | return newPoint; 221 | } 222 | 223 | void Path::drawProgressivePath(FGGraphics &g, int pointIdx) 224 | { 225 | // find the first stop point 226 | int stopIdx = getStopPoint(); 227 | if ( stopIdx > pointIdx ) 228 | { 229 | stopIdx = pointIdx; 230 | } 231 | 232 | // run through the points and draw the path 233 | int lastX; 234 | int lastY; 235 | g.setColor(m_color); 236 | for ( int i=0 ; i<=stopIdx ; i++ ) 237 | { 238 | int x = m_engine->modelToViewX(m_points[i].m_fixX); 239 | int y = m_engine->modelToViewY(m_points[i].m_fixY); 240 | 241 | bool bDraw = false; 242 | if ( i != 0 ) 243 | { 244 | // if we aren't thrusting, don't draw this segment 245 | // but DO draw it if we're past day 170 246 | FGDoubleVector thrust; 247 | getThrustForPoint(i, thrust); 248 | if ( thrust.getLength() != 0.0 ) 249 | { 250 | bDraw = true; 251 | } 252 | 253 | if ( i > 170 ) bDraw = true; 254 | } 255 | 256 | if ( bDraw ) 257 | { 258 | g.drawLine(lastX, lastY, x, y); 259 | } 260 | 261 | lastX = x; 262 | lastY = y; 263 | } 264 | } 265 | 266 | void Path::drawSelf(FGGraphics &g, AccelerationPoint *sel) 267 | { 268 | // find the first stop point 269 | int stopIdx = getStopPoint(); 270 | 271 | // run through the points and draw the path 272 | int lastX; 273 | int lastY; 274 | g.setColor(m_color); 275 | for ( int i=0 ; i<=stopIdx ; i++ ) 276 | { 277 | int x = m_engine->modelToViewX(m_points[i].m_fixX); 278 | int y = m_engine->modelToViewY(m_points[i].m_fixY); 279 | 280 | if ( i != 0 ) 281 | { 282 | // draw 283 | g.drawLine(lastX, lastY, x, y); 284 | } 285 | 286 | lastX = x; 287 | lastY = y; 288 | } 289 | 290 | // run through the acceleration points and draw them 291 | for ( AccelerationPointIter iter = m_accelerationPoints.begin() ; iter != m_accelerationPoints.end() ; iter++ ) 292 | { 293 | // if we have *passed* the current idx, then we know our location 294 | AccelerationPoint *ap= *iter; 295 | 296 | // draw a box around the point 297 | int pointIdx = ap->m_pointIdx; 298 | int x = m_engine->modelToViewX(m_points[pointIdx].m_fixX); 299 | int y = m_engine->modelToViewY(m_points[pointIdx].m_fixY); 300 | 301 | // draw the box 302 | if ( ap == sel ) 303 | { 304 | g.setColor(0xffff00); 305 | } 306 | else 307 | { 308 | if ( ap->m_type == ACCTYPE_REDIRECT ) 309 | { 310 | g.setColor(0x0000ff); 311 | } 312 | else 313 | { 314 | g.setColor(0xff0000); 315 | } 316 | } 317 | g.fillRect(x-m_size/2, y-m_size/2, m_size, m_size); 318 | 319 | // draw the acceleration line 320 | drawThrustLine(g, pointIdx); 321 | 322 | // if this was a stopper, we stop 323 | if ( ap->m_type == ACCTYPE_STOPTRACE ) break; 324 | } 325 | } 326 | 327 | int Path::getStopPoint() 328 | { 329 | int highestIdx = 0; 330 | for ( AccelerationPointIter iter = m_accelerationPoints.begin() ; iter != m_accelerationPoints.end() ; iter++ ) 331 | { 332 | // if we have *passed* the current idx, then we know our location 333 | AccelerationPoint *ap= *iter; 334 | if ( ap->m_type == ACCTYPE_STOPTRACE ) 335 | { 336 | return ap->m_pointIdx; 337 | } 338 | } 339 | return PATH_NUM_POINTS-1; 340 | } 341 | 342 | void Path::drawThrustLine(FGGraphics &g, int pointIDX) 343 | { 344 | if ( pointIDX == -1 ) return; 345 | 346 | // show the thrust vector at this point 347 | FGDoubleVector thrust; 348 | getThrustForPoint(pointIDX, thrust); 349 | 350 | if ( thrust.getLength() == 0.0 ) return; 351 | 352 | thrust.setLength(DISPLAY_THRUSTLINE_LENGTH); 353 | int x1 = m_engine->modelToViewX(m_points[pointIDX].m_fixX); 354 | int y1 = m_engine->modelToViewY(m_points[pointIDX].m_fixY); 355 | int x2 = x1 + (int)thrust.m_fixX; 356 | int y2 = y1 + (int)thrust.m_fixY; 357 | 358 | g.drawLine(x1, y1, x2, y2); 359 | } 360 | 361 | AccelerationPoint *Path::getNearestAccelPoint(int viewX, int viewY) 362 | { 363 | // see what point idx is closest to this point 364 | int MAX_DIST = DISPLAY_THRUSTLINE_LENGTH + DISPLAY_THRUSTLINE_LENGTH/10; 365 | int MAX_DIST_SQ = MAX_DIST*MAX_DIST; 366 | 367 | AccelerationPoint *closestAP = NULL; 368 | int closestDistSq = 0; 369 | 370 | for ( AccelerationPointIter iter = m_accelerationPoints.begin() ; iter != m_accelerationPoints.end() ; iter++ ) 371 | { 372 | AccelerationPoint *ap= *iter; 373 | int accelPointIdx = ap->m_pointIdx; 374 | 375 | int dx = viewX - m_engine->modelToViewX(m_points[accelPointIdx].m_fixX); 376 | int dy = viewY - m_engine->modelToViewY(m_points[accelPointIdx].m_fixY); 377 | int distSq = dx*dx + dy*dy; 378 | if ( distSq < MAX_DIST_SQ ) 379 | { 380 | if ( (closestAP == NULL) || (distSq < closestDistSq) ) 381 | { 382 | closestDistSq = distSq; 383 | closestAP = ap; 384 | } 385 | } 386 | 387 | // if this was a stopper, we stop 388 | if ( ap->m_type == ACCTYPE_STOPTRACE ) break; 389 | } 390 | 391 | return closestAP; 392 | } 393 | 394 | int Path::getNearestPointIdx(int viewX, int viewY) 395 | { 396 | // see what point idx is closest to this point 397 | int MAX_DIST = DISPLAY_THRUSTLINE_LENGTH + DISPLAY_THRUSTLINE_LENGTH/10; 398 | int MAX_DIST_SQ = MAX_DIST*MAX_DIST; 399 | 400 | int stopIdx = getStopPoint(); 401 | int closestIdx = -1; 402 | int closestDistSq = 0; 403 | 404 | for ( int i=0 ; i<=stopIdx ; i++ ) 405 | { 406 | int dx = viewX - m_engine->modelToViewX(m_points[i].m_fixX); 407 | int dy = viewY - m_engine->modelToViewY(m_points[i].m_fixY); 408 | int distSq = dx*dx + dy*dy; 409 | if ( distSq < MAX_DIST_SQ ) 410 | { 411 | if ( (closestIdx == -1) || (distSq < closestDistSq) ) 412 | { 413 | closestDistSq = distSq; 414 | closestIdx = i; 415 | } 416 | } 417 | } 418 | 419 | return closestIdx; 420 | } 421 | 422 | void Path::getThrustForPoint(int pointIdx, FGDoubleVector &result) 423 | { 424 | if ( m_accelerationPoints.size() == 0 ) 425 | { 426 | // no points at all 427 | result.setXY(0,0); 428 | return; 429 | } 430 | 431 | // note the vector to the orbitee. 432 | FGDoubleVector gravDir; 433 | getGravForPoint(pointIdx, gravDir); 434 | 435 | // find out which acceleration point we're affected by 436 | AccelerationPoint *ap = NULL; 437 | for ( AccelerationPointIter iter = m_accelerationPoints.begin() ; iter != m_accelerationPoints.end() ; iter++ ) 438 | { 439 | // if we have *passed* the current idx, then we know our location 440 | AccelerationPoint *thisAP = *iter; 441 | if ( thisAP->m_pointIdx > pointIdx ) 442 | { 443 | // this is our guy 444 | iter--; 445 | ap = *iter; 446 | break; 447 | } 448 | } 449 | 450 | if ( ap == NULL ) 451 | { 452 | // we are on the last acceleration point 453 | AccelerationPointIter iter = m_accelerationPoints.end(); 454 | iter--; 455 | ap = *iter; 456 | } 457 | 458 | // create a relative acceleration vector based on the deflection angle and magnitude 459 | FGDoubleVector acc; 460 | acc.set(gravDir); 461 | acc.rotate(ap->m_angle); 462 | acc.setLength(ap->m_mag); 463 | 464 | // done 465 | result.set(acc); 466 | } 467 | 468 | void Path::calcPoints() 469 | { 470 | // note the vel and pos. 471 | FGDoubleVector pos; 472 | FGDoubleVector vel; 473 | pos.set(m_startPos); 474 | vel.set(m_startVel); 475 | 476 | // start off at the start pos 477 | int stopIdx = getStopPoint(); 478 | m_points[0].set(pos); 479 | 480 | bool bHalt = false; 481 | 482 | for ( int i=1 ; i<=stopIdx ; i++ ) 483 | { 484 | if ( bHalt ) 485 | { 486 | m_points[i].set(m_points[i-1]); 487 | continue; 488 | } 489 | 490 | // first, accelerate based on the thing we're orbiting 491 | // start with a vector pointed at the orbitee 492 | FGDoubleVector gravAcc; 493 | gravAcc.set(m_orbitee->m_pos); 494 | gravAcc.subtractVector(pos); 495 | 496 | // work out the strength of the acceleration. 497 | // It'll be u/d^2 where u = SGP, and d = distance to the object 498 | // this is a good time to include the seconds value 499 | double gravAccLen = (POINTS_TIME * m_orbitee->m_sgp)/gravAcc.getLengthSq(); 500 | 501 | // set the new length 502 | gravAcc.setLength(gravAccLen); 503 | 504 | // apply the acceleration 505 | vel.addVector(gravAcc); 506 | 507 | FGDoubleVector thrustAcc; 508 | getThrustForPoint(i, thrustAcc); 509 | 510 | // we now know the acceleration vector. Apply it to the vel 511 | // first we'll have to multiply by the number of seconds in a step 512 | thrustAcc.scalarMultiply(POINTS_TIME); 513 | vel.addVector(thrustAcc); 514 | 515 | // run through the acceleration points to see if this point is a redirect 516 | for ( AccelerationPointIter iter = m_accelerationPoints.begin() ; iter != m_accelerationPoints.end() ; iter++ ) 517 | { 518 | // if we have *passed* the current idx, then we know our location 519 | AccelerationPoint *ap = *iter; 520 | if ( ap->m_pointIdx != i ) continue; 521 | 522 | // the current point 523 | if ( ap->m_type == ACCTYPE_REDIRECT ) 524 | { 525 | // it's a redirect. Change the angle of the velocity 526 | // vector to the angle of the thrust vector 527 | vel.setRTheta(vel.getLength(), thrustAcc.getAngle()); 528 | } 529 | break; 530 | } 531 | 532 | // now apply the vel to the pos. But we don't want to ruin the value we have 533 | // for vel, so we use an intermediate vector for the mult 534 | FGDoubleVector work; 535 | work.set(vel); 536 | work.scalarMultiply(POINTS_TIME); 537 | pos.addVector(work); 538 | 539 | // note the point 540 | m_points[i].set(pos); 541 | 542 | // if the pos gets closer than a certain distance from the sun, we just stop 543 | if ( FGDoubleGeometry::getDistance(m_points[i], m_orbitee->m_pos) < FATAL_SUN_APPROACH ) 544 | { 545 | bHalt = true; 546 | } 547 | } 548 | } 549 | 550 | --------------------------------------------------------------------------------