Jump to content

KSP Math for distance is off?


Recommended Posts

7 minutes ago, fulgur said:

So how do you work out the error? Am I doing it wrong? Because an error of 200m every 25000m is an error of 0.008, which is more than 0.00000001 or whatever your figure was.

the error factor given is in degrees, not distance

The error in distance will depend on how many meters is in a degree at the given altitude

Link to comment
Share on other sites

@DoctorDavinci, measuring distance in degrees is worth pointing out in a comment but otherwise fine.  Nautical miles are defined as a minutes of latitude at the mean sphere fitting sea level on Earth --- but a minute of longitude is usually a smaller distance than a degree of latitude, and usually less than a nautical mile. (Maybe we take you too literally with 'distance'.  In any case, I would expect Squads functions to give a different answer for 'distance' than your math.)

That is, we are surprised not to see the cosine of the latitude in the math you chose.
If you spawn at 60°N 31°W when 'current' was 60°N 29°W,  your math gives 1° degree distance, but that is only 30 nautical miles. 
On the equator, spawning 0°N 31°W from 0°N 30°W is also 1°, but a full 60 nautical miles.

It looks like you use this math to check if you have moved more than 10°.  That math might be fine for your needs (except for a typo, v.Current.latitude - _spawnLoc.z).  Very near the poles, of course, your checking region is very narrow east-to-west, while still being full size in altitude and north-to-south.

Link to comment
Share on other sites

11 hours ago, fulgur said:

It is quite a major difference if it does happen, because when doing interplanetary transfers, it could cause you to completely lose the intercept.

I suspect many players can attest to plotting a node for intercept only to find they've missed on arrival if they have not been monitoring it en route. This could potentially have some explanation for why that occurs. Squad's math isn't necessarily wrong though, rounding can cause such errors even with the "right" math.

Link to comment
Share on other sites

14 hours ago, DoctorDavinci said:

Here's my code ... Tell me I'm wrong

Sorry I'm a bit late to the party, but... looks to me like you're wrong.  ;)

As far as I can tell, it looks as though you're trying to use the Pythagorean theorem to calculate the distance between two things, using latitude and longitude as "coordinates".

13 hours ago, DoctorDavinci said:

I then calculate the hypotenuse using the following  (Pythagoras perhaps?)

                        double diffSqr = (_latDiff * _latDiff) + (_lonDiff * _lonDiff);

That doesn't work.  The Pythagorean theorem only works in linear Cartesian space, not in a polar coordinate system.  If you try to use it with latitude / longitude, you'll get wrong answers.  It doesn't work over long distances, because the curvature of the sphere becomes a bigger and bigger error factor.  It doesn't even work as a first-order approximation for small distances, if you're away from the equator, because a degree of longitude isn't the same size as a degree of latitude.  (Easy way to see this:  picture two points that are one meter apart, on opposite sides of the north pole.  Longitude distance is 180 degrees.  It'll tell you they're "far apart" when they're not.)

It would work as an approximation for very short distances if you're on the equator, but that's the only situation where it would.

"Hypotenuse" isn't a thing on the surface of a sphere.  It's only a thing in linear coordinates.

14 hours ago, Hotel26 said:

Furthermore, I see you are using trig for long/lat meaning you are computing circumferences (great circle) but then you appear to be using Pythag (line of sight/Cartesian) mixed in.

^This.

10 hours ago, cineboxandrew said:

It doesn’t work like that, the Pythagorean theorem works on linear directions in cartesian space, not angles in spherical space. If you start at the equator and go 90° east and 90° north, you’re 90° from your starting point, not 127°

^ This.  This is it, exactly.

 

10 hours ago, DoctorDavinci said:

Please, show me where I am wrong ... I beg you

If you still don't believe this, here's a very simple test case for you to try your math on.

Spoiler

Let's say you have two points, A and B, and you want to calculate how far apart they are.  To simplify the math, assume both are at zero altitude, i.e. each one is exactly 600 km from Kerbin's center.  In both cases, they're almost exactly two meters apart, as follows:

Case 1:

  • Both are at the same latitude, exactly 1 meter great-circle distance from the north pole.  On Kerbin, I believe that would put the latitude at 89.99990451 degrees (if I've punched the numbers into my calculator here correctly);)
  • One's at longitude 0, the other's at longitude 180 (i.e. they're on opposite sides of the north pole, each 1 meter from it).
  • In other words, delta latitude is zero, delta longitude is 180 degrees.

Case 2:

  • Both are at the same latitude, exactly on the equator.
  • One is at longitude 0.  The other is two meters due east from it, at longitude 0.000191 degrees east (again, if I haven't missed a decimal point somewhere) ;)

 

So... two cases.  Both of them have two points that are physically about two meters apart.  Both of them have a delta-latitude of zero.  In one case, you're at the equator; in the other, you're straddling the north pole.  It's clear that the distance between the two points should be the same in both cases.

So, that gives an easy test case for your code, doesn't it?  Try plugging those two situations into your code; it should give the same answer (close to 2 meters) in both cases.  If you get different answers, then your code is wrong.

So... what happens if you run these two test cases?

 

 

Anyway, enough about what's wrong.  What does "right" look like?

Detailed explanation of correct solution is below, but it boils down to this:

  1. First convert your two points to Cartesian XYZ coordinates
  2. Then apply the three-dimensional Pythagorean theorem, sqrt(dx2 + dy2 + dz2)

 

I'll start with the simple case and then work up to the case I think you're trying to solve.

To actually find the straight-line distance between two points in 3D space, if you have Cartesian coordinates to begin with, it's simply the Pythagorean theorem:

// suppose your two ships are at x1,y1,z1 and x2,y2,z2

double dX = x1 - x2;
double dY = y1 - y2;
double dZ = z1 - z2;

double distance = Math.sqrt(dX*dX + dY*dY + dZ*dZ);

 

That much is simple enough.  But that's assuming you have the positions in Cartesian XYZ coordinates.  What if they're given as spherical polar coordinates, instead? Well, in that case, you need to convert from polar to Cartesian, then apply the Pythagorean solution given above.

// suppose your two ships are at latRad1,lonRad1,r1 and latRad2,lonRad2,r2
// where latitude and longitude are in RADIANS
// and radius r is in distance from center of planet

double cylindricalR1 = r1 * Math.cos(latRad1);
double cylindricalR2 = r2 * Math.cos(latRad2);

double x1 = cylindricalR1 * Math.cos(lonRad1);
double x2 = cylindricalR2 * Math.cos(lonRad2);

double y1 = cylindricalR1 * Math.sin(lonRad1);
double y2 = cylindricalR2 * Math.sin(lonRad2);

double z1 = r1 * Math.sin(latRad1);
double z2 = r2 * Math.sin(latRad2);

// we now have their positions x1,y1,z1 and x2,y2,z2 in Cartesian space,
// so we can plug those numbers into the Pythagorean code from the
// previous example

 

Now, this second example assumes that your latitude/longitude are conveniently in radians, and that you conveniently have the radius as distance-from-center-of-planet.

If what you actually have are latitude/longitude in degrees, and an altitude above sea level, then you'd need to do some conversion first:

// let's say you have *degree* coords latDeg1,lonDeg1 and latDeg2,lonDeg2
// and altitude above sea level (in meters) alt1 and alt2
// and that the radius of the planet is rPlanet

double latRad1 = latDeg1 * Math.PI / 180.0;
double latRad2 = latDeg2 * Math.PI / 180.0;
double lonRad1 = lonDeg1 * Math.PI / 180.0;
double lonRad2 = lonDeg2 * Math.PI / 180.0;

double r1 = rPlanet + alt1;
double r2 = rPlanet + alt2;

// we now have spherical polar coordinates suitable for plugging
// into the polar-to-Cartesian code in the above example

 

So that ought to give you the correct answer.  Per the above three examples,

  1. Take your lat/lon in degrees and altitude in meters, convert them to lat/lon in radians and radius from center of planet
  2. Take those numbers, and use trigonometry to convert to cartesian XYZ coordinates
  3. Take those numbers and use the Pythagorean theorem to find the linear distance between the two points.

Does this help?

Link to comment
Share on other sites

@Snark would you be so kind as to move this thread to the C# Development Help and Support section ... I have a few questions regarding the targeting distance deal and would like to pick your brain a bit (as well as anyone else who would like to chime in)

In the spoiler below is the main targeting code I am using in OrX Kontinuum ... The example I posted at the beginning of the thread is only for changing the color of the distance display and preventing a stage gate from being placed if it is too far from the challenge starting coordinates (the Short Track Racing challenge creator has a maximum radius of 4km for placing gates)

Spoiler

                    if (FlightGlobals.ActiveVessel.altitude <= _altMission)
                    {
                        _altDiff = _altMission - FlightGlobals.ActiveVessel.altitude;
                    }
                    else
                    {
                        _altDiff = FlightGlobals.ActiveVessel.altitude - _altMission;
                    }

                    if (_latMission >= 0)
                    {
                        if (FlightGlobals.ActiveVessel.latitude >= _latMission)
                        {
                            _latDiff = FlightGlobals.ActiveVessel.latitude - _latMission;
                        }
                        else
                        {
                            _latDiff = _latMission - FlightGlobals.ActiveVessel.latitude;
                        }
                    }
                    else
                    {
                        if (FlightGlobals.ActiveVessel.latitude >= 0)
                        {
                            _latDiff = FlightGlobals.ActiveVessel.latitude - _latMission;
                        }
                        else
                        {
                            if (FlightGlobals.ActiveVessel.latitude <= _latMission)
                            {
                                _latDiff = FlightGlobals.ActiveVessel.latitude - _latMission;
                            }
                            else
                            {

                                _latDiff = _latMission - FlightGlobals.ActiveVessel.latitude;
                            }
                        }
                    }

                    if (_lonMission >= 0)
                    {
                        if (FlightGlobals.ActiveVessel.longitude >= _lonMission)
                        {
                            _lonDiff = FlightGlobals.ActiveVessel.longitude - _lonMission;
                        }
                        else
                        {
                            _lonDiff = _lonMission - FlightGlobals.ActiveVessel.latitude;
                        }
                    }
                    else
                    {
                        if (FlightGlobals.ActiveVessel.longitude >= 0)
                        {
                            _lonDiff = FlightGlobals.ActiveVessel.longitude - _lonMission;
                        }
                        else
                        {
                            if (FlightGlobals.ActiveVessel.longitude <= _lonMission)
                            {
                                _lonDiff = FlightGlobals.ActiveVessel.longitude - _lonMission;
                            }
                            else
                            {

                                _lonDiff = _lonMission - FlightGlobals.ActiveVessel.longitude;
                            }
                        }
                    }

                    double diffSqr = (_latDiff * _latDiff) + (_lonDiff * _lonDiff);
                    double _altDiffDeg = _altDiff * degPerMeter;
                    double altAdded = (_altDiffDeg * _altDiffDeg) + diffSqr;
                    double _targetDistance = Math.Sqrt(altAdded) * mPerDegree;

                    targetDistance = _targetDistance;
                    OrXHoloKron.instance.targetDistance = _targetDistance;

                    if (OrXHoloKron.instance.challengeRunning)
                    {
                        if (FlightGlobals.ActiveVessel.srfSpeed >= OrXHoloKron.instance.topSurfaceSpeed)
                        {
                            OrXHoloKron.instance.topSurfaceSpeed = FlightGlobals.ActiveVessel.srfSpeed;
                        }

                        if (!FlightGlobals.ActiveVessel.LandedOrSplashed)
                        {
                            OrXHoloKron.instance.airTime += Time.fixedDeltaTime;
                        }
                    }

                    if (_targetDistance <= 20000)
                    {
                        if (checking)
                        {
                            if (OrXHoloKron.instance.bdaChallenge)
                            {
                                if (OrXVesselLog.instance._playerCraft.Contains(FlightGlobals.ActiveVessel))
                                {
                                    Vector3d stageStartCoords = OrXSpawnHoloKron.instance.WorldPositionToGeoCoords(new Vector3d(_latMission, _lonMission, _altMission), FlightGlobals.currentMainBody);

                                    OrXHoloKron.instance.checking = false;
                                    OrXLog.instance.DebugLog("[OrX Target Distance - Goal] === TARGET Name: " + HoloKronName);
                                    OrXLog.instance.DebugLog("[OrX Target Distance - Goal] === TARGET Distance in Meters: " + _targetDistance);
                                    checking = false;
                                    CheckIfHoloSpawned(HoloKronName, stageStartCoords, missionCoords, primary, Goal);
                                }
                            }
                            else
                            {
                                if (_continue)
                                {
                                    if (_targetDistance <= 4000)
                                    {
                                        _continue = false;
                                        Vector3d stageStartCoords = OrXSpawnHoloKron.instance.WorldPositionToGeoCoords(new Vector3d(_latMission, _lonMission, _altMission), FlightGlobals.currentMainBody);
                                        OrXLog.instance.DebugLog("[OrX HoloKron Distance] === " + HoloKronName + " Distance in Meters: " + _targetDistance);
                                        CheckIfHoloSpawned(HoloKronName, stageStartCoords, missionCoords, primary, Goal);
                                    }
                                }
                                else
                                {
                                }
                            }
                        }
                        else
                        {
                            if (_targetDistance <= 50 && OrXHoloKron.instance.checking)
                            {
                                OrXHoloKron.instance.checking = false;

                                if (!OrXHoloKron.instance.bdaChallenge)
                                {
                                    OrXHoloKron.instance.OrXHCGUIEnabled = false;
                                    OrXHoloKron.instance.MainMenu();
                                }
                            }
                        }
                    }
                    else
                    {
                        if (OrXHoloKron.instance.bdaChallenge && !_airsupportSpawned)
                        {
                            try
                            {
                                bool _continue = false;

                                List<Vessel>.Enumerator _playerVessels = OrXVesselLog.instance._playerCraft.GetEnumerator();
                                while (_playerVessels.MoveNext())
                                {
                                    if (_playerVessels.Current != null)
                                    {
                                        if (_targetDistance <= 60000)
                                        {
                                            if (_playerVessels.Current.altitude >= _playerVessels.Current.radarAltitude)
                                            {
                                                if (_playerVessels.Current.radarAltitude >= _targetDistance / 100)
                                                {
                                                    _randomSpawned = true;
                                                    _continue = true;
                                                }
                                            }
                                            else
                                            {
                                                if (_playerVessels.Current.altitude >= _targetDistance / 100)
                                                {
                                                    _randomSpawned = true;
                                                    _continue = true;
                                                }

                                            }
                                        }
                                        else
                                        {
                                            if (OrXSpawnHoloKron.instance._interceptorCount != 0 && !_randomSpawned)
                                            {
                                                if (_targetDistance <= 100000)
                                                {
                                                    double spawnChance;

                                                    if (_playerVessels.Current.altitude >= _playerVessels.Current.radarAltitude)
                                                    {
                                                        spawnChance = (((100000 - _targetDistance) / 10000) * OrXSpawnHoloKron.instance._interceptorCount) * 500;
                                                        if (_playerVessels.Current.radarAltitude <= spawnChance)
                                                        {
                                                            _continue = true;
                                                        }
                                                    }
                                                    else
                                                    {
                                                        spawnChance = (((100000 - _targetDistance) / 10000) * OrXSpawnHoloKron.instance._interceptorCount) * 500;
                                                        if (_playerVessels.Current.altitude <= spawnChance)
                                                        {
                                                            _continue = true;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                _playerVessels.Dispose();

                                if (_continue)
                                {
                                    if (!_randomSpawned)
                                    {
                                        _randomSpawned = true;
                                        OrXLog.instance.DebugLog("[OrX Target Distance - Spawn Random Air Support] === TARGET Distance in Meters: " + _targetDistance);
                                        _wmActivateDelay = (100000 - ((float)_targetDistance)) * 2;
                                        OrXSpawnHoloKron.instance.SpawnRandomAirSupport(_wmActivateDelay);
                                    }
                                    else
                                    {
                                        OrXLog.instance.DebugLog("[OrX Target Distance - Spawn Air Support] === TARGET Distance in Meters: " + _targetDistance);
                                        _airsupportSpawned = true;
                                        _wmActivateDelay = 60000 - ((float)_targetDistance);
                                        OrXSpawnHoloKron.instance.SpawnAirSupport(false, true, HoloKronName, new Vector3d(), _wmActivateDelay);
                                    }
                                }
                            }
                            catch { }
                        }
                    }

 

So what you were saying above, just so that I understand, is I need to take the active vessel coordinates (lat, lon, alt) as well as the mission coordinates and convert them from the coordinates as reported by the vessel (FlightGlobals.ActiveVessel.latitude for example) and convert them using this (I have the altitude plus radius of the celestial body already):

double latRad1 = latDeg1 * Math.PI / 180.0;
double latRad2 = latDeg2 * Math.PI / 180.0;
double lonRad1 = lonDeg1 * Math.PI / 180.0;
double lonRad2 = lonDeg2 * Math.PI / 180.0;

And then do this:

double cylindricalR1 = r1 * Math.cos(latRad1);
double cylindricalR2 = r2 * Math.cos(latRad2);

double x1 = cylindricalR1 * Math.cos(lonRad1);
double x2 = cylindricalR2 * Math.cos(lonRad2);

double y1 = cylindricalR1 * Math.sin(lonRad1);
double y2 = cylindricalR2 * Math.sin(lonRad2);

double z1 = r1 * Math.sin(latRad1);
double z2 = r2 * Math.sin(latRad2);

and then I do the math I am doing in my code above ... Swapping the mission coordinates and active vessel coordinates with the converted coordinates?

Edited by DoctorDavinci
Link to comment
Share on other sites

15 hours ago, DoctorDavinci said:

In the spoiler below is the main targeting code I am using in OrX Kontinuum ... The example I posted at the beginning of the thread is only for changing the color of the distance display and preventing a stage gate from being placed if it is too far from the challenge starting coordinates (the Short Track Racing challenge creator has a maximum radius of 4km for placing gates)

Thanks, I appreciate the full context... but I find the code pretty hard to follow (there's a lot of stuff in there, with lots and lots of nested clauses in one big monolithic block, and no code comments).  Fair 'nuff if that's your comfort zone :) ... just makes it a bit harder for others to follow (well, at least my tired old brain, anyway), particularly if the reader doesn't have the broader context of the overall design of the mod.

So I haven't really read your code in detail, other than glancing briefly at it.  The following is based on my understanding of what you're trying to do, based on what you've said here.

15 hours ago, DoctorDavinci said:

So what you were saying above, just so that I understand, is I need to take the active vessel coordinates (lat, lon, alt) as well as the mission coordinates and convert them from the coordinates as reported by the vessel (FlightGlobals.ActiveVessel.latitude for example) and convert them using this (I have the altitude plus radius of the celestial body already):

That oughta do it, yes.

Though, if you don't mind my asking... any particular reason you're using latitude / longitude / altitude for all this stuff, which imposes the the need for trigonometric conversions?  For example, Vessel.GetWorldPos3D() hands you the XYZ coordinates as a handy Vector3d right there.  Not sure what your "target" is (a vessel?  something else?), but if you have a way of getting its world position as a Vector3d, then that would solve your problem for you right there:

For example,

Vector3d vesselPos = FlightGlobals.ActiveVessel.GetWorldPos3D();
Vector3d targetPos = whateverTheTargetIs.SomehowGetWorldPos3D();
double distanceToTarget = (vesselPos - targetPos).magnitude;
// All done!

 

15 hours ago, DoctorDavinci said:

And then do this:


double cylindricalR1 = r1 * Math.cos(latRad1);
double cylindricalR2 = r2 * Math.cos(latRad2);

double x1 = cylindricalR1 * Math.cos(lonRad1);
double x2 = cylindricalR2 * Math.cos(lonRad2);

double y1 = cylindricalR1 * Math.sin(lonRad1);
double y2 = cylindricalR2 * Math.sin(lonRad2);

double z1 = r1 * Math.sin(latRad1);
double z2 = r2 * Math.sin(latRad2);

 

If what you have is just latitude / longitude / altitude, and you don't have a way of getting the XYZ coords directly, then yes, that's what you'd do, yes.

15 hours ago, DoctorDavinci said:

and then I do the math I am doing in my code above

There, I'm not really in a position to give you an answer, because the code's pretty complicated and I honestly have trouble telling what "the math I am doing in my code above" is trying to accomplish-- it has a whole bunch of if-else statements for "if this thing is bigger than that thing" and so forth, the motivation for which I don't understand if all you're trying to do is answer the question "what's the straight-line distance between these two things".

But if you have two points in 3D space, both defined as XYZ coordinates, then it's just the simple Pythagorean distance given by

double dX = x1 - x2;
double dY = y1 - y2;
double dZ = z1 - z2;

double distance = Math.sqrt(dX*dX + dY*dY + dZ*dZ);

Or, if they're already stored as a Vector3d, you can just do (v1 - v2).magnitude, which amounts to the same thing.

Link to comment
Share on other sites

4 hours ago, Snark said:

Thanks, I appreciate the full context... but I find the code pretty hard to follow (there's a lot of stuff in there, with lots and lots of nested clauses in one big monolithic block, and no code comments).  Fair 'nuff if that's your comfort zone :) ... just makes it a bit harder for others to follow (well, at least my tired old brain, anyway), particularly if the reader doesn't have the broader context of the overall design of the mod.

Not really a comfort zone thing, more like I have no idea what I am doing as I am no computer scientist or coder for that matter ... Totally self taught with a little help from some friends

4 hours ago, Snark said:

Though, if you don't mind my asking... any particular reason you're using latitude / longitude / altitude for all this stuff, which imposes the the need for trigonometric conversions?  For example, Vessel.GetWorldPos3D() hands you the XYZ coordinates as a handy Vector3d right there.  Not sure what your "target" is (a vessel?  something else?), but if you have a way of getting its world position as a Vector3d, then that would solve your problem for you right there:

For example,


Vector3d vesselPos = FlightGlobals.ActiveVessel.GetWorldPos3D();
Vector3d targetPos = whateverTheTargetIs.SomehowGetWorldPos3D();
double distanceToTarget = (vesselPos - targetPos).magnitude;
// All done!

 

If what you have is just latitude / longitude / altitude, and you don't have a way of getting the XYZ coords directly, then yes, that's what you'd do, yes.

 

To give you a bit more context, the original idea of creating OrX Kontinuum was to not have to hard code missions into OrX - The Loot Box Controversy (I turned KSP into a first person shooter ... I really wanted a zombie mode for KSP :wink:) ... However now OrX K has turned into a challenge builder (Geo-Caching, Short Track Racing, Dakar Racing, BD Armory challenges etc..) with a custom encrypted save file that can be shared with other players (a custom save game, if you will, that can be merged into a current game on the fly while in the flight scene)

The challenges are built while in the flight scene with the HoloKron system I created ... This system is what creates and encrypts the .orx file that contains coordinates for gates that were placed as well as vessels that were spawned while building (including the vessels themselves) and a bunch of other data such as the challenge type, starting coords, stage coords etc....

Then there is a spawning system that takes that data in the .orx file when a player competes in a challenge and uses it to spawn vessels, HoloKrons, stage gates etc... when a player gets to within a specified distance .... This is the main function of the distance checking is for (BD Armory challenges also have some additional logic for triggering the guard mode, activating engines, turning on the AI pilot etc...)

There's a few more goodies in OrX K such as the beginnings of W[ind/S] (wind for sailing and hang gliding challenges ... yes, it works quite well) as well as a working Scuba Kerb for scuba diving with your kerbals (including bends and nitrogen narcosis :ph34r:)

4 hours ago, Snark said:

Vector3d vesselPos = FlightGlobals.ActiveVessel.GetWorldPos3D();
Vector3d targetPos = whateverTheTargetIs.SomehowGetWorldPos3D();
double distanceToTarget = (vesselPos - targetPos).magnitude;

^^ This ... I didn't even think about this at all although, as mentioned above, I don't know what I am doing

I can just create a Vector3d for the target pos from the coords in the .orx file and then use double distanceToTarget = (FlightGlobals.ActiveVessel.GetWorldPos3D()- targetPos).magnitude; ...... yes?

Does the .magnitude return a value in meters?

Edited by DoctorDavinci
Link to comment
Share on other sites

2 hours ago, DoctorDavinci said:

Not really a comfort zone thing, more like I have no idea what I am doing as I am no computer scientist or coder for that matter ... Totally self taught with a little help from some friends

Fair 'nuff then.

Interested in some friendly coding style advice/suggestions from a grizzled old coder, which may help make life a bit easier for you?  (I ask first, because no desire to pontificate unless you're actually interested.  Not everyone is.)  ;)

2 hours ago, DoctorDavinci said:

I can just create a Vector3d for the target pos from the coords in the .orx file and then use double distanceToTarget = (FlightGlobals.ActiveVessel.GetWorldPos3D()- targetPos).magnitude; ...... yes?

Yes.

Spoiler

EDIT:  Ignore everything in this spoiler below here, it's wrong.  I missed that Vector3d is actually a struct and not a class.  Never mind!  (Thanks to Boris-Barboris for the clarification below.)

You could certainly do that, but whether it's a good idea or not depends on context.  Because if you do that, you have to create a new object, like this:


Vector3d targetPos = new Vector3d(targetX, targetY, targetZ); // potentially expensive!

It's important to understand that creating new objects in C# ain't free.  Actually, it's not so much the creation of them, it's the garbage disposal afterwards.  Think of "create an object via new", in C#, like buying some bottled water.  It generates trash-- the empty bottle-- which you have to clean up somehow afterwards.

And, like the bottled-water scenario, how much that matters really depends on frequency.  Like, if you're out in the hot summer sun and guzzling dozens of bottles of water, and you buy a new bottled water every time... all those empties really pile up, it's costly.  Better to just have one bottle that you keep reusing.  Whereas if you just happen to be stuck in an airport or something and buy one bottle, it's not that big a deal.

I have no idea what context you're running this code in-- is this something that only happens rarely, like when someone clicks a thing?  Or is this something that gets called on every single frame, e.g. dozens of times per second?

If it's the former, then it's not really a concern to create a new object, because the garbage collection cost isn't all that big, compared with other stuff in KSP.  But if it's getting spammed on every single Unity frame, then making lots and lots of extra objects that have to be garbage-disposed has the potential to impact your performance.

So, if your target just happens to already have a Vector3d with its position on it, then by all means re-use that, as described.  But if what you have is just X, Y, and Z, then I'd suggest plugging those into the Pythagorean theorem and doing the math yourself with Math.sqrt, since that way you're not allocating any new objects and you save that cost.

 

2 hours ago, DoctorDavinci said:

Does the .magnitude return a value in meters?

.magnitude returns whatever units the Vector3d is in.

Remember, Vector3d doesn't itself specify any units, any more than double does.  It's just a trio of double values named x, y, and z.

So, suppose you have a Vector3d where x = 3, and y = 4, and z = 0.  The magnitude will be 5.  Because 32 + 42 + 02 = 52.

So... 5 what?  Answer:  5 of whatever x, y, and z were specified in.

So, for example, Vessel.GetWorldPos3D() happens to return a Vector3d whose units are meters.  So if you have two vectors, and both of them happen to be in meters, then the magnitude of each one will be in meters, too.

(And of course, if you're doing any math on the vectors, like finding v1 - v2 to get the delta between them... better make sure they're both in the same units, or you're trying to mix apples and oranges and hilarity will ensue.)  ;)

Edited by Snark
Edited for correctness. Thanks, Boris-Barboris!
Link to comment
Share on other sites

28 minutes ago, Boris-Barboris said:

Vector3 is a struct and new operator on structs does not allocate on GC heap unless it's boxed.

...rummage rummage...

Ah, quite so, as is Vector3d.  Earlier comment cheerfully withdrawn.  Thanks!  :)

Link to comment
Share on other sites

1 hour ago, Snark said:

Interested in some friendly coding style advice/suggestions from a grizzled old coder, which may help make life a bit easier for you?  (I ask first, because no desire to pontificate unless you're actually interested.  Not everyone is.)  ;)

I am always up for learning new things ... Just not so keen on comments such as found on the first page of this thread :wink:

Anyways, looks like I fixed it ... Although I couldn't get the code you posted to work correctly, weird numbers coming out of what I wrote

I instead did a bit of digging and came up with two methods to achieve the proper distance ... the first using a modified snippet of code mashed together with another snippet of code I found on Stack Exchange and the other, more interesting, is by using System.Device.LocationSystem that is in .net 4.7.2 as it has a direct method specifically made to do distance checking (probably for mobile devices methinks ... or something like that)

I went with the first option in the end and it seems to be spot on and I would prefer to see what the code is doing instead of relying on outside functions to do the work (mainly so I can understand what is going on) ... Although the displayed distance still has a minor error due to my using Math.Round on the distance to 1 decimal place (I'm also splitting the difference with the two altitude values ... adding them together and dividing by 2)

Quote

        public static double Radians(double x)
        {
            return x * Math.PI / 180;
        }
        public double GetDistance(double lon1, double lat1, double lon2, double lat2)
        {
            double dlon = Radians(lon2 - lon1);
            double dlat = Radians(lat2 - lat1);

            double a = (Math.Sin(dlat / 2) * Math.Sin(dlat / 2)) + Math.Cos(Radians(lat1)) * Math.Cos(Radians(lat2)) * (Math.Sin(dlon / 2) * Math.Sin(dlon / 2));
            double angle = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
            return angle * (FlightGlobals.ActiveVessel.mainBody.Radius + ((FlightGlobals.ActiveVessel.altitude + _altMission) / 2));
        }

I haven't tested it at the poles, only near the Desert Airfield, but from the looks of it the error I was seeing in the distance is fixed

Link to comment
Share on other sites

1 hour ago, DoctorDavinci said:

I am always up for learning new things ...

The main suggestions are:

  1. Liberally include code comments, like, a lot.  How much is "enough" is subjective, of course, but just for comparison, here's a typical source file of mine to give an idea of the amount of comments I like to use, myself.
  2. Avoid really long blocks of code that go on for pages and pages-- if it's more than will fit on a screen or two, consider breaking it up into smaller functions.

Rationale for the code-comment thing:  It'll make your life easier.

  • It means you're less likely to self-inflict bugs on yourself by getting confused about "what did I mean when I coded this".  It's easy to write code that's meant to do one thing, and then come back weeks or months later and read it and think you meant something else and get caught up in your own assumptions.
  • It means that other people can read your code much more easily too, which means that if you ever need to ask for help, it's a lot easier to get it.  It also means that anyone inclined to debug your mod themselves, and/or look at your source code, will better be able to understand it and more likely to spot problems (e.g. stemming from mistaken assumptions or whatever).
  • It means that when you do have a bug, and you're trying to track it down, you'll be able to follow the logic flow of your code faster and more reliably, and incorrect assumptions will tend to "stick out" more.

Rationale for the break-it-up-into-smaller-functions thing is much the same.  Pretty much the same benefits as the above.

Link to comment
Share on other sites

17 minutes ago, Snark said:

The main suggestions are:

  1. Liberally include code comments, like, a lot.  How much is "enough" is subjective, of course, but just for comparison, here's a typical source file of mine to give an idea of the amount of comments I like to use, myself.
  2. Avoid really long blocks of code that go on for pages and pages-- if it's more than will fit on a screen or two, consider breaking it up into smaller functions.

Rationale for the code-comment thing:  It'll make your life easier.

  • It means you're less likely to self-inflict bugs on yourself by getting confused about "what did I mean when I coded this".  It's easy to write code that's meant to do one thing, and then come back weeks or months later and read it and think you meant something else and get caught up in your own assumptions.
  • It means that other people can read your code much more easily too, which means that if you ever need to ask for help, it's a lot easier to get it.  It also means that anyone inclined to debug your mod themselves, and/or look at your source code, will better be able to understand it and more likely to spot problems (e.g. stemming from mistaken assumptions or whatever).
  • It means that when you do have a bug, and you're trying to track it down, you'll be able to follow the logic flow of your code faster and more reliably, and incorrect assumptions will tend to "stick out" more.

Rationale for the break-it-up-into-smaller-functions thing is much the same.  Pretty much the same benefits as the above.

I've actually started on this about a month ago or so ... I had one class that turned into over 15000 lines of code lol 

There is a ton of stuff I need to separate into their own classes and also a bunch of repetitive code in there ... I was going on the concept of getting it to work first and then start cutting it up into pieces

Anyways, thanx for the suggestions and help with this target distance issue ... I also managed to fix some spawning issues (holokrons spawning underground), challenger placement problems (vessels crashing through the surface while being moved to the challenge start pos) as well as fixing the scoreboard and score detail menu 

I think it is time to do as you suggest above and buckle down to refactoring .... Sitting at over 28000 lines of code now so it will probably take awhile lol

But it's all working ... we now have a challenge creator mod for KSP :)

I'm going to package it up and post a new release, do a bit of Dakar racing and then dive into cleaning up my mess :D

Thanx again

Edited by DoctorDavinci
Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...